diff options
130 files changed, 3480 insertions, 2330 deletions
diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 9153426b29ab..50e0a1b45c9d 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -35446,51 +35446,6 @@ Landroid/net/IIpConnectivityMetrics$Stub;->TRANSACTION_removeNetdEventCallback:I Landroid/net/IIpConnectivityMetrics;->addNetdEventCallback(ILandroid/net/INetdEventCallback;)Z Landroid/net/IIpConnectivityMetrics;->logEvent(Landroid/net/ConnectivityMetricsEvent;)I Landroid/net/IIpConnectivityMetrics;->removeNetdEventCallback(I)Z -Landroid/net/IIpSecService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/IIpSecService$Stub$Proxy;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse; -Landroid/net/IIpSecService$Stub$Proxy;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V -Landroid/net/IIpSecService$Stub$Proxy;->applyTunnelModeTransform(IIILjava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->closeUdpEncapsulationSocket(I)V -Landroid/net/IIpSecService$Stub$Proxy;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse; -Landroid/net/IIpSecService$Stub$Proxy;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse; -Landroid/net/IIpSecService$Stub$Proxy;->deleteTransform(I)V -Landroid/net/IIpSecService$Stub$Proxy;->deleteTunnelInterface(ILjava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/IIpSecService$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/IIpSecService$Stub$Proxy;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse; -Landroid/net/IIpSecService$Stub$Proxy;->releaseSecurityParameterIndex(I)V -Landroid/net/IIpSecService$Stub$Proxy;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V -Landroid/net/IIpSecService$Stub;-><init>()V -Landroid/net/IIpSecService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IIpSecService; -Landroid/net/IIpSecService$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/IIpSecService$Stub;->TRANSACTION_addAddressToTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_allocateSecurityParameterIndex:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTransportModeTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTunnelModeTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_closeUdpEncapsulationSocket:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_createTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_createTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_openUdpEncapsulationSocket:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_releaseSecurityParameterIndex:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_removeAddressFromTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_removeTransportModeTransforms:I -Landroid/net/IIpSecService;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse; -Landroid/net/IIpSecService;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V -Landroid/net/IIpSecService;->applyTunnelModeTransform(IIILjava/lang/String;)V -Landroid/net/IIpSecService;->closeUdpEncapsulationSocket(I)V -Landroid/net/IIpSecService;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse; -Landroid/net/IIpSecService;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse; -Landroid/net/IIpSecService;->deleteTransform(I)V -Landroid/net/IIpSecService;->deleteTunnelInterface(ILjava/lang/String;)V -Landroid/net/IIpSecService;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse; -Landroid/net/IIpSecService;->releaseSecurityParameterIndex(I)V -Landroid/net/IIpSecService;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V Landroid/net/INetd$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/INetd$Stub$Proxy;->addVirtualTunnelInterface(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V Landroid/net/INetd$Stub$Proxy;->bandwidthEnableDataSaver(Z)Z @@ -35914,174 +35869,6 @@ Landroid/net/InterfaceConfiguration;->mFlags:Ljava/util/HashSet; Landroid/net/InterfaceConfiguration;->mHwAddr:Ljava/lang/String; Landroid/net/InterfaceConfiguration;->setHardwareAddress(Ljava/lang/String;)V Landroid/net/InterfaceConfiguration;->validateFlag(Ljava/lang/String;)V -Landroid/net/IpSecAlgorithm;->checkValidOrThrow(Ljava/lang/String;II)V -Landroid/net/IpSecAlgorithm;->CRYPT_NULL:Ljava/lang/String; -Landroid/net/IpSecAlgorithm;->equals(Landroid/net/IpSecAlgorithm;Landroid/net/IpSecAlgorithm;)Z -Landroid/net/IpSecAlgorithm;->isAead()Z -Landroid/net/IpSecAlgorithm;->isAuthentication()Z -Landroid/net/IpSecAlgorithm;->isEncryption()Z -Landroid/net/IpSecAlgorithm;->isUnsafeBuild()Z -Landroid/net/IpSecAlgorithm;->mKey:[B -Landroid/net/IpSecAlgorithm;->mName:Ljava/lang/String; -Landroid/net/IpSecAlgorithm;->mTruncLenBits:I -Landroid/net/IpSecAlgorithm;->TAG:Ljava/lang/String; -Landroid/net/IpSecConfig;-><init>()V -Landroid/net/IpSecConfig;-><init>(Landroid/net/IpSecConfig;)V -Landroid/net/IpSecConfig;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecConfig;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecConfig;->equals(Landroid/net/IpSecConfig;Landroid/net/IpSecConfig;)Z -Landroid/net/IpSecConfig;->getAuthenticatedEncryption()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getAuthentication()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getDestinationAddress()Ljava/lang/String; -Landroid/net/IpSecConfig;->getEncapRemotePort()I -Landroid/net/IpSecConfig;->getEncapSocketResourceId()I -Landroid/net/IpSecConfig;->getEncapType()I -Landroid/net/IpSecConfig;->getEncryption()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getMarkMask()I -Landroid/net/IpSecConfig;->getMarkValue()I -Landroid/net/IpSecConfig;->getMode()I -Landroid/net/IpSecConfig;->getNattKeepaliveInterval()I -Landroid/net/IpSecConfig;->getNetwork()Landroid/net/Network; -Landroid/net/IpSecConfig;->getSourceAddress()Ljava/lang/String; -Landroid/net/IpSecConfig;->getSpiResourceId()I -Landroid/net/IpSecConfig;->mAuthenticatedEncryption:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mAuthentication:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mDestinationAddress:Ljava/lang/String; -Landroid/net/IpSecConfig;->mEncapRemotePort:I -Landroid/net/IpSecConfig;->mEncapSocketResourceId:I -Landroid/net/IpSecConfig;->mEncapType:I -Landroid/net/IpSecConfig;->mEncryption:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mMarkMask:I -Landroid/net/IpSecConfig;->mMarkValue:I -Landroid/net/IpSecConfig;->mMode:I -Landroid/net/IpSecConfig;->mNattKeepaliveInterval:I -Landroid/net/IpSecConfig;->mNetwork:Landroid/net/Network; -Landroid/net/IpSecConfig;->mSourceAddress:Ljava/lang/String; -Landroid/net/IpSecConfig;->mSpiResourceId:I -Landroid/net/IpSecConfig;->setAuthenticatedEncryption(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setAuthentication(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setDestinationAddress(Ljava/lang/String;)V -Landroid/net/IpSecConfig;->setEncapRemotePort(I)V -Landroid/net/IpSecConfig;->setEncapSocketResourceId(I)V -Landroid/net/IpSecConfig;->setEncapType(I)V -Landroid/net/IpSecConfig;->setEncryption(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setMarkMask(I)V -Landroid/net/IpSecConfig;->setMarkValue(I)V -Landroid/net/IpSecConfig;->setMode(I)V -Landroid/net/IpSecConfig;->setNattKeepaliveInterval(I)V -Landroid/net/IpSecConfig;->setNetwork(Landroid/net/Network;)V -Landroid/net/IpSecConfig;->setSourceAddress(Ljava/lang/String;)V -Landroid/net/IpSecConfig;->setSpiResourceId(I)V -Landroid/net/IpSecConfig;->TAG:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)V -Landroid/net/IpSecManager$IpSecTunnelInterface;->addAddress(Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$IpSecTunnelInterface;->getInterfaceName()Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->getResourceId()I -Landroid/net/IpSecManager$IpSecTunnelInterface;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mInterfaceName:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mLocalAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mOpPackageName:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mRemoteAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mResourceId:I -Landroid/net/IpSecManager$IpSecTunnelInterface;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mUnderlyingNetwork:Landroid/net/Network; -Landroid/net/IpSecManager$IpSecTunnelInterface;->removeAddress(Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$ResourceUnavailableException;-><init>(Ljava/lang/String;)V -Landroid/net/IpSecManager$SecurityParameterIndex;-><init>(Landroid/net/IIpSecService;Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$SecurityParameterIndex;->getResourceId()I -Landroid/net/IpSecManager$SecurityParameterIndex;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$SecurityParameterIndex;->mDestinationAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$SecurityParameterIndex;->mResourceId:I -Landroid/net/IpSecManager$SecurityParameterIndex;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager$SecurityParameterIndex;->mSpi:I -Landroid/net/IpSecManager$SpiUnavailableException;-><init>(Ljava/lang/String;I)V -Landroid/net/IpSecManager$SpiUnavailableException;->mSpi:I -Landroid/net/IpSecManager$Status;->OK:I -Landroid/net/IpSecManager$Status;->RESOURCE_UNAVAILABLE:I -Landroid/net/IpSecManager$Status;->SPI_UNAVAILABLE:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;-><init>(Landroid/net/IIpSecService;I)V -Landroid/net/IpSecManager$UdpEncapsulationSocket;->getResourceId()I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPfd:Landroid/os/ParcelFileDescriptor; -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPort:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mResourceId:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;)V -Landroid/net/IpSecManager;->applyTunnelModeTransform(Landroid/net/IpSecManager$IpSecTunnelInterface;ILandroid/net/IpSecTransform;)V -Landroid/net/IpSecManager;->createIpSecTunnelInterface(Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)Landroid/net/IpSecManager$IpSecTunnelInterface; -Landroid/net/IpSecManager;->INVALID_RESOURCE_ID:I -Landroid/net/IpSecManager;->maybeHandleServiceSpecificException(Landroid/os/ServiceSpecificException;)V -Landroid/net/IpSecManager;->mContext:Landroid/content/Context; -Landroid/net/IpSecManager;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager;->removeTunnelModeTransform(Landroid/net/Network;Landroid/net/IpSecTransform;)V -Landroid/net/IpSecManager;->rethrowCheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/io/IOException; -Landroid/net/IpSecManager;->rethrowUncheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/lang/RuntimeException; -Landroid/net/IpSecManager;->TAG:Ljava/lang/String; -Landroid/net/IpSecSpiResponse;-><init>(I)V -Landroid/net/IpSecSpiResponse;-><init>(III)V -Landroid/net/IpSecSpiResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecSpiResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecSpiResponse;->resourceId:I -Landroid/net/IpSecSpiResponse;->spi:I -Landroid/net/IpSecSpiResponse;->status:I -Landroid/net/IpSecSpiResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecTransform$Builder;->buildTunnelModeTransform(Ljava/net/InetAddress;Landroid/net/IpSecManager$SecurityParameterIndex;)Landroid/net/IpSecTransform; -Landroid/net/IpSecTransform$Builder;->mConfig:Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform$Builder;->mContext:Landroid/content/Context; -Landroid/net/IpSecTransform$NattKeepaliveCallback;-><init>()V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_ERROR:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_UNSUPPORTED:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_INVALID_NETWORK:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onError(I)V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStarted()V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStopped()V -Landroid/net/IpSecTransform;-><init>(Landroid/content/Context;Landroid/net/IpSecConfig;)V -Landroid/net/IpSecTransform;->activate()Landroid/net/IpSecTransform; -Landroid/net/IpSecTransform;->checkResultStatus(I)V -Landroid/net/IpSecTransform;->ENCAP_ESPINUDP:I -Landroid/net/IpSecTransform;->ENCAP_ESPINUDP_NON_IKE:I -Landroid/net/IpSecTransform;->ENCAP_NONE:I -Landroid/net/IpSecTransform;->equals(Landroid/net/IpSecTransform;Landroid/net/IpSecTransform;)Z -Landroid/net/IpSecTransform;->getConfig()Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform;->getIpSecService()Landroid/net/IIpSecService; -Landroid/net/IpSecTransform;->getResourceId()I -Landroid/net/IpSecTransform;->mCallbackHandler:Landroid/os/Handler; -Landroid/net/IpSecTransform;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecTransform;->mConfig:Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform;->mContext:Landroid/content/Context; -Landroid/net/IpSecTransform;->mKeepalive:Landroid/net/ConnectivityManager$PacketKeepalive; -Landroid/net/IpSecTransform;->mKeepaliveCallback:Landroid/net/ConnectivityManager$PacketKeepaliveCallback; -Landroid/net/IpSecTransform;->MODE_TRANSPORT:I -Landroid/net/IpSecTransform;->MODE_TUNNEL:I -Landroid/net/IpSecTransform;->mResourceId:I -Landroid/net/IpSecTransform;->mUserKeepaliveCallback:Landroid/net/IpSecTransform$NattKeepaliveCallback; -Landroid/net/IpSecTransform;->startNattKeepalive(Landroid/net/IpSecTransform$NattKeepaliveCallback;ILandroid/os/Handler;)V -Landroid/net/IpSecTransform;->stopNattKeepalive()V -Landroid/net/IpSecTransform;->TAG:Ljava/lang/String; -Landroid/net/IpSecTransformResponse;-><init>(I)V -Landroid/net/IpSecTransformResponse;-><init>(II)V -Landroid/net/IpSecTransformResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecTransformResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecTransformResponse;->resourceId:I -Landroid/net/IpSecTransformResponse;->status:I -Landroid/net/IpSecTransformResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(I)V -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(IILjava/lang/String;)V -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecTunnelInterfaceResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecTunnelInterfaceResponse;->interfaceName:Ljava/lang/String; -Landroid/net/IpSecTunnelInterfaceResponse;->resourceId:I -Landroid/net/IpSecTunnelInterfaceResponse;->status:I -Landroid/net/IpSecTunnelInterfaceResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecUdpEncapResponse;-><init>(I)V -Landroid/net/IpSecUdpEncapResponse;-><init>(IIILjava/io/FileDescriptor;)V -Landroid/net/IpSecUdpEncapResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecUdpEncapResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecUdpEncapResponse;->fileDescriptor:Landroid/os/ParcelFileDescriptor; -Landroid/net/IpSecUdpEncapResponse;->port:I -Landroid/net/IpSecUdpEncapResponse;->resourceId:I -Landroid/net/IpSecUdpEncapResponse;->status:I -Landroid/net/IpSecUdpEncapResponse;->TAG:Ljava/lang/String; Landroid/net/ITetheringStatsProvider$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getTetherStats(I)Landroid/net/NetworkStats; diff --git a/core/api/current.txt b/core/api/current.txt index 4777e3c83d11..7d45ad87c969 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4069,7 +4069,7 @@ package android.app { method public int getMaxNumPictureInPictureActions(); method public final android.media.session.MediaController getMediaController(); method @NonNull public android.view.MenuInflater getMenuInflater(); - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method public final android.app.Activity getParent(); method @Nullable public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); @@ -4970,7 +4970,7 @@ package android.app { method @NonNull @UiContext public final android.content.Context getContext(); method @Nullable public android.view.View getCurrentFocus(); method @NonNull public android.view.LayoutInflater getLayoutInflater(); - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method @Nullable public final android.app.Activity getOwnerActivity(); method @Nullable public final android.view.SearchEvent getSearchEvent(); method public final int getVolumeControlStream(); @@ -7611,7 +7611,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; - field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; + field @Deprecated public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field @Deprecated public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; field public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT"; @@ -26391,75 +26391,6 @@ package android.net { method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); } - public final class IpSecAlgorithm implements android.os.Parcelable { - ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]); - ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int); - method public int describeContents(); - method @NonNull public byte[] getKey(); - method @NonNull public String getName(); - method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms(); - method public int getTruncationLengthBits(); - method public void writeToParcel(android.os.Parcel, int); - field public static final String AUTH_AES_CMAC = "cmac(aes)"; - field public static final String AUTH_AES_XCBC = "xcbc(aes)"; - field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; - field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)"; - field public static final String AUTH_HMAC_MD5 = "hmac(md5)"; - field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; - field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; - field public static final String AUTH_HMAC_SHA384 = "hmac(sha384)"; - field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; - field public static final String CRYPT_AES_CBC = "cbc(aes)"; - field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))"; - } - - public final class IpSecManager { - method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; - method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException; - method public void removeTransportModeTransforms(@NonNull java.net.DatagramSocket) throws java.io.IOException; - method public void removeTransportModeTransforms(@NonNull java.io.FileDescriptor) throws java.io.IOException; - field public static final int DIRECTION_IN = 0; // 0x0 - field public static final int DIRECTION_OUT = 1; // 0x1 - } - - public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { - } - - public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { - method public void close(); - method public int getSpi(); - } - - public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException { - method public int getSpi(); - } - - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public void close() throws java.io.IOException; - method public java.io.FileDescriptor getFileDescriptor(); - method public int getPort(); - } - - public final class IpSecTransform implements java.lang.AutoCloseable { - method public void close(); - } - - public static class IpSecTransform.Builder { - ctor public IpSecTransform.Builder(@NonNull android.content.Context); - method @NonNull public android.net.IpSecTransform buildTransportModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - method @NonNull public android.net.IpSecTransform.Builder setAuthenticatedEncryption(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setAuthentication(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setEncryption(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int); - } - public class LocalServerSocket implements java.io.Closeable { ctor public LocalServerSocket(String) throws java.io.IOException; ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException; @@ -48972,7 +48903,7 @@ package android.view { } public interface OnBackInvokedDispatcherOwner { - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); } public interface OnReceiveContentListener { @@ -49400,7 +49331,7 @@ package android.view { field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedMotionEvent> CREATOR; } - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { ctor public View(android.content.Context); ctor public View(android.content.Context, @Nullable android.util.AttributeSet); ctor public View(android.content.Context, @Nullable android.util.AttributeSet, int); @@ -49604,7 +49535,6 @@ package android.view { method @IdRes public int getNextFocusLeftId(); method @IdRes public int getNextFocusRightId(); method @IdRes public int getNextFocusUpId(); - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 4448a032dc00..9737cde48b4d 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -139,9 +139,14 @@ package android.content { package android.content.pm { + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { + method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraryInfos(); + } + public abstract class PackageManager { method @NonNull public String getPermissionControllerPackageName(); method @NonNull public String getSupplementalProcessPackageName(); + field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000 } } @@ -278,14 +283,6 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR; } - public final class IpSecManager { - field public static final int DIRECTION_FWD = 2; // 0x2 - } - - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public int getResourceId(); - } - public class LocalSocket implements java.io.Closeable { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } diff --git a/core/api/removed.txt b/core/api/removed.txt index 311b110f1997..07639fbf5378 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -513,7 +513,7 @@ package android.util { package android.view { - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { method protected void initializeFadingEdge(android.content.res.TypedArray); method protected void initializeScrollbars(android.content.res.TypedArray); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1cba58c391dc..6a458fbd30ee 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2826,8 +2826,8 @@ package android.content { field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; - field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 - field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 + field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 + field @Deprecated public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String CLOUDSEARCH_SERVICE = "cloudsearch"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -7952,7 +7952,7 @@ package android.media.tv.tuner.frontend { public class FrontendStatus { method public int getAgc(); - method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpInfo[] getAllAtsc3PlpInfo(); + method @NonNull public java.util.List<android.media.tv.tuner.frontend.Atsc3PlpInfo> getAllAtsc3PlpInfo(); method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo(); method public int getBandwidth(); method public int getBer(); @@ -8336,23 +8336,6 @@ package android.net { method public void release(); } - public final class IpSecManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - } - - public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException; - method public void close(); - method @NonNull public String getInterfaceName(); - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void setUnderlyingNetwork(@NonNull android.net.Network) throws java.io.IOException; - } - - public static class IpSecTransform.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - } - public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); @@ -14734,6 +14717,7 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR; field public static final String RCS_PROFILE_1_0 = "UP_1.0"; field public static final String RCS_PROFILE_2_3 = "UP_2.3"; + field public static final String RCS_PROFILE_2_4 = "UP_2.4"; } public final class RcsContactPresenceTuple implements android.os.Parcelable { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index e17a9bb1512c..1b45e88584fe 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1,11 +1,5 @@ // Baseline format: 1.0 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): - - - -BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex): - Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex) - ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener` @@ -15,8 +9,6 @@ GenericException: android.app.prediction.AppPredictor#finalize(): GenericException: android.hardware.location.ContextHubClient#finalize(): -GenericException: android.net.IpSecManager.IpSecTunnelInterface#finalize(): - GenericException: android.service.autofill.augmented.FillWindow#finalize(): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index fea739668512..9d6fc0e67f03 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1607,10 +1607,6 @@ package android.net { method public void setIncludeTestInterfaces(boolean); } - public final class IpSecManager { - field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 - } - public class NetworkPolicyManager { method public boolean getRestrictBackground(); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); @@ -2820,7 +2816,7 @@ package android.view { method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams); } - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { method public android.view.View getTooltipView(); method public boolean isAutofilled(); method public static boolean isDefaultFocusHighlightEnabled(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 530666b8f1d7..90c56ae63374 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6139,6 +6139,10 @@ public class Activity extends ContextThemeWrapper * you to specify a custom animation even when starting an activity from * outside the context of the current top activity. * + * <p>Af of {@link android.os.Build.VERSION_CODES#S} application can only specify + * a transition animation when the transition happens within the same task. System + * default animation is used for cross-task transition animations. + * * @param enterAnim A resource ID of the animation resource to use for * the incoming activity. Use 0 for no animation. * @param exitAnim A resource ID of the animation resource to use for @@ -8742,17 +8746,15 @@ public class Activity extends ContextThemeWrapper * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this * activity is attached to. * - * Returns null if the activity is not attached to a window with a decor. + * @throws IllegalStateException if this Activity is not visual. */ - @Nullable + @NonNull @Override public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - if (mWindow != null) { - View decorView = mWindow.getDecorView(); - if (decorView != null) { - return decorView.getOnBackInvokedDispatcher(); - } + if (mWindow == null) { + throw new IllegalStateException("OnBackInvokedDispatcher are not available on " + + "non-visual activities"); } - return null; + return ((OnBackInvokedDispatcherOwner) mWindow).getOnBackInvokedDispatcher(); } } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index cce7dd338b3d..a58ceaa99022 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -215,6 +215,14 @@ public abstract class ActivityManagerInternal { public abstract boolean isSystemReady(); /** + * Returns package name given pid. + * + * @param pid The pid we are searching package name for. + */ + @Nullable + public abstract String getPackageNameByPid(int pid); + + /** * Sets if the given pid has an overlay UI or not. * * @param pid The pid we are setting overlay UI for. diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index a7fb83bfcf5e..4b42ddc383b2 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -1449,15 +1449,9 @@ public class Dialog implements DialogInterface, Window.Callback, * * Returns null if the dialog is not attached to a window with a decor. */ - @Nullable + @NonNull @Override public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - if (mWindow != null) { - View decorView = mWindow.getDecorView(); - if (decorView != null) { - return decorView.getOnBackInvokedDispatcher(); - } - } - return null; + return ((OnBackInvokedDispatcherOwner) mWindow).getOnBackInvokedDispatcher(); } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 79180cbc57fd..4187ba0a10a5 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -139,12 +139,10 @@ import android.net.ConnectivityFrameworkInitializer; import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.EthernetManager; import android.net.IEthernetManager; -import android.net.IIpSecService; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.IPacProxyManager; import android.net.IVpnManager; -import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; import android.net.NetworkWatchlistManager; @@ -441,15 +439,6 @@ public final class SystemServiceRegistry { return new VcnManager(ctx, service); }}); - registerService(Context.IPSEC_SERVICE, IpSecManager.class, - new CachedServiceFetcher<IpSecManager>() { - @Override - public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE); - IIpSecService service = IIpSecService.Stub.asInterface(b); - return new IpSecManager(ctx, service); - }}); - registerService(Context.COUNTRY_DETECTOR, CountryDetector.class, new StaticServiceFetcher<CountryDetector>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 82cdf6f1fffc..6cd991bc49b8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1330,7 +1330,10 @@ public class DevicePolicyManager { * * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or * {@link #ACTION_PROVISION_MANAGED_DEVICE} + * + * @deprecated Logo customization is no longer supported in the provisioning flow. */ + @Deprecated public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; diff --git a/core/java/android/companion/TEST_MAPPING b/core/java/android/companion/TEST_MAPPING index 63f54fa35158..b561c29c37c4 100644 --- a/core/java/android/companion/TEST_MAPPING +++ b/core/java/android/companion/TEST_MAPPING @@ -1,12 +1,7 @@ { - "presubmit": [ + "imports": [ { - "name": "CtsOsTestCases", - "options": [ - { - "include-filter": "android.os.cts.CompanionDeviceManagerTest" - } - ] + "path": "frameworks/base/services/companion" } ] } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 66903e028cd4..6ffea3f9217d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -408,6 +408,7 @@ public abstract class Context { * @hide */ @SystemApi + @Deprecated public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000; /** @@ -421,12 +422,13 @@ public abstract class Context { public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000; /** - * This flag has never been used. + * Flag for {@link #bindService}: allow background activity starts from the bound service's + * process. + * This flag is only respected if the caller is holding + * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}. * @hide - * @deprecated This flag has never been used. */ @SystemApi - @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000; /** diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9e9dd1edd577..567f649ea762 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import static android.os.Build.VERSION_CODES.DONUT; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -48,6 +49,7 @@ import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -62,58 +64,58 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class); /** - * Default task affinity of all activities in this application. See - * {@link ActivityInfo#taskAffinity} for more information. This comes - * from the "taskAffinity" attribute. + * Default task affinity of all activities in this application. See + * {@link ActivityInfo#taskAffinity} for more information. This comes + * from the "taskAffinity" attribute. */ public String taskAffinity; - + /** * Optional name of a permission required to be able to access this * application's components. From the "permission" attribute. */ public String permission; - + /** * The name of the process this application should run in. From the * "process" attribute or, if not set, the same as * <var>packageName</var>. */ public String processName; - + /** * Class implementing the Application object. From the "class" * attribute. */ public String className; - + /** * A style resource identifier (in the package's resources) of the * description of an application. From the "description" attribute * or, if not set, 0. */ - public int descriptionRes; - + public int descriptionRes; + /** * A style resource identifier (in the package's resources) of the * default visual theme of the application. From the "theme" attribute * or, if not set, 0. */ public int theme; - + /** * Class implementing the Application's manage space * functionality. From the "manageSpaceActivity" * attribute. This is an optional attribute and will be null if * applications don't specify it in their manifest */ - public String manageSpaceActivityName; - + public String manageSpaceActivityName; + /** * Class implementing the Application's backup functionality. From * the "backupAgent" attribute. This is an optional attribute and * will be null if the application does not specify it in its manifest. - * + * * <p>If android:allowBackup is set to false, this attribute is ignored. */ public String backupAgentName; @@ -174,7 +176,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@code signatureOrSystem}. */ public static final int FLAG_SYSTEM = 1<<0; - + /** * Value for {@link #flags}: set to true if this application would like to * allow debugging of its @@ -183,7 +185,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:debuggable} of the <application> tag. */ public static final int FLAG_DEBUGGABLE = 1<<1; - + /** * Value for {@link #flags}: set to true if this application has code * associated with it. Comes @@ -191,7 +193,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:hasCode} of the <application> tag. */ public static final int FLAG_HAS_CODE = 1<<2; - + /** * Value for {@link #flags}: set to true if this application is persistent. * Comes from {@link android.R.styleable#AndroidManifestApplication_persistent @@ -212,20 +214,20 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:allowTaskReparenting} of the <application> tag. */ public static final int FLAG_ALLOW_TASK_REPARENTING = 1<<5; - + /** * Value for {@link #flags}: default value for the corresponding ActivityInfo flag. * Comes from {@link android.R.styleable#AndroidManifestApplication_allowClearUserData * android:allowClearUserData} of the <application> tag. */ public static final int FLAG_ALLOW_CLEAR_USER_DATA = 1<<6; - + /** * Value for {@link #flags}: this is set if this application has been * installed as an update to a built-in system application. */ public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7; - + /** * Value for {@link #flags}: this is set if the application has specified * {@link android.R.styleable#AndroidManifestApplication_testOnly @@ -240,15 +242,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:smallScreens}. */ public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9; - + /** * Value for {@link #flags}: true when the application's window can be * displayed on normal screens. Corresponds to * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens * android:normalScreens}. */ - public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; - + public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; + /** * Value for {@link #flags}: true when the application's window can be * increased in size for larger screens. Corresponds to @@ -256,7 +258,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:largeScreens}. */ public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11; - + /** * Value for {@link #flags}: true when the application knows how to adjust * its UI for different screen sizes. Corresponds to @@ -264,7 +266,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:resizeable}. */ public static final int FLAG_RESIZEABLE_FOR_SCREENS = 1<<12; - + /** * Value for {@link #flags}: true when the application knows how to * accommodate different screen densities. Corresponds to @@ -276,7 +278,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ @Deprecated public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13; - + /** * Value for {@link #flags}: set to true if this application would like to * request the VM to operate under the safe mode. Comes from @@ -288,7 +290,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #flags}: set to <code>false</code> if the application does not wish * to permit any OS-driven backups of its data; <code>true</code> otherwise. - * + * * <p>Comes from the * {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup} * attribute of the <application> tag. @@ -351,7 +353,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:xlargeScreens}. */ public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19; - + /** * Value for {@link #flags}: true when the application has requested a * large heap for its processes. Corresponds to @@ -1114,7 +1116,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * the same uid). */ public int uid; - + /** * The minimum SDK version this application can run on. It will not run * on earlier versions. @@ -1817,7 +1819,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (sb == null) { sb = ab.packageName; } - + return sCollator.compare(sa.toString(), sb.toString()); } @@ -1830,7 +1832,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public ApplicationInfo() { createTimestamp = System.currentTimeMillis(); } - + public ApplicationInfo(ApplicationInfo orig) { super(orig); taskAffinity = orig.taskAffinity; @@ -2125,7 +2127,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Disable compatibility mode - * + * * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -2346,7 +2348,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } return pm.getDefaultActivityIcon(); } - + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean isPackageUnavailable(PackageManager pm) { try { @@ -2655,4 +2657,22 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int getLocaleConfigRes() { return localeConfigRes; } + + + /** + * List of all shared libraries this application is linked against. This + * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public List<SharedLibraryInfo> getSharedLibraryInfos() { + if (sharedLibraryInfos == null) { + return Collections.EMPTY_LIST; + } + return sharedLibraryInfos; + } + } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index aa647000ee2d..07227c5fe0e1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1063,6 +1063,7 @@ public abstract class PackageManager { * via this flag. * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 0x04000000; /** diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl index 68b5679919d6..02db2749bbe8 100644 --- a/core/java/android/os/logcat/ILogcatManagerService.aidl +++ b/core/java/android/os/logcat/ILogcatManagerService.aidl @@ -22,5 +22,7 @@ package android.os.logcat; interface ILogcatManagerService { void startThread(in int uid, in int gid, in int pid, in int fd); void finishThread(in int uid, in int gid, in int pid, in int fd); + void approve(in int uid, in int gid, in int pid, in int fd); + void decline(in int uid, in int gid, in int pid, in int fd); } diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java index 05c312b56cc7..f3ca531f2a42 100644 --- a/core/java/android/view/OnBackInvokedDispatcher.java +++ b/core/java/android/view/OnBackInvokedDispatcher.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,6 +33,13 @@ import java.lang.annotation.RetentionPolicy; * target (a.k.a. the callback to be invoked next), or its behavior. */ public abstract class OnBackInvokedDispatcher { + + /** @hide */ + public static final String TAG = "OnBackInvokedDispatcher"; + + /** @hide */ + public static final boolean DEBUG = Build.isDebuggable(); + /** @hide */ @IntDef({ PRIORITY_DEFAULT, diff --git a/core/java/android/view/OnBackInvokedDispatcherOwner.java b/core/java/android/view/OnBackInvokedDispatcherOwner.java index 0e14ed4cdb07..e69efe01138c 100644 --- a/core/java/android/view/OnBackInvokedDispatcherOwner.java +++ b/core/java/android/view/OnBackInvokedDispatcherOwner.java @@ -16,7 +16,7 @@ package android.view; -import android.annotation.Nullable; +import android.annotation.NonNull; /** * A class that provides an {@link OnBackInvokedDispatcher} that allows you to register @@ -28,6 +28,6 @@ public interface OnBackInvokedDispatcherOwner { * to its registered {@link OnBackInvokedCallback}s. * Returns null when the root view is not attached to a window or a view tree with a decor. */ - @Nullable + @NonNull OnBackInvokedDispatcher getOnBackInvokedDispatcher(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4ff7e2297ea0..36a97e4cdfd5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -834,7 +834,7 @@ import java.util.function.Predicate; */ @UiThread public class View implements Drawable.Callback, KeyEvent.Callback, - AccessibilityEventSource, OnBackInvokedDispatcherOwner { + AccessibilityEventSource { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private static final boolean DBG = false; @@ -31447,23 +31447,4 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return null; } - - /** - * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to. - * - * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached - * to a window or a view tree with a decor. - */ - @Nullable - public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - ViewParent parent = getParent(); - if (parent instanceof View) { - return ((View) parent).getOnBackInvokedDispatcher(); - } else if (parent instanceof ViewRootImpl) { - // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have - // a {@link com.android.internal.policy.DecorView}. - return ((ViewRootImpl) parent).getOnBackInvokedDispatcher(); - } - return null; - } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 386b277156e3..3d8653554efd 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -236,7 +236,7 @@ import java.util.function.Consumer; @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, - AttachedSurfaceControl { + AttachedSurfaceControl, OnBackInvokedDispatcherOwner { private static final String TAG = "ViewRootImpl"; private static final boolean DBG = false; private static final boolean LOCAL_LOGV = false; @@ -313,9 +313,9 @@ public final class ViewRootImpl implements ViewParent, private @SurfaceControl.BufferTransform int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY; /** - * The fallback {@link OnBackInvokedDispatcher} when the window doesn't have a decor view. + * The top level {@link OnBackInvokedDispatcher}. */ - private WindowOnBackInvokedDispatcher mFallbackOnBackInvokedDispatcher = + private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(); /** @@ -893,7 +893,6 @@ public final class ViewRootImpl implements ViewParent, mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; - mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow); } public static void addFirstDrawHandler(Runnable callback) { @@ -1144,9 +1143,6 @@ public final class ViewRootImpl implements ViewParent, if (pendingInsetsController != null) { pendingInsetsController.replayAndAttach(mInsetsController); } - ((RootViewSurfaceTaker) mView) - .provideWindowOnBackInvokedDispatcher() - .attachToWindow(mWindowSession, mWindow); } try { @@ -1193,6 +1189,7 @@ public final class ViewRootImpl implements ViewParent, getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame); setFrame(mTmpFrames.frame); + registerBackCallbackOnWindow(); if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; @@ -8417,6 +8414,7 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; } + mOnBackInvokedDispatcher.detachFromWindow(); WindowManagerGlobal.getInstance().doRemoveView(this); } @@ -10771,12 +10769,17 @@ public final class ViewRootImpl implements ViewParent, * Returns the {@link OnBackInvokedDispatcher} on the decor view if one exists, or the * fallback {@link OnBackInvokedDispatcher} instance. */ - @Nullable - public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - if (mView instanceof RootViewSurfaceTaker) { - return ((RootViewSurfaceTaker) mView).provideWindowOnBackInvokedDispatcher(); - } - return mFallbackOnBackInvokedDispatcher; + @NonNull + public WindowOnBackInvokedDispatcher getOnBackInvokedDispatcher() { + return mOnBackInvokedDispatcher; + } + + /** + * When this ViewRootImpl is added to the window manager, transfers the first + * {@link OnBackInvokedCallback} to be called to the server. + */ + private void registerBackCallbackOnWindow() { + mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow); } @Override diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index 571714cc05d5..18c20e2b1fa5 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -16,8 +16,6 @@ package android.window; -import static java.util.Objects.requireNonNull; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -61,6 +59,12 @@ public final class BackNavigationInfo implements Parcelable { public static final int TYPE_CROSS_TASK = 3; /** + * A {@link android.view.OnBackInvokedCallback} is available and needs to be called. + * <p> + */ + public static final int TYPE_CALLBACK = 4; + + /** * Defines the type of back destinations a back even can lead to. This is used to define the * type of animation that need to be run on SystemUI. */ @@ -84,35 +88,39 @@ public final class BackNavigationInfo implements Parcelable { private final RemoteCallback mRemoteCallback; @Nullable private final WindowConfiguration mTaskWindowConfiguration; + @Nullable + private final IOnBackInvokedCallback mOnBackInvokedCallback; /** * Create a new {@link BackNavigationInfo} instance. * - * @param type The {@link BackTargetType} of the destination (what will be displayed after - * the back action) - * @param topWindowLeash The leash to animate away the current topWindow. The consumer - * of the leash is responsible for removing it. - * @param screenshotSurface The screenshot of the previous activity to be displayed. - * @param screenshotBuffer A buffer containing a screenshot used to display the activity. - * See {@link #getScreenshotHardwareBuffer()} for information - * about nullity. - * @param taskWindowConfiguration The window configuration of the Task being animated - * beneath. - * @param onBackNavigationDone The callback to be called once the client is done with the back - * preview. + * @param type The {@link BackTargetType} of the destination (what will be + * displayed after the back action). + * @param topWindowLeash The leash to animate away the current topWindow. The consumer + * of the leash is responsible for removing it. + * @param screenshotSurface The screenshot of the previous activity to be displayed. + * @param screenshotBuffer A buffer containing a screenshot used to display the activity. + * See {@link #getScreenshotHardwareBuffer()} for information + * about nullity. + * @param taskWindowConfiguration The window configuration of the Task being animated beneath. + * @param onBackNavigationDone The callback to be called once the client is done with the + * back preview. + * @param onBackInvokedCallback The back callback registered by the current top level window. */ public BackNavigationInfo(@BackTargetType int type, @Nullable SurfaceControl topWindowLeash, @Nullable SurfaceControl screenshotSurface, @Nullable HardwareBuffer screenshotBuffer, @Nullable WindowConfiguration taskWindowConfiguration, - @NonNull RemoteCallback onBackNavigationDone) { + @Nullable RemoteCallback onBackNavigationDone, + @Nullable IOnBackInvokedCallback onBackInvokedCallback) { mType = type; mDepartingWindowContainer = topWindowLeash; mScreenshotSurface = screenshotSurface; mScreenshotBuffer = screenshotBuffer; mTaskWindowConfiguration = taskWindowConfiguration; mRemoteCallback = onBackNavigationDone; + mOnBackInvokedCallback = onBackInvokedCallback; } private BackNavigationInfo(@NonNull Parcel in) { @@ -121,7 +129,8 @@ public final class BackNavigationInfo implements Parcelable { mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR); mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR); mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR); - mRemoteCallback = requireNonNull(in.readTypedObject(RemoteCallback.CREATOR)); + mRemoteCallback = in.readTypedObject(RemoteCallback.CREATOR); + mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder()); } @Override @@ -132,10 +141,12 @@ public final class BackNavigationInfo implements Parcelable { dest.writeTypedObject(mScreenshotBuffer, flags); dest.writeTypedObject(mTaskWindowConfiguration, flags); dest.writeTypedObject(mRemoteCallback, flags); + dest.writeStrongInterface(mOnBackInvokedCallback); } /** * Returns the type of back navigation that is about to happen. + * * @see BackTargetType */ public @BackTargetType int getType() { @@ -152,8 +163,8 @@ public final class BackNavigationInfo implements Parcelable { } /** - * Returns the {@link SurfaceControl} that should be used to display a screenshot of the - * previous activity. + * Returns the {@link SurfaceControl} that should be used to display a screenshot of the + * previous activity. */ @Nullable public SurfaceControl getScreenshotSurface() { @@ -185,11 +196,27 @@ public final class BackNavigationInfo implements Parcelable { } /** + * Returns the {@link android.view.OnBackInvokedCallback} of the top level window or null if + * the client didn't register a callback. + * <p> + * This is never null when {@link #getType} returns {@link #TYPE_CALLBACK}. + * + * @see android.view.OnBackInvokedCallback + * @see android.view.OnBackInvokedDispatcher + */ + @Nullable + public IOnBackInvokedCallback getOnBackInvokedCallback() { + return mOnBackInvokedCallback; + } + + /** * Callback to be called when the back preview is finished in order to notify the server that * it can clean up the resources created for the animation. */ public void onBackNavigationFinished() { - mRemoteCallback.sendResult(null); + if (mRemoteCallback != null) { + mRemoteCallback.sendResult(null); + } } @Override @@ -218,6 +245,7 @@ public final class BackNavigationInfo implements Parcelable { + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration + ", mScreenshotBuffer=" + mScreenshotBuffer + ", mRemoteCallback=" + mRemoteCallback + + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback + '}'; } @@ -226,7 +254,7 @@ public final class BackNavigationInfo implements Parcelable { */ public static String typeToString(@BackTargetType int type) { switch (type) { - case TYPE_UNDEFINED: + case TYPE_UNDEFINED: return "TYPE_UNDEFINED"; case TYPE_DIALOG_CLOSE: return "TYPE_DIALOG_CLOSE"; diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java new file mode 100644 index 000000000000..509bbd4de389 --- /dev/null +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; +import android.util.Pair; +import android.view.OnBackInvokedCallback; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link OnBackInvokedDispatcher} only used to hold callbacks while an actual + * dispatcher becomes available. <b>It does not dispatch the back events</b>. + * <p> + * Once the actual {@link OnBackInvokedDispatcherOwner} becomes available, + * {@link #setActualDispatcherOwner(OnBackInvokedDispatcherOwner)} needs to + * be called and this {@link ProxyOnBackInvokedDispatcher} will pass the callback registrations + * onto it. + * <p> + * This dispatcher will continue to keep track of callback registrations and when a dispatcher is + * removed or set it will unregister the callbacks from the old one and register them on the new + * one unless {@link #reset()} is called before. + * + * @hide + */ +public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher { + + /** + * List of pair representing an {@link OnBackInvokedCallback} and its associated priority. + * + * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int) + */ + private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>(); + private final Object mLock = new Object(); + private OnBackInvokedDispatcherOwner mActualDispatcherOwner = null; + + @Override + public void registerOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback, int priority) { + if (DEBUG) { + Log.v(TAG, String.format("Pending register %s. Actual=%s", callback, + mActualDispatcherOwner)); + } + synchronized (mLock) { + mCallbacks.add(Pair.create(callback, priority)); + if (mActualDispatcherOwner != null) { + mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + callback, priority); + } + + } + } + + @Override + public void unregisterOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback) { + if (DEBUG) { + Log.v(TAG, String.format("Pending unregister %s. Actual=%s", callback, + mActualDispatcherOwner)); + } + synchronized (mLock) { + mCallbacks.removeIf((p) -> p.first.equals(callback)); + } + } + + /** + * Transfers all the pending callbacks to the provided dispatcher. + * <p> + * The callbacks are registered on the dispatcher in the same order as they were added on this + * proxy dispatcher. + */ + private void transferCallbacksToDispatcher() { + if (mActualDispatcherOwner == null) { + return; + } + OnBackInvokedDispatcher dispatcher = + mActualDispatcherOwner.getOnBackInvokedDispatcher(); + if (DEBUG) { + Log.v(TAG, String.format("Pending transferring %d callbacks to %s", mCallbacks.size(), + dispatcher)); + } + for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) { + dispatcher.registerOnBackInvokedCallback(callbackPair.first, + callbackPair.second); + } + mCallbacks.clear(); + } + + private void clearCallbacksOnDispatcher() { + if (mActualDispatcherOwner == null) { + return; + } + OnBackInvokedDispatcher onBackInvokedDispatcher = + mActualDispatcherOwner.getOnBackInvokedDispatcher(); + for (Pair<OnBackInvokedCallback, Integer> callback : mCallbacks) { + onBackInvokedDispatcher.unregisterOnBackInvokedCallback(callback.first); + } + } + + /** + * Resets this {@link ProxyOnBackInvokedDispatcher} so it loses track of the currently + * registered callbacks. + * <p> + * Using this method means that when setting a new {@link OnBackInvokedDispatcherOwner}, the + * callbacks registered on the old one won't be removed from it and won't be registered on + * the new one. + */ + public void reset() { + if (DEBUG) { + Log.v(TAG, "Pending reset callbacks"); + } + synchronized (mLock) { + mCallbacks.clear(); + } + } + + /** + * Sets the actual {@link OnBackInvokedDispatcherOwner} that will provides the + * {@link OnBackInvokedDispatcher} onto which the callbacks will be registered. + * <p> + * If any dispatcher owner was already present, all the callbacks that were added via this + * {@link ProxyOnBackInvokedDispatcher} will be unregistered from the old one and registered + * on the new one if it is not null. + * <p> + * If you do not wish for the previously registered callbacks to be reassigned to the new + * dispatcher, {@link #reset} must be called beforehand. + */ + public void setActualDispatcherOwner( + @Nullable OnBackInvokedDispatcherOwner actualDispatcherOwner) { + if (DEBUG) { + Log.v(TAG, String.format("Pending setActual %s. Current %s", + actualDispatcherOwner, mActualDispatcherOwner)); + } + synchronized (mLock) { + if (actualDispatcherOwner == mActualDispatcherOwner) { + return; + } + clearCallbacksOnDispatcher(); + mActualDispatcherOwner = actualDispatcherOwner; + transferCallbacksToDispatcher(); + } + } +} diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 2925341cd948..40e40856b000 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -87,7 +87,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.OnBackInvokedDispatcher; import android.view.PendingInsetsController; import android.view.ThreadedRenderer; import android.view.View; @@ -109,7 +108,6 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.PopupWindow; -import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; @@ -297,7 +295,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return true; }; private Consumer<Boolean> mCrossWindowBlurEnabledListener; - private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { @@ -326,7 +323,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); - mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(); } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1880,7 +1876,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } mPendingInsetsController.detach(); - mOnBackInvokedDispatcher.detachFromWindow(); } @Override @@ -1925,11 +1920,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return mPendingInsetsController; } - @Override - public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - private ActionMode createActionMode( int type, ActionMode.Callback2 callback, View originatingView) { switch (type) { @@ -2384,7 +2374,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } } - mOnBackInvokedDispatcher.clear(); } @Override @@ -2666,15 +2655,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - /** - * Returns the {@link OnBackInvokedDispatcher} on the decor view. - */ - @Override - @Nullable - public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - @Override public String toString() { return "DecorView@" + Integer.toHexString(this.hashCode()) + "[" diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 7755b694af03..12f38a4d5b9a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -91,6 +91,8 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; import android.view.ScrollCaptureCallback; import android.view.SearchEvent; import android.view.SurfaceHolder.Callback2; @@ -110,6 +112,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import android.window.ProxyOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.view.menu.ContextMenuBuilder; @@ -134,7 +137,8 @@ import java.util.List; * * @hide */ -public class PhoneWindow extends Window implements MenuBuilder.Callback { +public class PhoneWindow extends Window implements MenuBuilder.Callback, + OnBackInvokedDispatcherOwner { private final static String TAG = "PhoneWindow"; @@ -340,6 +344,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean mDecorFitsSystemWindows = true; + private ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher = + new ProxyOnBackInvokedDispatcher(); + static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); @@ -2146,6 +2153,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */ void onViewRootImplSet(ViewRootImpl viewRoot) { viewRoot.setActivityConfigCallback(mActivityConfigCallback); + mProxyOnBackInvokedDispatcher.setActualDispatcherOwner(viewRoot); applyDecorFitsSystemWindows(); } @@ -3993,4 +4001,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public AttachedSurfaceControl getRootSurfaceControl() { return getViewRootImplOrNull(); } + + @NonNull + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + return mProxyOnBackInvokedDispatcher; + } } diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index 4b89bf5082ba..3ab9a335c8b7 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -15,12 +15,10 @@ */ package com.android.internal.view; -import android.annotation.NonNull; import android.annotation.Nullable; import android.view.InputQueue; import android.view.PendingInsetsController; import android.view.SurfaceHolder; -import android.window.WindowOnBackInvokedDispatcher; /** hahahah */ public interface RootViewSurfaceTaker { @@ -31,6 +29,4 @@ public interface RootViewSurfaceTaker { InputQueue.Callback willYouTakeTheInputQueue(); void onRootViewScrollYChanged(int scrollY); @Nullable PendingInsetsController providePendingInsetsController(); - /** @hide */ - @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher(); } diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index d48ea3b8785c..04f4d7b09d82 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -452,16 +452,16 @@ message LowPowerStandbyControllerDumpProto { optional bool is_interactive = 5; // Time (in elapsedRealtime) when the device was last interactive - optional bool last_interactive_time = 6; + optional int64 last_interactive_time = 6; - // Time (in milliseconds) after becoming non-interactive that Low Power Standby can activate + // Timeout (in milliseconds) after becoming non-interactive that Low Power Standby can activate optional int32 standby_timeout_config = 7; // True if the device has entered idle mode since becoming non-interactive - optional int32 idle_since_non_interactive = 8; + optional bool idle_since_non_interactive = 8; // True if the device is currently in idle mode - optional int32 is_device_idle = 9; + optional bool is_device_idle = 9; // Set of app ids that are exempt form low power standby repeated int32 allowlist = 10; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 85504ceae791..9db715bfc4e1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6540,6 +6540,14 @@ android:exported="false"> </activity> + <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity" + android:theme="@style/Theme.Dialog.Confirmation" + android:excludeFromRecents="true" + android:process=":ui" + android:label="@string/log_access_confirmation_title" + android:exported="false"> + </activity> + <activity android:name="com.android.server.notification.NASLearnMoreActivity" android:theme="@style/Theme.Dialog.Confirmation" android:excludeFromRecents="true" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 3a2fb6e70ba8..cb40e86f1535 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2839,6 +2839,14 @@ <attr name="path" /> <attr name="minSdkVersion" /> <attr name="maxSdkVersion" /> + <!-- The order in which the apex system services are initiated. When there are dependencies + among apex system services, setting this attribute for each of them ensures that they are + created in the order required by those dependencies. The apex-system-services that are + started manually within SystemServer ignore the initOrder and are not considered for + automatic starting of the other services. + The value is a simple integer, with higher number being initialized first. If not specified, + the default order is 0. --> + <attr name="initOrder" format="integer" /> </declare-styleable> <!-- The <code>receiver</code> tag declares an diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 52c62050f6a1..0dbd4175b716 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5689,6 +5689,20 @@ <!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] --> <string name="harmful_app_warning_title">Harmful app detected</string> + <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] --> + <string name="log_access_confirmation_title">System log access request</string> + <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] --> + <string name="log_access_confirmation_allow">Only this time</string> + <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] --> + <string name="log_access_confirmation_deny">Don\u2019t allow</string> + + <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> + <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging. + These logs might contain information that apps and services on your device have written.</string> + + <!-- Privacy notice do not show [CHAR LIMIT=20] --> + <string name="log_access_do_not_show_again">Don\u2019t show again</string> + <!-- Text describing a permission request for one app to show another app's slices [CHAR LIMIT=NONE] --> <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index facfdb22b91b..764b2738bce8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3865,6 +3865,11 @@ <java-symbol type="string" name="harmful_app_warning_title" /> <java-symbol type="layout" name="harmful_app_warning_dialog" /> + <java-symbol type="string" name="log_access_confirmation_allow" /> + <java-symbol type="string" name="log_access_confirmation_deny" /> + <java-symbol type="string" name="log_access_confirmation_title" /> + <java-symbol type="string" name="log_access_confirmation_body" /> + <java-symbol type="string" name="config_defaultAssistantAccessComponent" /> <java-symbol type="string" name="slices_permission_request" /> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml index bd987a03b51f..6639c0241ae2 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.BATTERY_STATS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/> <application android:theme="@style/Theme" diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java new file mode 100644 index 000000000000..91d853143764 --- /dev/null +++ b/core/tests/coretests/src/android/window/BackNavigationTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import static junit.framework.Assert.fail; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.annotation.NonNull; +import android.app.ActivityTaskManager; +import android.app.EmptyActivity; +import android.app.Instrumentation; +import android.os.RemoteException; +import android.support.test.uiautomator.UiDevice; +import android.view.OnBackInvokedCallback; + +import androidx.lifecycle.Lifecycle; +import androidx.test.core.app.ActivityScenario; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Integration test for back navigation + */ +public class BackNavigationTest { + + @Rule + public final ActivityScenarioRule<EmptyActivity> mScenarioRule = + new ActivityScenarioRule<>(EmptyActivity.class); + private ActivityScenario<EmptyActivity> mScenario; + private Instrumentation mInstrumentation; + + @Before + public void setup() { + mScenario = mScenarioRule.getScenario(); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + try { + UiDevice.getInstance(mInstrumentation).wakeUp(); + } catch (RemoteException ignored) { + } + mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(); + } + + @Test + public void registerCallback_initialized() { + CountDownLatch latch = registerBackCallback(); + mScenario.moveToState(Lifecycle.State.RESUMED); + assertCallbackIsCalled(latch); + } + + @Test + public void registerCallback_created() { + mScenario.moveToState(Lifecycle.State.CREATED); + CountDownLatch latch = registerBackCallback(); + mScenario.moveToState(Lifecycle.State.STARTED); + mScenario.moveToState(Lifecycle.State.RESUMED); + assertCallbackIsCalled(latch); + } + + @Test + public void registerCallback_resumed() { + mScenario.moveToState(Lifecycle.State.CREATED); + mScenario.moveToState(Lifecycle.State.STARTED); + mScenario.moveToState(Lifecycle.State.RESUMED); + CountDownLatch latch = registerBackCallback(); + assertCallbackIsCalled(latch); + } + + private void assertCallbackIsCalled(CountDownLatch latch) { + try { + mInstrumentation.getUiAutomation().waitForIdle(500, 1000); + BackNavigationInfo info = ActivityTaskManager.getService().startBackNavigation(); + assertNotNull("BackNavigationInfo is null", info); + assertNotNull("OnBackInvokedCallback is null", info.getOnBackInvokedCallback()); + info.getOnBackInvokedCallback().onBackInvoked(); + assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); + } catch (RemoteException ex) { + ex.rethrowFromSystemServer(); + } catch (InterruptedException ex) { + fail("Application died before invoking the callback.\n" + ex.getMessage()); + } catch (TimeoutException ex) { + fail(ex.getMessage()); + } + } + + @NonNull + private CountDownLatch registerBackCallback() { + CountDownLatch backInvokedLatch = new CountDownLatch(1); + CountDownLatch backRegisteredLatch = new CountDownLatch(1); + mScenario.onActivity(activity -> { + activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + new OnBackInvokedCallback() { + @Override + public void onBackInvoked() { + backInvokedLatch.countDown(); + } + }, 0 + ); + backRegisteredLatch.countDown(); + }); + try { + if (!backRegisteredLatch.await(100, TimeUnit.MILLISECONDS)) { + fail("Back callback was not registered on the Activity thread. This might be " + + "an error with the test itself."); + } + } catch (InterruptedException e) { + fail(e.getMessage()); + } + return backInvokedLatch; + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index d0026016ecf4..a331b6eb6750 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -474,6 +474,7 @@ applications that come with the platform <!-- Permission needed for CTS test - WifiManagerTest --> <permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <permission name="android.permission.NEARBY_WIFI_DEVICES" /> <permission name="android.permission.OVERRIDE_WIFI_CONFIG" /> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 15672332a522..f2a875c76f1c 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -727,12 +727,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1343787701": { - "message": "startBackNavigation task=%s, topRunningActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-1340540100": { "message": "Creating SnapshotStartingData", "level": "VERBOSE", @@ -1765,6 +1759,12 @@ "group": "WM_DEBUG_SYNC_ENGINE", "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" }, + "-228813488": { + "message": "%s: Setting back callback %s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "-208825711": { "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s", "level": "DEBUG", @@ -3691,6 +3691,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "1898905572": { + "message": "startBackNavigation task=%s, topRunningActivity=%s, topWindow=%s backCallback=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1903353011": { "message": "notifyAppStopped: %s", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml deleted file mode 100644 index ff5740609b84..000000000000 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@android:color/system_neutral2_400" - android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/> -</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml deleted file mode 100644 index 16dea484b644..000000000000 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@android:color/system_neutral2_200"> - <item android:drawable="@drawable/letterbox_education_ic_expand_more"/> -</ripple>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml deleted file mode 100644 index a309d4829f0d..000000000000 --- a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml +++ /dev/null @@ -1,61 +0,0 @@ -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<com.android.wm.shell.compatui.LetterboxEduToastLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@color/compat_controls_background" - android:gravity="center" - android:paddingVertical="14dp" - android:paddingHorizontal="16dp"> - - <!-- Adding an extra layer to animate the alpha of the background and content separately. --> - <LinearLayout - android:id="@+id/letterbox_education_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/letterbox_education_icon" - android:layout_width="@dimen/letterbox_education_toast_icon_size" - android:layout_height="@dimen/letterbox_education_toast_icon_size"/> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxWidth="@dimen/letterbox_education_toast_text_max_width" - android:paddingHorizontal="16dp" - android:lineSpacingExtra="5sp" - android:text="@string/letterbox_education_toast_title" - android:textAlignment="viewStart" - android:textColor="@color/compat_controls_text" - android:textSize="16sp" - android:maxLines="1" - android:ellipsize="end"/> - - <ImageButton - android:id="@+id/letterbox_education_toast_expand" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/letterbox_education_ic_expand_more_ripple" - android:background="@android:color/transparent" - android:contentDescription="@string/letterbox_education_expand_button_description"/> - - </LinearLayout> - -</com.android.wm.shell.compatui.LetterboxEduToastLayout> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index ab2c9b1466b8..40c7647ecedf 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -219,18 +219,9 @@ <!-- The width of the camera compat hint. --> <dimen name="camera_compat_hint_width">143dp</dimen> - <!-- The corner radius of the letterbox education toast. --> - <dimen name="letterbox_education_toast_corner_radius">100dp</dimen> - <!-- The corner radius of the letterbox education dialog. --> <dimen name="letterbox_education_dialog_corner_radius">28dp</dimen> - <!-- The margin between the letterbox education toast/dialog and the bottom of the task. --> - <dimen name="letterbox_education_margin_bottom">16dp</dimen> - - <!-- The size of the icon in the letterbox education toast. --> - <dimen name="letterbox_education_toast_icon_size">24dp</dimen> - <!-- The size of an icon in the letterbox education dialog. --> <dimen name="letterbox_education_dialog_icon_size">48dp</dimen> @@ -243,9 +234,6 @@ <!-- The maximum width of the title and subtitle in the letterbox education dialog. --> <dimen name="letterbox_education_dialog_title_max_width">444dp</dimen> - <!-- The maximum width of the text in the letterbox education toast. --> - <dimen name="letterbox_education_toast_text_max_width">398dp</dimen> - <!-- The distance that the letterbox education dialog will move up during appear/dismiss animation. --> <dimen name="letterbox_education_dialog_animation_elevation">20dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index a8a9ed74cd35..16a4b524803a 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -174,9 +174,6 @@ <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] --> <string name="letterbox_education_dialog_title">Get the most out of <xliff:g id="app_name" example="YouTube">%s</xliff:g></string> - <!-- The title of the letterbox education toast. [CHAR LIMIT=60] --> - <string name="letterbox_education_toast_title">Rotate your device for a full-screen view</string> - <!-- Description of the rotate screen into portrait action. [CHAR LIMIT=NONE] --> <string name="letterbox_education_screen_rotation_portrait_text">Rotate your screen to portrait</string> @@ -192,7 +189,4 @@ <!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] --> <string name="letterbox_education_got_it">Got it</string> - <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] --> - <string name="letterbox_education_expand_button_description">Expand for more information.</string> - </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java deleted file mode 100644 index e7f592dcb14d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.compatui.letterboxedu; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.wm.shell.R; - -/** - * Container for the Letterbox Education Toast. - */ -// TODO(b/215316431): Add tests -public class LetterboxEduToastLayout extends FrameLayout { - - public LetterboxEduToastLayout(Context context) { - this(context, null); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - /** - * Register a callback for the dismiss button. - * @param callback The callback to register - */ - void setExpandOnClickListener(Runnable callback) { - findViewById(R.id.letterbox_education_toast_expand).setOnClickListener( - view -> callback.run()); - } - - /** - * Updates the layout with the given app info. - * @param appName The name of the app - * @param appIcon The icon of the app - */ - void updateAppInfo(String appName, Drawable appIcon) { - ImageView icon = findViewById(R.id.letterbox_education_icon); - icon.setContentDescription(appName); - icon.setImageDrawable(appIcon); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 3cfa541c1c86..d022ec15f232 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -89,9 +89,17 @@ interface ISplitScreen { /** * Version of startTasks using legacy transition system. */ - oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, - int sideTaskId, in Bundle sideOptions, int sidePosition, - float splitRatio, in RemoteAnimationAdapter adapter) = 11; + oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, + int sideTaskId, in Bundle sideOptions, int sidePosition, + float splitRatio, in RemoteAnimationAdapter adapter) = 11; + + /** + * Start a pair of intent and task using legacy transition system. + */ + oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, + in Intent fillInIntent, int taskId, boolean intentFirst, in Bundle mainOptions, + in Bundle sideOptions, int sidePosition, float splitRatio, + in RemoteAnimationAdapter adapter) = 12; /** * Blocking call that notifies and gets additional split-screen targets when entering @@ -100,5 +108,7 @@ interface ISplitScreen { * @param appTargets apps that will be re-parented to display area */ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - in RemoteAnimationTarget[] appTargets) = 12; + in RemoteAnimationTarget[] appTargets) = 13; + + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 3e6dc8241f4f..990b53a601f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -641,6 +641,18 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override + public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, + Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions, + Bundle sideOptions, int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter) { + executeRemoteCallWithTaskPermission(mController, + "startIntentAndTaskWithLegacyTransition", (controller) -> + controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition( + pendingIntent, fillInIntent, taskId, intentFirst, mainOptions, + sideOptions, sidePosition, splitRatio, adapter)); + } + + @Override public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index a2c2f591cde0..219530b46da4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -57,8 +57,10 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; +import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; @@ -467,6 +469,116 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } + /** Start an intent and a task ordered by {@code intentFirst}. */ + void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, + int taskId, boolean intentFirst, @Nullable Bundle mainOptions, + @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter) { + // TODO: try pulling the first chunk of this method into a method so that it can be shared + // with startTasksWithLegacyTransition. So far attempts to do so result in failure in split. + + // Init divider first to make divider leash for remote animation target. + mSplitLayout.init(); + // Set false to avoid record new bounds with old task still on top; + mShouldUpdateRecents = false; + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final WindowContainerTransaction evictWct = new WindowContainerTransaction(); + prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct); + prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct); + // Need to add another wrapper here in shell so that we can inject the divider bar + // and also manage the process elevation via setRunningRemote + IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { + @Override + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + final IRemoteAnimationFinishedCallback finishedCallback) { + RemoteAnimationTarget[] augmentedNonApps = + new RemoteAnimationTarget[nonApps.length + 1]; + for (int i = 0; i < nonApps.length; ++i) { + augmentedNonApps[i] = nonApps[i]; + } + augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget(); + + IRemoteAnimationFinishedCallback wrapCallback = + new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() throws RemoteException { + mShouldUpdateRecents = true; + mSyncQueue.queue(evictWct); + mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + finishedCallback.onAnimationFinished(); + } + }; + try { + try { + ActivityTaskManager.getService().setRunningRemoteTransitionDelegate( + adapter.getCallingApplication()); + } catch (SecurityException e) { + Slog.e(TAG, "Unable to boost animation thread. This should only happen" + + " during unit tests"); + } + adapter.getRunner().onAnimationStart(transit, apps, wallpapers, + augmentedNonApps, wrapCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Error starting remote animation", e); + } + } + + @Override + public void onAnimationCancelled() { + mShouldUpdateRecents = true; + mSyncQueue.queue(evictWct); + mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + try { + adapter.getRunner().onAnimationCancelled(); + } catch (RemoteException e) { + Slog.e(TAG, "Error starting remote animation", e); + } + } + }; + RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter( + wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay()); + + if (mainOptions == null) { + mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle(); + } else { + ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions); + mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter)); + mainOptions = mainActivityOptions.toBundle(); + } + + sideOptions = sideOptions != null ? sideOptions : new Bundle(); + setSideStagePosition(sidePosition, wct); + + mSplitLayout.setDivideRatio(splitRatio); + if (mMainStage.isActive()) { + mMainStage.moveToTop(getMainStageBounds(), wct); + } else { + // Build a request WCT that will launch both apps such that task 0 is on the main stage + // while task 1 is on the side stage. + mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); + } + mSideStage.moveToTop(getSideStageBounds(), wct); + + // Make sure the launch options will put tasks in the corresponding split roots + addActivityOptions(mainOptions, mMainStage); + addActivityOptions(sideOptions, mSideStage); + + // Add task launch requests + if (intentFirst) { + wct.sendPendingIntent(pendingIntent, fillInIntent, mainOptions); + wct.startTask(taskId, sideOptions); + } else { + wct.startTask(taskId, mainOptions); + wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions); + } + + // Using legacy transitions, so we can't use blast sync since it conflicts. + mTaskOrganizer.applyTransaction(wct); + } + /** * Collects all the current child tasks of a specific split and prepares transaction to evict * them to display. diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index 574a9f4da627..556742e2ac5f 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -26,8 +26,16 @@ <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6000s" /> <option name="hidden-api-checks" value="false" /> + <option name="device-listeners" + value="com.android.server.wm.flicker.TraceFileReadyListener" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="(\w)+\.winscope" /> + <option name="pull-pattern-keys" value="(\w)+\.mp4" /> + <option name="collect-on-run-ended-only" value="false" /> + <option name="clean-up" value="true" /> + </metrics_collector> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 960c7ac4099a..b738c47ef6ff 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -75,7 +75,8 @@ public class BackAnimationControllerTest { screenshotSurface, hardwareBuffer, new WindowConfiguration(), - new RemoteCallback((bundle) -> {})); + new RemoteCallback((bundle) -> {}), + null); try { doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation(); } catch (RemoteException ex) { diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index c1e9b38a13b0..9fbea724f491 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -25,6 +25,9 @@ import android.media.tv.tuner.Lnb; import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * A Frontend Status class that contains the metrics of the active frontend. @@ -1086,22 +1089,26 @@ public class FrontendStatus { } /** - * Gets an array of all PLPs information of ATSC3 frontend, which includes both tuned and not + * Gets a list of all PLPs information of ATSC3 frontend, which includes both tuned and not * tuned PLPs for currently watching service. * - * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL - * doesn't return all PLPs information will throw IllegalStateException. Use - * {@link TunerVersionChecker#getTunerVersion()} to check the version. + * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will throw + * UnsupportedOperationException. Use {@link TunerVersionChecker#getTunerVersion()} to check + * the version. + * + * @return a list of all PLPs information. It is empty if HAL doesn't return all PLPs + * information status. */ - @SuppressLint("ArrayReturn") @NonNull - public Atsc3PlpInfo[] getAllAtsc3PlpInfo() { - TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status"); + public List<Atsc3PlpInfo> getAllAtsc3PlpInfo() { + if (!TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status")) { + throw new UnsupportedOperationException("Atsc3PlpInfo all status is empty"); + } if (mAllPlpInfo == null) { - throw new IllegalStateException("Atsc3PlpInfo all status is empty"); + return Collections.EMPTY_LIST; } - return mAllPlpInfo; + return Arrays.asList(mAllPlpInfo); } /** diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 54538d91a5cf..6329565ca567 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -158,7 +158,6 @@ filegroup { name: "framework-connectivity-tiramisu-sources", srcs: [ ":framework-connectivity-ethernet-sources", - ":framework-connectivity-ipsec-sources", ":framework-connectivity-netstats-sources", ], visibility: ["//frameworks/base"], @@ -167,6 +166,7 @@ filegroup { filegroup { name: "framework-connectivity-tiramisu-updatable-sources", srcs: [ + ":framework-connectivity-ipsec-sources", ":framework-connectivity-nsd-sources", ":framework-connectivity-tiramisu-internal-sources", ], diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java index 630f902ecfd7..577ac5466692 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java +++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java @@ -48,5 +48,14 @@ public final class ConnectivityFrameworkInitializerTiramisu { return new NsdManager(context, service); } ); + + SystemServiceRegistry.registerContextAwareService( + Context.IPSEC_SERVICE, + IpSecManager.class, + (context, serviceBinder) -> { + IIpSecService service = IIpSecService.Stub.asInterface(serviceBinder); + return new IpSecManager(context, service); + } + ); } } diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp index 24bc91d91ef7..d2d6ef0cbbb1 100644 --- a/packages/ConnectivityT/service/Android.bp +++ b/packages/ConnectivityT/service/Android.bp @@ -83,7 +83,6 @@ filegroup { name: "services.connectivity-tiramisu-sources", srcs: [ ":services.connectivity-ethernet-sources", - ":services.connectivity-ipsec-sources", ":services.connectivity-netstats-sources", ], path: "src", @@ -93,6 +92,7 @@ filegroup { filegroup { name: "services.connectivity-tiramisu-updatable-sources", srcs: [ + ":services.connectivity-ipsec-sources", ":services.connectivity-nsd-sources", ], path: "src", diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java index 179d9459fd84..4bc40eae4404 100644 --- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java +++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java @@ -1008,16 +1008,10 @@ public class IpSecService extends IIpSecService.Stub { * * @param context Binder context for this service */ - private IpSecService(Context context) { + public IpSecService(Context context) { this(context, new Dependencies()); } - static IpSecService create(Context context) - throws InterruptedException { - final IpSecService service = new IpSecService(context); - return service; - } - @NonNull private AppOpsManager getAppOpsManager() { AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); @@ -1054,26 +1048,6 @@ public class IpSecService extends IIpSecService.Stub { } } - /** Called by system server when system is ready. */ - public void systemReady() { - if (isNetdAlive()) { - Log.d(TAG, "IpSecService is ready"); - } else { - Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!"); - } - } - - synchronized boolean isNetdAlive() { - try { - if (mNetd == null) { - return false; - } - return mNetd.isAlive(); - } catch (RemoteException re) { - return false; - } - } - /** * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1. @@ -1896,7 +1870,6 @@ public class IpSecService extends IIpSecService.Stub { mContext.enforceCallingOrSelfPermission(DUMP, TAG); pw.println("IpSecService dump:"); - pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead")); pw.println(); pw.println("mUserResourceTracker:"); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index ca90fbedd4e0..6f4cd4a0f6ee 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -533,6 +533,7 @@ <!-- Permission needed for CTS test - WifiManagerTest --> <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" /> <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" /> <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index f7a7603944f6..3051d8056a89 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -19,6 +19,7 @@ package com.android.systemui.animation import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.app.ActivityManager import android.app.Dialog import android.graphics.Color import android.graphics.Rect @@ -45,7 +46,8 @@ private const val TAG = "DialogLaunchAnimator" class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), - private var isForTesting: Boolean = false + // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed + private var forceDisableSynchronization: Boolean = ActivityManager.isLowRamDeviceStatic() ) { private companion object { private val TIMINGS = ActivityLaunchAnimator.TIMINGS @@ -111,7 +113,7 @@ class DialogLaunchAnimator @JvmOverloads constructor( dialog = dialog, animateBackgroundBoundsChange, animatedParent, - isForTesting + forceDisableSynchronization ) openedDialogs.add(animatedDialog) @@ -187,10 +189,9 @@ private class AnimatedDialog( private val parentAnimatedDialog: AnimatedDialog? = null, /** - * Whether we are currently running in a test, in which case we need to disable - * synchronization. + * Whether synchronization should be disabled, which can be useful if we are running in a test. */ - private val isForTesting: Boolean + private val forceDisableSynchronization: Boolean ) { /** * The DecorView of this dialog window. @@ -420,8 +421,9 @@ private class AnimatedDialog( * (or inversely, removed from the UI when the touch surface is made visible). */ private fun synchronizeNextDraw(then: () -> Unit) { - if (isForTesting || !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null || - !decorView.isAttachedToWindow || decorView.viewRootImpl == null) { + if (forceDisableSynchronization || + !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null || + !decorView.isAttachedToWindow || decorView.viewRootImpl == null) { // No need to synchronize if either the touch surface or dialog view is not attached // to a window. then() diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 4817d453ba0b..c58c00114262 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -14,126 +14,137 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.clipboardoverlay.DraggableConstraintLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:theme="@style/FloatingOverlay" android:alpha="0" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView - android:id="@+id/actions_container_background" - android:visibility="gone" - android:layout_height="0dp" - android:layout_width="0dp" - android:elevation="1dp" - android:background="@drawable/action_chip_container_background" - android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" - app:layout_constraintBottom_toBottomOf="@+id/actions_container" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@+id/actions_container" - app:layout_constraintEnd_toEndOf="@+id/actions_container"/> - <HorizontalScrollView - android:id="@+id/actions_container" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" - android:paddingEnd="@dimen/overlay_action_container_padding_right" - android:paddingVertical="@dimen/overlay_action_container_padding_vertical" - android:elevation="1dp" - android:scrollbars="none" - app:layout_constraintHorizontal_bias="0" - app:layout_constraintWidth_percent="1.0" - app:layout_constraintWidth_max="wrap" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toEndOf="@+id/preview_border" - app:layout_constraintEnd_toEndOf="parent"> - <LinearLayout - android:id="@+id/actions" + android:id="@+id/background_protection" + android:layout_height="@dimen/overlay_bg_protection_height" + android:layout_width="match_parent" + android:layout_gravity="bottom" + android:src="@drawable/overlay_actions_background_protection"/> + <com.android.systemui.clipboardoverlay.DraggableConstraintLayout + android:id="@+id/clipboard_ui" + android:theme="@style/FloatingOverlay" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/actions_container_background" + android:visibility="gone" + android:layout_height="0dp" + android:layout_width="0dp" + android:elevation="1dp" + android:background="@drawable/action_chip_container_background" + android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" + app:layout_constraintBottom_toBottomOf="@+id/actions_container" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/actions_container" + app:layout_constraintEnd_toEndOf="@+id/actions_container"/> + <HorizontalScrollView + android:id="@+id/actions_container" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" + android:paddingEnd="@dimen/overlay_action_container_padding_right" + android:paddingVertical="@dimen/overlay_action_container_padding_vertical" + android:elevation="1dp" + android:scrollbars="none" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintWidth_percent="1.0" + app:layout_constraintWidth_max="wrap" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@+id/preview_border" + app:layout_constraintEnd_toEndOf="parent"> + <LinearLayout + android:id="@+id/actions" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:animateLayoutChanges="true"> + <include layout="@layout/overlay_action_chip" + android:id="@+id/remote_copy_chip"/> + <include layout="@layout/overlay_action_chip" + android:id="@+id/edit_chip"/> + </LinearLayout> + </HorizontalScrollView> + <View + android:id="@+id/preview_border" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginStart="@dimen/overlay_offset_x" + android:layout_marginBottom="@dimen/overlay_offset_y" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toBottomOf="@id/actions_container_background" + android:elevation="@dimen/overlay_preview_elevation" + app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end" + app:layout_constraintTop_toTopOf="@id/clipboard_preview_top" + android:background="@drawable/overlay_border"/> + <androidx.constraintlayout.widget.Barrier + android:id="@+id/clipboard_preview_end" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:animateLayoutChanges="true"> - <include layout="@layout/overlay_action_chip" - android:id="@+id/remote_copy_chip"/> - <include layout="@layout/overlay_action_chip" - android:id="@+id/edit_chip"/> - </LinearLayout> - </HorizontalScrollView> - <View - android:id="@+id/preview_border" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_marginStart="@dimen/overlay_offset_x" - android:layout_marginBottom="@dimen/overlay_offset_y" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="@id/actions_container_background" - android:elevation="@dimen/overlay_preview_elevation" - app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end" - app:layout_constraintTop_toTopOf="@id/clipboard_preview_top" - android:background="@drawable/overlay_border"/> - <androidx.constraintlayout.widget.Barrier - android:id="@+id/clipboard_preview_end" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:barrierMargin="@dimen/overlay_border_width" - app:barrierDirection="end" - app:constraint_referenced_ids="clipboard_preview"/> - <androidx.constraintlayout.widget.Barrier - android:id="@+id/clipboard_preview_top" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:barrierDirection="top" - app:barrierMargin="@dimen/overlay_border_width_neg" - app:constraint_referenced_ids="clipboard_preview"/> - <FrameLayout - android:id="@+id/clipboard_preview" - android:elevation="@dimen/overlay_preview_elevation" - android:background="@drawable/overlay_preview_background" - android:clipChildren="true" - android:clipToOutline="true" - android:clipToPadding="true" - android:layout_width="@dimen/clipboard_preview_size" - android:layout_margin="@dimen/overlay_border_width" - android:layout_height="wrap_content" - android:layout_gravity="center" - app:layout_constraintBottom_toBottomOf="@id/preview_border" - app:layout_constraintStart_toStartOf="@id/preview_border" - app:layout_constraintEnd_toEndOf="@id/preview_border" - app:layout_constraintTop_toTopOf="@id/preview_border"> - <TextView android:id="@+id/text_preview" - android:textFontWeight="500" - android:padding="8dp" - android:gravity="center|start" - android:ellipsize="end" - android:autoSizeTextType="uniform" - android:autoSizeMinTextSize="10sp" - android:autoSizeMaxTextSize="200sp" - android:textColor="?android:attr/textColorPrimary" - android:layout_width="@dimen/clipboard_preview_size" - android:layout_height="@dimen/clipboard_preview_size"/> - <ImageView - android:id="@+id/image_preview" - android:scaleType="fitCenter" - android:adjustViewBounds="true" - android:layout_width="match_parent" - android:layout_height="wrap_content"/> - </FrameLayout> - <FrameLayout - android:id="@+id/dismiss_button" - android:layout_width="@dimen/overlay_dismiss_button_tappable_size" - android:layout_height="@dimen/overlay_dismiss_button_tappable_size" - android:elevation="@dimen/overlay_dismiss_button_elevation" - android:visibility="gone" - app:layout_constraintStart_toEndOf="@id/clipboard_preview" - app:layout_constraintEnd_toEndOf="@id/clipboard_preview" - app:layout_constraintTop_toTopOf="@id/clipboard_preview" - app:layout_constraintBottom_toTopOf="@id/clipboard_preview" - android:contentDescription="@string/clipboard_dismiss_description"> - <ImageView - android:id="@+id/dismiss_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="@dimen/overlay_dismiss_button_margin" - android:src="@drawable/overlay_cancel"/> - </FrameLayout> -</com.android.systemui.clipboardoverlay.DraggableConstraintLayout> + app:barrierMargin="@dimen/overlay_border_width" + app:barrierDirection="end" + app:constraint_referenced_ids="clipboard_preview"/> + <androidx.constraintlayout.widget.Barrier + android:id="@+id/clipboard_preview_top" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="top" + app:barrierMargin="@dimen/overlay_border_width_neg" + app:constraint_referenced_ids="clipboard_preview"/> + <FrameLayout + android:id="@+id/clipboard_preview" + android:elevation="@dimen/overlay_preview_elevation" + android:background="@drawable/overlay_preview_background" + android:clipChildren="true" + android:clipToOutline="true" + android:clipToPadding="true" + android:layout_width="@dimen/clipboard_preview_size" + android:layout_margin="@dimen/overlay_border_width" + android:layout_height="wrap_content" + android:layout_gravity="center" + app:layout_constraintBottom_toBottomOf="@id/preview_border" + app:layout_constraintStart_toStartOf="@id/preview_border" + app:layout_constraintEnd_toEndOf="@id/preview_border" + app:layout_constraintTop_toTopOf="@id/preview_border"> + <TextView android:id="@+id/text_preview" + android:textFontWeight="500" + android:padding="8dp" + android:gravity="center|start" + android:ellipsize="end" + android:autoSizeTextType="uniform" + android:autoSizeMinTextSize="10sp" + android:autoSizeMaxTextSize="200sp" + android:textColor="?android:attr/textColorPrimary" + android:layout_width="@dimen/clipboard_preview_size" + android:layout_height="@dimen/clipboard_preview_size"/> + <ImageView + android:id="@+id/image_preview" + android:scaleType="fitCenter" + android:adjustViewBounds="true" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + </FrameLayout> + <FrameLayout + android:id="@+id/dismiss_button" + android:layout_width="@dimen/overlay_dismiss_button_tappable_size" + android:layout_height="@dimen/overlay_dismiss_button_tappable_size" + android:elevation="@dimen/overlay_dismiss_button_elevation" + android:visibility="gone" + app:layout_constraintStart_toEndOf="@id/clipboard_preview" + app:layout_constraintEnd_toEndOf="@id/clipboard_preview" + app:layout_constraintTop_toTopOf="@id/clipboard_preview" + app:layout_constraintBottom_toTopOf="@id/clipboard_preview" + android:contentDescription="@string/clipboard_dismiss_description"> + <ImageView + android:id="@+id/dismiss_image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="@dimen/overlay_dismiss_button_margin" + android:src="@drawable/overlay_cancel"/> + </FrameLayout> + </com.android.systemui.clipboardoverlay.DraggableConstraintLayout> +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml index a41d34f8ab41..82c8d5f04327 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="@dimen/dream_overlay_complication_clock_time_padding_left" - android:fontFamily="sans-serif-thin" + android:fontFamily="@font/clock" android:textColor="@android:color/white" android:format12Hour="h:mm" android:format24Hour="kk:mm" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4dca0b0076d9..e5cabb0ecac0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2379,11 +2379,15 @@ <string name="fgs_manager_dialog_title">Active apps</string> <!-- Label of the button to stop an app from running [CHAR LIMIT=12]--> <string name="fgs_manager_app_item_stop_button_label">Stop</string> + <!-- Label of the button to stop an app from running but the app is already stopped and the button is disabled [CHAR LIMIT=12]--> + <string name="fgs_manager_app_item_stop_button_stopped_label">Stopped</string> <!-- Label for button to copy edited text back to the clipboard [CHAR LIMIT=20] --> <string name="clipboard_edit_text_copy">Copy</string> <!-- Text informing user that content has been copied to the system clipboard [CHAR LIMIT=NONE] --> <string name="clipboard_overlay_text_copied">Copied</string> + <!-- Text informing user where text being edited was copied from [CHAR LIMIT=NONE] --> + <string name="clipboard_edit_source">From <xliff:g id="appName" example="Gmail">%1$s</xliff:g></string> <!-- Label for button to dismiss clipboard overlay [CHAR LIMIT=NONE] --> <string name="clipboard_dismiss_description">Dismiss copy UI</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java index 72b40d42b7b8..54664f2fdd93 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java @@ -48,7 +48,7 @@ public class ClipboardListener extends CoreStartable @Override public void start() { if (DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) { + DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) { mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class)); mClipboardManager.addPrimaryClipChangedListener(this); } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 8b549b43019f..f6d64643b3cd 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -103,6 +103,7 @@ public class ClipboardOverlayController { private final AccessibilityManager mAccessibilityManager; private final TextClassifier mTextClassifier; + private final FrameLayout mContainer; private final DraggableConstraintLayout mView; private final ImageView mImagePreview; private final TextView mTextPreview; @@ -147,8 +148,9 @@ public class ClipboardOverlayController { mWindow = FloatingWindowUtil.getFloatingWindow(mContext); mWindow.setWindowManager(mWindowManager, null, null); - mView = (DraggableConstraintLayout) + mContainer = (FrameLayout) LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null); + mView = requireNonNull(mContainer.findViewById(R.id.clipboard_ui)); mActionContainerBackground = requireNonNull(mView.findViewById(R.id.actions_container_background)); mActionContainer = requireNonNull(mView.findViewById(R.id.actions)); @@ -180,7 +182,7 @@ public class ClipboardOverlayController { attachWindow(); withWindowAttached(() -> { - mWindow.setContentView(mView); + mWindow.setContentView(mContainer); updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets()); mView.requestLayout(); mView.post(this::animateIn); @@ -371,7 +373,7 @@ public class ClipboardOverlayController { private ValueAnimator getEnterAnimation() { ValueAnimator anim = ValueAnimator.ofFloat(0, 1); - mView.setAlpha(0); + mContainer.setAlpha(0); mDismissButton.setVisibility(View.GONE); final View previewBorder = requireNonNull(mView.findViewById(R.id.preview_border)); final View actionBackground = requireNonNull( @@ -383,7 +385,7 @@ public class ClipboardOverlayController { } anim.addUpdateListener(animation -> { - mView.setAlpha(animation.getAnimatedFraction()); + mContainer.setAlpha(animation.getAnimatedFraction()); float scale = 0.6f + 0.4f * animation.getAnimatedFraction(); mView.setPivotY(mView.getHeight() - previewBorder.getHeight() / 2f); mView.setPivotX(actionBackground.getWidth() / 2f); @@ -394,7 +396,7 @@ public class ClipboardOverlayController { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - mView.setAlpha(1); + mContainer.setAlpha(1); mTimeoutHandler.resetTimeout(); } }); @@ -439,7 +441,7 @@ public class ClipboardOverlayController { private void reset() { mView.setTranslationX(0); - mView.setAlpha(0); + mContainer.setAlpha(0); resetActionChips(); mTimeoutHandler.cancelTimeout(); } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java index be10c359f3c1..a57a1351779f 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java @@ -22,9 +22,12 @@ import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.util.Log; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; +import android.widget.TextView; import com.android.systemui.R; @@ -32,8 +35,11 @@ import com.android.systemui.R; * Lightweight activity for editing text clipboard contents */ public class EditTextActivity extends Activity { + private static final String TAG = "EditTextActivity"; + private EditText mEditText; private ClipboardManager mClipboardManager; + private TextView mAttribution; @Override protected void onCreate(Bundle savedInstanceState) { @@ -42,6 +48,7 @@ public class EditTextActivity extends Activity { findViewById(R.id.copy_button).setOnClickListener((v) -> saveToClipboard()); findViewById(R.id.share).setOnClickListener((v) -> share()); mEditText = findViewById(R.id.edit_text); + mAttribution = findViewById(R.id.attribution); mClipboardManager = requireNonNull(getSystemService(ClipboardManager.class)); } @@ -53,7 +60,15 @@ public class EditTextActivity extends Activity { finish(); return; } - // TODO: put clip attribution in R.id.attribution TextView + PackageManager pm = getApplicationContext().getPackageManager(); + try { + CharSequence label = pm.getApplicationLabel( + pm.getApplicationInfo(mClipboardManager.getPrimaryClipSource(), + PackageManager.ApplicationInfoFlags.of(0))); + mAttribution.setText(getResources().getString(R.string.clipboard_edit_source, label)); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Package not found: " + mClipboardManager.getPrimaryClipSource(), e); + } mEditText.setText(clip.getItemAt(0).getText()); mEditText.requestFocus(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java index 053c5b345760..d539f5c2b870 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java @@ -22,6 +22,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextClock; import com.android.internal.util.Preconditions; import com.android.systemui.R; @@ -78,6 +79,8 @@ public interface DreamClockTimeComplicationComponent { "clock_time_complication_layout_params"; // Order weight of insert into parent container int INSERT_ORDER_WEIGHT = 0; + String TAG_WEIGHT = "'wght' "; + int WEIGHT = 200; /** * Provides the complication view. @@ -86,10 +89,12 @@ public interface DreamClockTimeComplicationComponent { @DreamClockTimeComplicationScope @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) static View provideComplicationView(LayoutInflater layoutInflater) { - return Preconditions.checkNotNull( - layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, - null, false), + final TextClock view = Preconditions.checkNotNull((TextClock) + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, + null, false), "R.layout.dream_overlay_complication_clock_time did not properly inflated"); + view.setFontVariationSettings(TAG_WEIGHT + WEIGHT); + return view; } /** diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt deleted file mode 100644 index 42f3512129a3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2021 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.fgsmanager - -import android.content.Context -import android.os.Bundle -import android.text.format.DateUtils -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.ImageView -import android.widget.TextView -import androidx.annotation.GuardedBy -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.android.systemui.R -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.fgsmanager.FgsManagerDialogController.RunningApp -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor - -/** - * Dialog which shows a list of running foreground services and offers controls to them - */ -class FgsManagerDialog( - context: Context, - private val executor: Executor, - @Background private val backgroundExecutor: Executor, - private val systemClock: SystemClock, - private val fgsManagerDialogController: FgsManagerDialogController -) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog) { - - private val appListRecyclerView: RecyclerView = RecyclerView(this.context) - private val adapter: AppListAdapter = AppListAdapter() - - init { - setTitle(R.string.fgs_manager_dialog_title) - setView(appListRecyclerView) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - appListRecyclerView.layoutManager = LinearLayoutManager(context) - fgsManagerDialogController.registerDialogForChanges( - object : FgsManagerDialogController.FgsManagerDialogCallback { - override fun onRunningAppsChanged(apps: List<RunningApp>) { - executor.execute { - adapter.setData(apps) - } - } - } - ) - appListRecyclerView.adapter = adapter - backgroundExecutor.execute { adapter.setData(fgsManagerDialogController.runningAppList) } - } - - private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() { - private val lock = Any() - - @GuardedBy("lock") - private val data: MutableList<RunningApp> = ArrayList() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder { - return AppItemViewHolder(LayoutInflater.from(context) - .inflate(R.layout.fgs_manager_app_item, parent, false)) - } - - override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) { - var runningApp: RunningApp - synchronized(lock) { - runningApp = data[position] - } - with(holder) { - iconView.setImageDrawable(runningApp.mIcon) - appLabelView.text = runningApp.mAppLabel - durationView.text = DateUtils.formatDuration( - Math.max(systemClock.elapsedRealtime() - runningApp.mTimeStarted, 60000), - DateUtils.LENGTH_MEDIUM) - stopButton.setOnClickListener { - fgsManagerDialogController - .stopAllFgs(runningApp.mUserId, runningApp.mPackageName) - } - } - } - - override fun getItemCount(): Int { - synchronized(lock) { return data.size } - } - - fun setData(newData: List<RunningApp>) { - var oldData: List<RunningApp> - synchronized(lock) { - oldData = ArrayList(data) - data.clear() - data.addAll(newData) - } - - DiffUtil.calculateDiff(object : DiffUtil.Callback() { - override fun getOldListSize(): Int { - return oldData.size - } - - override fun getNewListSize(): Int { - return newData.size - } - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): - Boolean { - return oldData[oldItemPosition] == newData[newItemPosition] - } - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): - Boolean { - return true // TODO, look into updating the time subtext - } - }).dispatchUpdatesTo(this) - } - } - - private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) { - val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label) - val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration) - val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon) - val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt deleted file mode 100644 index 159ed39025a1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2021 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.fgsmanager - -import android.content.pm.PackageManager -import android.content.pm.PackageManager.NameNotFoundException -import android.graphics.drawable.Drawable -import android.os.Handler -import android.os.UserHandle -import android.util.ArrayMap -import android.util.Log -import androidx.annotation.GuardedBy -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.policy.RunningFgsController -import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime -import javax.inject.Inject - -/** - * Controls events relevant to FgsManagerDialog - */ -class FgsManagerDialogController @Inject constructor( - private val packageManager: PackageManager, - @Background private val backgroundHandler: Handler, - private val runningFgsController: RunningFgsController -) : RunningFgsController.Callback { - private val lock = Any() - private val clearCacheToken = Any() - - @GuardedBy("lock") - private var runningApps: Map<UserPackageTime, RunningApp>? = null - @GuardedBy("lock") - private var listener: FgsManagerDialogCallback? = null - - interface FgsManagerDialogCallback { - fun onRunningAppsChanged(apps: List<RunningApp>) - } - - data class RunningApp( - val mUserId: Int, - val mPackageName: String, - val mAppLabel: CharSequence, - val mIcon: Drawable, - val mTimeStarted: Long - ) - - val runningAppList: List<RunningApp> - get() { - synchronized(lock) { - if (runningApps == null) { - onFgsPackagesChangedLocked(runningFgsController.getPackagesWithFgs()) - } - return convertToRunningAppList(runningApps!!) - } - } - - fun registerDialogForChanges(callback: FgsManagerDialogCallback) { - synchronized(lock) { - runningFgsController.addCallback(this) - listener = callback - backgroundHandler.removeCallbacksAndMessages(clearCacheToken) - } - } - - fun onFinishDialog() { - synchronized(lock) { - listener = null - // Keep data such as icons cached for some time since loading can be slow - backgroundHandler.postDelayed( - { - synchronized(lock) { - runningFgsController.removeCallback(this) - runningApps = null - } - }, clearCacheToken, RUNNING_APP_CACHE_TIMEOUT_MILLIS) - } - } - - private fun onRunningAppsChanged(apps: ArrayMap<UserPackageTime, RunningApp>) { - listener?.let { - backgroundHandler.post { it.onRunningAppsChanged(convertToRunningAppList(apps)) } - } - } - - override fun onFgsPackagesChanged(packages: List<UserPackageTime>) { - backgroundHandler.post { - synchronized(lock) { onFgsPackagesChangedLocked(packages) } - } - } - - /** - * Run on background thread - */ - private fun onFgsPackagesChangedLocked(packages: List<UserPackageTime>) { - val newRunningApps = ArrayMap<UserPackageTime, RunningApp>() - for (packageWithFgs in packages) { - val ra = runningApps?.get(packageWithFgs) - if (ra == null) { - val userId = packageWithFgs.userId - val packageName = packageWithFgs.packageName - try { - val ai = packageManager.getApplicationInfo(packageName, 0) - var icon = packageManager.getApplicationIcon(ai) - icon = packageManager.getUserBadgedIcon(icon, - UserHandle.of(userId)) - val label = packageManager.getApplicationLabel(ai) - newRunningApps[packageWithFgs] = RunningApp(userId, packageName, - label, icon, packageWithFgs.startTimeMillis) - } catch (e: NameNotFoundException) { - Log.e(LOG_TAG, - "Application info not found: $packageName", e) - } - } else { - newRunningApps[packageWithFgs] = ra - } - } - runningApps = newRunningApps - onRunningAppsChanged(newRunningApps) - } - - fun stopAllFgs(userId: Int, packageName: String) { - runningFgsController.stopFgs(userId, packageName) - } - - companion object { - private val LOG_TAG = FgsManagerDialogController::class.java.simpleName - private const val RUNNING_APP_CACHE_TIMEOUT_MILLIS: Long = 20_000 - - private fun convertToRunningAppList(apps: Map<UserPackageTime, RunningApp>): - List<RunningApp> { - val result = mutableListOf<RunningApp>() - result.addAll(apps.values) - result.sortWith { a: RunningApp, b: RunningApp -> - b.mTimeStarted.compareTo(a.mTimeStarted) - } - return result - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt deleted file mode 100644 index 28749296c4cf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 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.fgsmanager - -import android.content.Context -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.animation.DialogLaunchAnimator -import android.content.DialogInterface -import android.view.View -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor -import javax.inject.Inject - -/** - * Factory to create [FgsManagerDialog] instances - */ -@SysUISingleton -class FgsManagerDialogFactory -@Inject constructor( - private val context: Context, - @Main private val executor: Executor, - @Background private val backgroundExecutor: Executor, - private val systemClock: SystemClock, - private val dialogLaunchAnimator: DialogLaunchAnimator, - private val fgsManagerDialogController: FgsManagerDialogController -) { - - val lock = Any() - - companion object { - private var fgsManagerDialog: FgsManagerDialog? = null - } - - /** - * Creates the dialog if it doesn't exist - */ - fun create(viewLaunchedFrom: View?) { - if (fgsManagerDialog == null) { - fgsManagerDialog = FgsManagerDialog(context, executor, backgroundExecutor, - systemClock, fgsManagerDialogController) - fgsManagerDialog!!.setOnDismissListener { i: DialogInterface? -> - fgsManagerDialogController.onFinishDialog() - fgsManagerDialog = null - } - dialogLaunchAnimator.showFromView(fgsManagerDialog!!, viewLaunchedFrom!!) - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 86845091d99c..663877ce1388 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -21,6 +21,7 @@ import android.content.Context; import android.view.WindowManager; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.commandline.CommandRegistry; import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Named; @@ -99,13 +101,13 @@ public interface MediaModule { @SysUISingleton static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, + CommandQueue commandQueue, Context context, - WindowManager windowManager, - CommandQueue commandQueue) { + WindowManager windowManager) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerSender(context, windowManager, commandQueue)); + return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager)); } /** */ @@ -128,6 +130,7 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, + @Main Executor mainExecutor, MediaTttChipControllerReceiver mediaTttChipControllerReceiver) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); @@ -136,6 +139,7 @@ public interface MediaModule { new MediaTttCommandLineHelper( commandRegistry, context, + mainExecutor, mediaTttChipControllerReceiver)); } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt index bbcbfba5bbe7..1ea21ce7728b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -21,13 +21,15 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.Icon import android.media.MediaRoute2Info +import android.util.Log import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver -import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast -import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast +import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast +import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast import com.android.systemui.media.taptotransfer.sender.TransferFailed import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded @@ -36,6 +38,7 @@ import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceed import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter +import java.util.concurrent.Executor import javax.inject.Inject /** @@ -46,13 +49,36 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, private val context: Context, + @Main private val mainExecutor: Executor, private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver, ) { - private val appIconDrawable = + private val appIconDrawable = Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { it.setTint(Color.YELLOW) } + /** + * A map from a display state string typed in the command line to the display int it represents. + */ + private val stateStringToStateInt: Map<String, Int> = mapOf( + AlmostCloseToStartCast::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + AlmostCloseToEndCast::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST, + TransferToReceiverTriggered::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED, + TransferToThisDeviceTriggered::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED, + TransferToReceiverSucceeded::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + TransferToThisDeviceSucceeded::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, + TransferFailed::class.simpleName!! + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED, + FAR_FROM_RECEIVER_STATE + to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER + ) + init { commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() } commandRegistry.registerCommand( @@ -68,22 +94,59 @@ class MediaTttCommandLineHelper @Inject constructor( .addFeature("feature") .build() + @StatusBarManager.MediaTransferSenderState + val displayState = stateStringToStateInt[args[1]] + if (displayState == null) { + pw.println("Invalid command name") + return + } + val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) as StatusBarManager statusBarManager.updateMediaTapToTransferSenderDisplay( - StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + displayState, routeInfo, - /* undoExecutor= */ null, - /* undoCallback= */ null + getUndoExecutor(displayState), + getUndoCallback(displayState) ) - // TODO(b/216318437): Migrate the rest of the callbacks to StatusBarManager. + } + + private fun getUndoExecutor( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Executor? { + return if (isSucceededState(displayState)) { + mainExecutor + } else { + null + } + } + + private fun getUndoCallback( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Runnable? { + return if (isSucceededState(displayState)) { + Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") } + } else { + null + } + } + + private fun isSucceededState( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Boolean { + return displayState == + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED || + displayState == + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED } override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>") + pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>") } } + // TODO(b/216318437): Migrate the receiver callbacks to StatusBarManager. + /** A command to DISPLAY the media ttt chip on the RECEIVER device. */ inner class AddChipCommandReceiver : Command { override fun execute(pw: PrintWriter, args: List<String>) { @@ -110,29 +173,15 @@ class MediaTttCommandLineHelper @Inject constructor( @VisibleForTesting const val SENDER_COMMAND = "media-ttt-chip-sender" @VisibleForTesting -const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender" -@VisibleForTesting const val ADD_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-add-receiver" @VisibleForTesting const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver" @VisibleForTesting -val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!! -@VisibleForTesting -val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName!! -@VisibleForTesting -val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!! -@VisibleForTesting -val TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME = - TransferToThisDeviceTriggered::class.simpleName!! -@VisibleForTesting -val TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME = TransferToReceiverSucceeded::class.simpleName!! -@VisibleForTesting -val TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME = - TransferToThisDeviceSucceeded::class.simpleName!! -@VisibleForTesting -val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!! -@VisibleForTesting -val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver" +val FAR_FROM_RECEIVER_STATE = "FarFromReceiver" private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon" -private const val TAG = "MediaTapToTransferCli" +private const val CLI_TAG = "MediaTransferCli" + +private val routeInfo = MediaRoute2Info.Builder("id", "Test Name") + .addFeature("feature") + .build()
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt index 118a04ccdcd0..05baf7806bda 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt @@ -59,7 +59,7 @@ sealed class ChipStateSender( * * @property otherDeviceName the name of the other device involved in the transfer. */ -class MoveCloserToStartCast( +class AlmostCloseToStartCast( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, @@ -76,7 +76,7 @@ class MoveCloserToStartCast( * * @property otherDeviceName the name of the other device involved in the transfer. */ -class MoveCloserToEndCast( +class AlmostCloseToEndCast( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index c510e358e4a5..d1790d2fd5e1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -21,6 +21,7 @@ import android.content.Context import android.graphics.Color import android.graphics.drawable.Icon import android.media.MediaRoute2Info +import android.util.Log import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -38,9 +39,9 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipControllerSender @Inject constructor( + commandQueue: CommandQueue, context: Context, windowManager: WindowManager, - private val commandQueue: CommandQueue ) : MediaTttChipControllerCommon<ChipStateSender>( context, windowManager, R.layout.media_ttt_chip ) { @@ -50,25 +51,80 @@ class MediaTttChipControllerSender @Inject constructor( it.setTint(Color.YELLOW) } - private val commandQueueCallback = object : CommandQueue.Callbacks { + private val commandQueueCallbacks = object : CommandQueue.Callbacks { override fun updateMediaTapToTransferSenderDisplay( @StatusBarManager.MediaTransferSenderState displayState: Int, routeInfo: MediaRoute2Info, undoCallback: IUndoMediaTransferCallback? ) { - // TODO(b/216318437): Trigger displayChip with the right state based on displayState. - displayChip( - MoveCloserToStartCast( - // TODO(b/217418566): This app icon content description is incorrect -- - // routeInfo.name is the name of the device, not the name of the app. - fakeAppIconDrawable, routeInfo.name.toString(), routeInfo.name.toString() - ) + this@MediaTttChipControllerSender.updateMediaTapToTransferSenderDisplay( + displayState, routeInfo, undoCallback ) } } init { - commandQueue.addCallback(commandQueueCallback) + commandQueue.addCallback(commandQueueCallbacks) + } + + private fun updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState displayState: Int, + routeInfo: MediaRoute2Info, + undoCallback: IUndoMediaTransferCallback? + ) { + // TODO(b/217418566): This app icon content description is incorrect -- + // routeInfo.name is the name of the device, not the name of the app. + val appIconContentDescription = routeInfo.name.toString() + val otherDeviceName = routeInfo.name.toString() + val chipState = when(displayState) { + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST -> + AlmostCloseToStartCast( + fakeAppIconDrawable, appIconContentDescription, otherDeviceName + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST -> + AlmostCloseToEndCast( + fakeAppIconDrawable, appIconContentDescription, otherDeviceName + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED -> + TransferToReceiverTriggered( + fakeAppIconDrawable, appIconContentDescription, otherDeviceName + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED -> + TransferToThisDeviceTriggered( + fakeAppIconDrawable, appIconContentDescription + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED -> + TransferToReceiverSucceeded( + fakeAppIconDrawable, + appIconContentDescription, + otherDeviceName, + undoCallback + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED -> + TransferToThisDeviceSucceeded( + fakeAppIconDrawable, + appIconContentDescription, + otherDeviceName, + undoCallback + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED, + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED -> + TransferFailed( + fakeAppIconDrawable, appIconContentDescription + ) + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER -> { + removeChip() + null + } + else -> { + Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState") + null + } + } + + chipState?.let { + displayChip(it) + } } /** Displays the chip view for the given state. */ @@ -97,3 +153,5 @@ class MediaTttChipControllerSender @Inject constructor( if (showFailure) { View.VISIBLE } else { View.GONE } } } + +const val SENDER_TAG = "MediaTapToTransferSender" diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt new file mode 100644 index 000000000000..eb3415639db6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import android.app.IActivityManager +import android.app.IForegroundServiceObserver +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable +import android.os.IBinder +import android.os.PowerExemptionManager +import android.os.RemoteException +import android.provider.DeviceConfig.NAMESPACE_SYSTEMUI +import android.text.format.DateUtils +import android.util.ArrayMap +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.GuardedBy +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED +import com.android.systemui.R +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.DeviceConfigProxy +import com.android.systemui.util.time.SystemClock +import java.util.Objects +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlin.math.max + +class FgsManagerController @Inject constructor( + private val context: Context, + @Main private val mainExecutor: Executor, + @Background private val backgroundExecutor: Executor, + private val systemClock: SystemClock, + private val activityManager: IActivityManager, + private val packageManager: PackageManager, + private val deviceConfigProxy: DeviceConfigProxy, + private val dialogLaunchAnimator: DialogLaunchAnimator +) : IForegroundServiceObserver.Stub() { + + companion object { + private val LOG_TAG = FgsManagerController::class.java.simpleName + } + + private var isAvailable = false + + private val lock = Any() + + @GuardedBy("lock") + var initialized = false + + @GuardedBy("lock") + private val runningServiceTokens = mutableMapOf<UserPackage, StartTimeAndTokens>() + + @GuardedBy("lock") + private var dialog: SystemUIDialog? = null + + @GuardedBy("lock") + private val appListAdapter: AppListAdapter = AppListAdapter() + + @GuardedBy("lock") + private var runningApps: ArrayMap<UserPackage, RunningApp> = ArrayMap() + + interface OnNumberOfPackagesChangedListener { + fun onNumberOfPackagesChanged(numPackages: Int) + } + + interface OnDialogDismissedListener { + fun onDialogDismissed() + } + + fun init() { + synchronized(lock) { + if (initialized) { + return + } + try { + activityManager.registerForegroundServiceObserver(this) + } catch (e: RemoteException) { + e.rethrowFromSystemServer() + } + + deviceConfigProxy.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI, + backgroundExecutor) { + isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable) + } + + isAvailable = deviceConfigProxy + .getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, false) + + initialized = true + } + } + + override fun onForegroundStateChanged( + token: IBinder, + packageName: String, + userId: Int, + isForeground: Boolean + ) { + synchronized(lock) { + val numPackagesBefore = getNumRunningPackagesLocked() + val userPackageKey = UserPackage(userId, packageName) + if (isForeground) { + runningServiceTokens.getOrPut(userPackageKey, { StartTimeAndTokens(systemClock) }) + .addToken(token) + } else { + if (runningServiceTokens[userPackageKey]?.also { + it.removeToken(token) }?.isEmpty() == true) { + runningServiceTokens.remove(userPackageKey) + } + } + + val numPackagesAfter = getNumRunningPackagesLocked() + + if (numPackagesAfter != numPackagesBefore) { + onNumberOfPackagesChangedListeners.forEach { + backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) } + } + } + + updateAppItemsLocked() + } + } + + @GuardedBy("lock") + val onNumberOfPackagesChangedListeners: MutableSet<OnNumberOfPackagesChangedListener> = + mutableSetOf() + + @GuardedBy("lock") + val onDialogDismissedListeners: MutableSet<OnDialogDismissedListener> = mutableSetOf() + + fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) { + synchronized(lock) { + onNumberOfPackagesChangedListeners.add(listener) + } + } + + fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) { + synchronized(lock) { + onNumberOfPackagesChangedListeners.remove(listener) + } + } + + fun addOnDialogDismissedListener(listener: OnDialogDismissedListener) { + synchronized(lock) { + onDialogDismissedListeners.add(listener) + } + } + + fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) { + synchronized(lock) { + onDialogDismissedListeners.remove(listener) + } + } + + fun isAvailable(): Boolean { + return isAvailable + } + + fun getNumRunningPackages(): Int { + synchronized(lock) { + return getNumRunningPackagesLocked() + } + } + + private fun getNumRunningPackagesLocked() = + runningServiceTokens.keys.count { it.uiControl != UIControl.HIDE_ENTRY } + + fun shouldUpdateFooterVisibility() = dialog == null + + fun showDialog(viewLaunchedFrom: View?) { + synchronized(lock) { + if (dialog == null) { + + val dialog = SystemUIDialog(context) + dialog.setTitle(R.string.fgs_manager_dialog_title) + + val dialogContext = dialog.context + + val recyclerView = RecyclerView(dialogContext) + recyclerView.layoutManager = LinearLayoutManager(dialogContext) + recyclerView.adapter = appListAdapter + + dialog.setView(recyclerView) + + this.dialog = dialog + + dialog.setOnDismissListener { + synchronized(lock) { + this.dialog = null + updateAppItemsLocked() + } + onDialogDismissedListeners.forEach { + mainExecutor.execute(it::onDialogDismissed) + } + } + + mainExecutor.execute { + viewLaunchedFrom + ?.let { dialogLaunchAnimator.showFromView(dialog, it) } ?: dialog.show() + } + + backgroundExecutor.execute { + synchronized(lock) { + updateAppItemsLocked() + } + } + } + } + } + + @GuardedBy("lock") + private fun updateAppItemsLocked() { + if (dialog == null) { + runningApps.clear() + return + } + + val addedPackages = runningServiceTokens.keys.filter { + it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true + } + val removedPackages = runningApps.keys.filter { !runningServiceTokens.containsKey(it) } + + addedPackages.forEach { + val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId) + runningApps[it] = RunningApp(it.userId, it.packageName, + runningServiceTokens[it]!!.startTime, it.uiControl, + ai.loadLabel(packageManager), ai.loadIcon(packageManager)) + } + + removedPackages.forEach { pkg -> + val ra = runningApps[pkg]!! + val ra2 = ra.copy().also { + it.stopped = true + it.appLabel = ra.appLabel + it.icon = ra.icon + } + runningApps[pkg] = ra2 + } + + mainExecutor.execute { + appListAdapter + .setData(runningApps.values.toList().sortedByDescending { it.timeStarted }) + } + } + + private fun stopPackage(userId: Int, packageName: String) { + activityManager.stopAppForUser(packageName, userId) + } + + private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() { + private val lock = Any() + + @GuardedBy("lock") + private var data: List<RunningApp> = listOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder { + return AppItemViewHolder(LayoutInflater.from(parent.context) + .inflate(R.layout.fgs_manager_app_item, parent, false)) + } + + override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) { + var runningApp: RunningApp + synchronized(lock) { + runningApp = data[position] + } + with(holder) { + iconView.setImageDrawable(runningApp.icon) + appLabelView.text = runningApp.appLabel + durationView.text = DateUtils.formatDuration( + max(systemClock.elapsedRealtime() - runningApp.timeStarted, 60000), + DateUtils.LENGTH_MEDIUM) + stopButton.setOnClickListener { + stopButton.setText(R.string.fgs_manager_app_item_stop_button_stopped_label) + stopPackage(runningApp.userId, runningApp.packageName) + } + if (runningApp.uiControl == UIControl.HIDE_BUTTON) { + stopButton.visibility = View.INVISIBLE + } + if (runningApp.stopped) { + stopButton.isEnabled = false + stopButton.setText(R.string.fgs_manager_app_item_stop_button_stopped_label) + durationView.visibility = View.GONE + } else { + stopButton.isEnabled = true + stopButton.setText(R.string.fgs_manager_app_item_stop_button_label) + durationView.visibility = View.VISIBLE + } + } + } + + override fun getItemCount(): Int { + return data.size + } + + fun setData(newData: List<RunningApp>) { + var oldData = data + data = newData + + DiffUtil.calculateDiff(object : DiffUtil.Callback() { + override fun getOldListSize(): Int { + return oldData.size + } + + override fun getNewListSize(): Int { + return newData.size + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): + Boolean { + return oldData[oldItemPosition] == newData[newItemPosition] + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): + Boolean { + return oldData[oldItemPosition].stopped == newData[newItemPosition].stopped + } + }).dispatchUpdatesTo(this) + } + } + + private inner class UserPackage( + val userId: Int, + val packageName: String + ) { + val uiControl: UIControl by lazy { + val uid = packageManager.getPackageUidAsUser(packageName, userId) + + when (activityManager.getBackgroundRestrictionExemptionReason(uid)) { + PowerExemptionManager.REASON_SYSTEM_UID, + PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY + + PowerExemptionManager.REASON_DEVICE_OWNER, + PowerExemptionManager.REASON_PROFILE_OWNER, + PowerExemptionManager.REASON_PROC_STATE_PERSISTENT, + PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI, + PowerExemptionManager.REASON_ROLE_DIALER, + PowerExemptionManager.REASON_SYSTEM_MODULE -> UIControl.HIDE_BUTTON + else -> UIControl.NORMAL + } + } + + override fun equals(other: Any?): Boolean { + if (other !is UserPackage) { + return false + } + return other.packageName == packageName && other.userId == userId + } + + override fun hashCode(): Int = Objects.hash(userId, packageName) + } + + private data class StartTimeAndTokens( + val systemClock: SystemClock + ) { + val startTime = systemClock.elapsedRealtime() + val tokens = mutableSetOf<IBinder>() + + fun addToken(token: IBinder) { + tokens.add(token) + } + + fun removeToken(token: IBinder) { + tokens.remove(token) + } + + fun isEmpty(): Boolean { + return tokens.isEmpty() + } + } + + private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) { + val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label) + val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration) + val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon) + val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button) + } + + private data class RunningApp( + val userId: Int, + val packageName: String, + val timeStarted: Long, + val uiControl: UIControl + ) { + constructor( + userId: Int, + packageName: String, + timeStarted: Long, + uiControl: UIControl, + appLabel: CharSequence, + icon: Drawable + ) : this(userId, packageName, timeStarted, uiControl) { + this.appLabel = appLabel + this.icon = icon + } + + // variables to keep out of the generated equals() + var appLabel: CharSequence = "" + var icon: Drawable? = null + var stopped = false + } + + private enum class UIControl { + NORMAL, HIDE_BUTTON, HIDE_ENTRY + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java index 082de1609423..55d4a53ced7b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java @@ -16,13 +16,9 @@ package com.android.systemui.qs; -import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; - -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW; import android.content.Context; -import android.provider.DeviceConfig; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -30,8 +26,6 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.fgsmanager.FgsManagerDialogFactory; -import com.android.systemui.statusbar.policy.RunningFgsController; import java.util.concurrent.Executor; @@ -41,24 +35,25 @@ import javax.inject.Named; /** * Footer entry point for the foreground service manager */ -public class QSFgsManagerFooter implements View.OnClickListener { +public class QSFgsManagerFooter implements View.OnClickListener, + FgsManagerController.OnDialogDismissedListener, + FgsManagerController.OnNumberOfPackagesChangedListener { private final View mRootView; private final TextView mFooterText; private final Context mContext; private final Executor mMainExecutor; private final Executor mExecutor; - private final RunningFgsController mRunningFgsController; - private final FgsManagerDialogFactory mFgsManagerDialogFactory; + + private final FgsManagerController mFgsManagerController; private boolean mIsInitialized = false; - private boolean mIsAvailable = false; + private int mNumPackages; @Inject QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView, - @Main Executor mainExecutor, RunningFgsController runningFgsController, - @Background Executor executor, - FgsManagerDialogFactory fgsManagerDialogFactory) { + @Main Executor mainExecutor, @Background Executor executor, + FgsManagerController fgsManagerController) { mRootView = rootView; mFooterText = mRootView.findViewById(R.id.footer_text); ImageView icon = mRootView.findViewById(R.id.primary_footer_icon); @@ -66,8 +61,7 @@ public class QSFgsManagerFooter implements View.OnClickListener { mContext = rootView.getContext(); mMainExecutor = mainExecutor; mExecutor = executor; - mRunningFgsController = runningFgsController; - mFgsManagerDialogFactory = fgsManagerDialogFactory; + mFgsManagerController = fgsManagerController; } public void init() { @@ -75,22 +69,28 @@ public class QSFgsManagerFooter implements View.OnClickListener { return; } - mRootView.setOnClickListener(this); - - mRunningFgsController.addCallback(packages -> refreshState()); + mFgsManagerController.init(); - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI, mExecutor, - (DeviceConfig.OnPropertiesChangedListener) properties -> { - mIsAvailable = properties.getBoolean(TASK_MANAGER_ENABLED, mIsAvailable); - }); - mIsAvailable = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, false); + mRootView.setOnClickListener(this); mIsInitialized = true; } + public void setListening(boolean listening) { + if (listening) { + mFgsManagerController.addOnDialogDismissedListener(this); + mFgsManagerController.addOnNumberOfPackagesChangedListener(this); + mNumPackages = mFgsManagerController.getNumRunningPackages(); + refreshState(); + } else { + mFgsManagerController.removeOnDialogDismissedListener(this); + mFgsManagerController.removeOnNumberOfPackagesChangedListener(this); + } + } + @Override public void onClick(View view) { - mFgsManagerDialogFactory.create(mRootView); + mFgsManagerController.showDialog(mRootView); } public void refreshState() { @@ -101,17 +101,25 @@ public class QSFgsManagerFooter implements View.OnClickListener { return mRootView; } - private boolean isAvailable() { - return mIsAvailable; - } - public void handleRefreshState() { - int numPackages = mRunningFgsController.getPackagesWithFgs().size(); mMainExecutor.execute(() -> { mFooterText.setText(mContext.getResources().getQuantityString( - R.plurals.fgs_manager_footer_label, numPackages, numPackages)); - mRootView.setVisibility(numPackages > 0 && isAvailable() ? View.VISIBLE : View.GONE); + R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages)); + if (mFgsManagerController.shouldUpdateFooterVisibility()) { + mRootView.setVisibility(mNumPackages > 0 + && mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE); + } }); } + @Override + public void onDialogDismissed() { + refreshState(); + } + + @Override + public void onNumberOfPackagesChanged(int numPackages) { + mNumPackages = numPackages; + refreshState(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index cbfe944a2c23..8f268b5cffe4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -192,6 +192,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { refreshAllTiles(); } + mQSFgsManagerFooter.setListening(listening); mQsSecurityFooter.setListening(listening); // Set the listening as soon as the QS fragment starts listening regardless of the diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index 48255b5360fc..6d1bbeed5372 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -34,8 +34,6 @@ import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DeviceControlsController; import com.android.systemui.statusbar.policy.HotspotController; -import com.android.systemui.statusbar.policy.RunningFgsController; -import com.android.systemui.statusbar.policy.RunningFgsControllerImpl; import com.android.systemui.statusbar.policy.WalletController; import com.android.systemui.util.settings.SecureSettings; @@ -91,9 +89,4 @@ public interface QSModule { /** */ @Binds QSHost provideQsHost(QSTileHost controllerImpl); - - /** */ - @Binds - RunningFgsController provideRunningFgsController( - RunningFgsControllerImpl runningFgsController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 31d9f09d1569..b328ae8cd0bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -226,8 +226,13 @@ public class ShadeListBuilder implements Dumpable { mNotifSections.clear(); for (NotifSectioner sectioner : sectioners) { - mNotifSections.add(new NotifSection(sectioner, mNotifSections.size())); + final NotifSection section = new NotifSection(sectioner, mNotifSections.size()); + final NotifComparator sectionComparator = section.getComparator(); + mNotifSections.add(section); sectioner.setInvalidationListener(this::onNotifSectionInvalidated); + if (sectionComparator != null) { + sectionComparator.setInvalidationListener(this::onNotifComparatorInvalidated); + } } mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); @@ -1098,7 +1103,12 @@ public class ShadeListBuilder implements Dumpable { callOnCleanup(mNotifComparators); for (int i = 0; i < mNotifSections.size(); i++) { - mNotifSections.get(i).getSectioner().onCleanup(); + final NotifSection notifSection = mNotifSections.get(i); + notifSection.getSectioner().onCleanup(); + final NotifComparator comparator = notifSection.getComparator(); + if (comparator != null) { + comparator.onCleanup(); + } } callOnCleanup(List.of(getStabilityManager())); @@ -1111,6 +1121,19 @@ public class ShadeListBuilder implements Dumpable { } } + @Nullable + private NotifComparator getSectionComparator( + @NonNull ListEntry o1, @NonNull ListEntry o2) { + final NotifSection section = o1.getSection(); + if (section != o2.getSection()) { + throw new RuntimeException("Entry ordering should only be done within sections"); + } + if (section != null) { + return section.getComparator(); + } + return null; + } + private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> { int cmp = Integer.compare( o1.getSectionIndex(), @@ -1122,6 +1145,12 @@ public class ShadeListBuilder implements Dumpable { cmp = Integer.compare(index1, index2); if (cmp != 0) return cmp; + NotifComparator sectionComparator = getSectionComparator(o1, o2); + if (sectionComparator != null) { + cmp = sectionComparator.compare(o1, o2); + if (cmp != 0) return cmp; + } + for (int i = 0; i < mNotifComparators.size(); i++) { cmp = mNotifComparators.get(i).compare(o1, o2); if (cmp != 0) return cmp; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index ba88ad7844f1..a390e9f9b09d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -51,21 +51,18 @@ class ConversationCoordinator @Inject constructor( val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean = isConversation(entry) - override fun getHeaderNodeController() = - // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController - if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null - } - val comparator = object : NotifComparator("People") { - override fun compare(entry1: ListEntry, entry2: ListEntry): Int { - assert(entry1.section === entry2.section) - if (entry1.section?.sectioner !== sectioner) { - return 0 + override fun getComparator() = object : NotifComparator("People") { + override fun compare(entry1: ListEntry, entry2: ListEntry): Int { + val type1 = getPeopleType(entry1) + val type2 = getPeopleType(entry2) + return type2.compareTo(type1) } - val type1 = getPeopleType(entry1) - val type2 = getPeopleType(entry2) - return type2.compareTo(type1) } + + override fun getHeaderNodeController() = + // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController + if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null } override fun attach(pipeline: NotifPipeline) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 031132424115..41b070635d4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener @@ -456,6 +457,13 @@ class HeadsUpCoordinator @Inject constructor( // TODO: This check won't notice if a child of the group is going to HUN... isGoingToShowHunNoRetract(entry) + override fun getComparator(): NotifComparator { + return object : NotifComparator("HeadsUp") { + override fun compare(o1: ListEntry, o2: ListEntry): Int = + mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry) + } + } + override fun getHeaderNodeController(): NodeController? = // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 850cb4b88154..757fb5a2fe9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -20,7 +20,6 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import java.io.FileDescriptor import java.io.PrintWriter @@ -64,7 +63,6 @@ class NotifCoordinatorsImpl @Inject constructor( private val mCoordinators: MutableList<Coordinator> = ArrayList() private val mOrderedSections: MutableList<NotifSectioner> = ArrayList() - private val mOrderedComparators: MutableList<NotifComparator> = ArrayList() /** * Creates all the coordinators. @@ -119,9 +117,6 @@ class NotifCoordinatorsImpl @Inject constructor( mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized - - // Manually add ordered comparators - mOrderedComparators.add(conversationCoordinator.comparator) } /** @@ -133,7 +128,6 @@ class NotifCoordinatorsImpl @Inject constructor( c.attach(pipeline) } pipeline.setSections(mOrderedSections) - pipeline.setComparators(mOrderedComparators) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt index 8444287cbf60..263737e20a13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.listbuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.stack.PriorityBucket @@ -29,5 +30,7 @@ data class NotifSection( val headerController: NodeController? = sectioner.headerNodeController + val comparator: NotifComparator? = sectioner.comparator + @PriorityBucket val bucket: Int = sectioner.bucket } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java index ef9ee11ef116..8c52c53ea6b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java @@ -55,8 +55,22 @@ public abstract class NotifSectioner extends Pluggable<NotifSectioner> { public abstract boolean isInSection(ListEntry entry); /** + * Returns an optional {@link NotifComparator} to sort entries only in this section. + * {@link ListEntry} instances passed to this comparator are guaranteed to have this section, + * and this ordering will take precedence over any global comparators supplied to {@link + * com.android.systemui.statusbar.notification.collection.NotifPipeline#setComparators(List)}. + * + * NOTE: this method is only called once when the Sectioner is attached. + */ + public @Nullable NotifComparator getComparator() { + return null; + } + + /** * Returns an optional {@link NodeSpec} for the section header. If {@code null}, no header will * be used for the section. + * + * NOTE: this method is only called once when the Sectioner is attached. */ public @Nullable NodeController getHeaderNodeController() { return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 3084a957aeb5..784a54681484 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -350,11 +350,14 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * @return -1 if the first argument should be ranked higher than the second, 1 if the second * one should be ranked higher and 0 if they are equal. */ - public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) { + public int compare(@Nullable NotificationEntry a, @Nullable NotificationEntry b) { + if (a == null || b == null) { + return Boolean.compare(a == null, b == null); + } AlertEntry aEntry = getHeadsUpEntry(a.getKey()); AlertEntry bEntry = getHeadsUpEntry(b.getKey()); if (aEntry == null || bEntry == null) { - return aEntry == null ? 1 : -1; + return Boolean.compare(aEntry == null, bEntry == null); } return aEntry.compareTo(bEntry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt deleted file mode 100644 index c6dbdb1b09a5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.policy - -/** - * Interface for tracking packages with running foreground services and demoting foreground status - */ -interface RunningFgsController : CallbackController<RunningFgsController.Callback> { - - /** - * @return A list of [UserPackageTime]s which have running foreground service(s) - */ - fun getPackagesWithFgs(): List<UserPackageTime> - - /** - * Stops all foreground services running as a package - * @param userId the userId the package is running under - * @param packageName the packageName - */ - fun stopFgs(userId: Int, packageName: String) - - /** - * Returns when the list of packages with foreground services changes - */ - interface Callback { - /** - * The thing that - * @param packages the list of packages - */ - fun onFgsPackagesChanged(packages: List<UserPackageTime>) - } - - /** - * A triplet <user, packageName, timeMillis> where each element is a package running - * under a user that has had at least one foreground service running since timeMillis. - * Time should be derived from [SystemClock.elapsedRealtime]. - */ - data class UserPackageTime(val userId: Int, val packageName: String, val startTimeMillis: Long) -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt deleted file mode 100644 index 6d3345df4f3e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.policy - -import android.app.IActivityManager -import android.app.IForegroundServiceObserver -import android.os.IBinder -import android.os.RemoteException -import android.util.Log -import androidx.annotation.GuardedBy -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.policy.RunningFgsController.Callback -import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor -import javax.inject.Inject - -/** - * Implementation for [RunningFgsController] - */ -@SysUISingleton -class RunningFgsControllerImpl @Inject constructor( - @Background private val executor: Executor, - private val systemClock: SystemClock, - private val activityManager: IActivityManager -) : RunningFgsController, IForegroundServiceObserver.Stub() { - - companion object { - private val LOG_TAG = RunningFgsControllerImpl::class.java.simpleName - } - - private val lock = Any() - - @GuardedBy("lock") - var initialized = false - - @GuardedBy("lock") - private val runningServiceTokens = mutableMapOf<UserPackageKey, StartTimeAndTokensValue>() - - @GuardedBy("lock") - private val callbacks = mutableSetOf<Callback>() - - fun init() { - synchronized(lock) { - if (initialized) { - return - } - try { - activityManager.registerForegroundServiceObserver(this) - } catch (e: RemoteException) { - e.rethrowFromSystemServer() - } - - initialized = true - } - } - - override fun addCallback(listener: Callback) { - init() - synchronized(lock) { callbacks.add(listener) } - } - - override fun removeCallback(listener: Callback) { - init() - synchronized(lock) { - if (!callbacks.remove(listener)) { - Log.e(LOG_TAG, "Callback was not registered.", RuntimeException()) - } - } - } - - override fun observe(lifecycle: Lifecycle?, listener: Callback?): Callback { - init() - return super.observe(lifecycle, listener) - } - - override fun observe(owner: LifecycleOwner?, listener: Callback?): Callback { - init() - return super.observe(owner, listener) - } - - override fun getPackagesWithFgs(): List<UserPackageTime> { - init() - return synchronized(lock) { getPackagesWithFgsLocked() } - } - - private fun getPackagesWithFgsLocked(): List<UserPackageTime> = - runningServiceTokens.map { - UserPackageTime(it.key.userId, it.key.packageName, it.value.fgsStartTime) - } - - override fun stopFgs(userId: Int, packageName: String) { - init() - try { - activityManager.stopAppForUser(packageName, userId) - } catch (e: RemoteException) { - e.rethrowFromSystemServer() - } - } - - private data class UserPackageKey( - val userId: Int, - val packageName: String - ) - - private class StartTimeAndTokensValue(systemClock: SystemClock) { - val fgsStartTime = systemClock.elapsedRealtime() - var tokens = mutableSetOf<IBinder>() - fun addToken(token: IBinder): Boolean { - return tokens.add(token) - } - - fun removeToken(token: IBinder): Boolean { - return tokens.remove(token) - } - - val isEmpty: Boolean - get() = tokens.isEmpty() - } - - override fun onForegroundStateChanged( - token: IBinder, - packageName: String, - userId: Int, - isForeground: Boolean - ) { - val result = synchronized(lock) { - val userPackageKey = UserPackageKey(userId, packageName) - if (isForeground) { - var addedNew = false - runningServiceTokens.getOrPut(userPackageKey) { - addedNew = true - StartTimeAndTokensValue(systemClock) - }.addToken(token) - if (!addedNew) { - return - } - } else { - val startTimeAndTokensValue = runningServiceTokens[userPackageKey] - if (startTimeAndTokensValue?.removeToken(token) == false) { - Log.e(LOG_TAG, - "Stopped foreground service was not known to be running.") - return - } - if (!startTimeAndTokensValue!!.isEmpty) { - return - } - runningServiceTokens.remove(userPackageKey) - } - getPackagesWithFgsLocked().toList() - } - - callbacks.forEach { executor.execute { it.onFgsPackagesChanged(result) } } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt index b951345a145b..61e78f5cb2fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt @@ -43,7 +43,7 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { @Before fun setUp() { dialogLaunchAnimator = DialogLaunchAnimator( - dreamManager, launchAnimator, isForTesting = true) + dreamManager, launchAnimator, forceDisableSynchronization = true) } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt index 5f800eb80ec2..c3d8d245baed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt @@ -16,24 +16,35 @@ package com.android.systemui.media.taptotransfer -import android.content.ComponentName +import android.app.StatusBarManager +import android.content.Context +import android.media.MediaRoute2Info import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver +import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast +import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast +import com.android.systemui.media.taptotransfer.sender.TransferFailed +import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered +import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded +import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered +import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Ignore import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.anyString import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import java.io.PrintWriter import java.io.StringWriter @@ -50,15 +61,19 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper @Mock + private lateinit var statusBarManager: StatusBarManager + @Mock private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver @Before fun setUp() { MockitoAnnotations.initMocks(this) + context.addMockSystemService(Context.STATUS_BAR_SERVICE, statusBarManager) mediaTttCommandLineHelper = MediaTttCommandLineHelper( commandRegistry, context, + FakeExecutor(FakeSystemClock()), mediaTttChipControllerReceiver, ) } @@ -88,91 +103,116 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { ) { EmptyCommand() } } - /* TODO(b/216318437): Revive these tests using the new SystemApis. @Test - fun sender_moveCloserToStartCast_serviceCallbackCalled() { - commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand()) - - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() + fun sender_almostCloseToStartCast_serviceCallbackCalled() { + commandRegistry.onShellCommand( + pw, getSenderCommand(AlmostCloseToStartCast::class.simpleName!!) + ) - val deviceInfoCaptor = argumentCaptor<DeviceInfo>() - verify(mediaSenderService).closeToReceiverToStartCast(any(), capture(deviceInfoCaptor)) - assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) + val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST), + capture(routeInfoCaptor), + nullable(), + nullable()) + assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) } @Test - fun sender_moveCloserToEndCast_serviceCallbackCalled() { - commandRegistry.onShellCommand(pw, getMoveCloserToEndCastCommand()) - - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() + fun sender_almostCloseToEndCast_serviceCallbackCalled() { + commandRegistry.onShellCommand( + pw, getSenderCommand(AlmostCloseToEndCast::class.simpleName!!) + ) - val deviceInfoCaptor = argumentCaptor<DeviceInfo>() - verify(mediaSenderService).closeToReceiverToEndCast(any(), capture(deviceInfoCaptor)) - assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) + val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST), + capture(routeInfoCaptor), + nullable(), + nullable()) + assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) } @Test fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() { - commandRegistry.onShellCommand(pw, getTransferToReceiverTriggeredCommand()) - - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() + commandRegistry.onShellCommand( + pw, getSenderCommand(TransferToReceiverTriggered::class.simpleName!!) + ) - val deviceInfoCaptor = argumentCaptor<DeviceInfo>() - verify(mediaSenderService).transferToReceiverTriggered(any(), capture(deviceInfoCaptor)) - assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) + val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED), + capture(routeInfoCaptor), + nullable(), + nullable()) + assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) } @Test fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() { - commandRegistry.onShellCommand(pw, getTransferToThisDeviceTriggeredCommand()) + commandRegistry.onShellCommand( + pw, getSenderCommand(TransferToThisDeviceTriggered::class.simpleName!!) + ) - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() - verify(mediaSenderService).transferToThisDeviceTriggered(any(), any()) + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED), + any(), + nullable(), + nullable()) } @Test fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() { - commandRegistry.onShellCommand(pw, getTransferToReceiverSucceededCommand()) - - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() + commandRegistry.onShellCommand( + pw, getSenderCommand(TransferToReceiverSucceeded::class.simpleName!!) + ) - val deviceInfoCaptor = argumentCaptor<DeviceInfo>() - verify(mediaSenderService) - .transferToReceiverSucceeded(any(), capture(deviceInfoCaptor), any()) - assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) + val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED), + capture(routeInfoCaptor), + nullable(), + nullable()) + assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) } @Test fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() { - commandRegistry.onShellCommand(pw, getTransferToThisDeviceSucceededCommand()) - - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() + commandRegistry.onShellCommand( + pw, getSenderCommand(TransferToThisDeviceSucceeded::class.simpleName!!) + ) - val deviceInfoCaptor = argumentCaptor<DeviceInfo>() - verify(mediaSenderService) - .transferToThisDeviceSucceeded(any(), capture(deviceInfoCaptor), any()) - assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) + val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED), + capture(routeInfoCaptor), + nullable(), + nullable()) + assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME) } @Test fun sender_transferFailed_serviceCallbackCalled() { - commandRegistry.onShellCommand(pw, getTransferFailedCommand()) + commandRegistry.onShellCommand(pw, getSenderCommand(TransferFailed::class.simpleName!!)) - assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue() - verify(mediaSenderService).transferFailed(any(), any()) + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED), + any(), + nullable(), + nullable()) } @Test - fun sender_noLongerCloseToReceiver_serviceCallbackCalledAndServiceUnbound() { - commandRegistry.onShellCommand(pw, getNoLongerCloseToReceiverCommand()) - - // Once we're no longer close to the receiver, we should unbind the service. - assertThat(context.isBound(mediaSenderServiceComponentName)).isFalse() - verify(mediaSenderService).noLongerCloseToReceiver(any(), any()) + fun sender_farFromReceiver_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getSenderCommand(FAR_FROM_RECEIVER_STATE)) + + verify(statusBarManager).updateMediaTapToTransferSenderDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER), + any(), + nullable(), + nullable()) } - */ - @Test fun receiver_addCommand_chipAdded() { commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG)) @@ -187,61 +227,8 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { verify(mediaTttChipControllerReceiver).removeChip() } - private fun getMoveCloserToStartCastCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - MOVE_CLOSER_TO_START_CAST_COMMAND_NAME - ) - - private fun getMoveCloserToEndCastCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - MOVE_CLOSER_TO_END_CAST_COMMAND_NAME - ) - - private fun getTransferToReceiverTriggeredCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME - ) - - private fun getTransferToThisDeviceTriggeredCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME - ) - - private fun getTransferToReceiverSucceededCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME - ) - - private fun getTransferToThisDeviceSucceededCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME - ) - - private fun getTransferFailedCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_FAILED_COMMAND_NAME - ) - - private fun getNoLongerCloseToReceiverCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME - ) + private fun getSenderCommand(displayState: String): Array<String> = + arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState) class EmptyCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index 58f4818b3de5..c74ac64656ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -16,8 +16,10 @@ package com.android.systemui.media.taptotransfer.sender +import android.app.StatusBarManager import android.graphics.drawable.Drawable import android.graphics.drawable.Icon +import android.media.MediaRoute2Info import android.view.View import android.view.WindowManager import android.widget.ImageView @@ -35,6 +37,7 @@ import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -49,17 +52,148 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { private lateinit var windowManager: WindowManager @Mock private lateinit var commandQueue: CommandQueue + private lateinit var commandQueueCallback: CommandQueue.Callbacks @Before fun setUp() { MockitoAnnotations.initMocks(this) appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) - controllerSender = MediaTttChipControllerSender(context, windowManager, commandQueue) + controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager) + + val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) + verify(commandQueue).addCallback(callbackCaptor.capture()) + commandQueueCallback = callbackCaptor.value!! + } + + @Test + fun commandQueueCallback_almostCloseToStartCast_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(almostCloseToStartCast().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_almostCloseToEndCast_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(almostCloseToEndCast().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_transferToReceiverTriggered_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferToReceiverTriggered().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_transferToThisDeviceTriggered_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferToThisDeviceTriggered().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_transferToReceiverSucceeded_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferToReceiverSucceeded().getChipTextString(context)) } @Test - fun moveCloserToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { - val state = moveCloserToStartCast() + fun commandQueueCallback_transferToThisDeviceSucceeded_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferToThisDeviceSucceeded().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_transferToReceiverFailed_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferFailed().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_transferToThisDeviceFailed_triggersCorrectChip() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED, + routeInfo, + null + ) + + assertThat(getChipView().getChipText()) + .isEqualTo(transferFailed().getChipTextString(context)) + } + + @Test + fun commandQueueCallback_farFromReceiver_noChipShown() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER, + routeInfo, + null + ) + + verify(windowManager, never()).addView(any(), any()) + } + + @Test + fun commandQueueCallback_almostCloseThenFarFromReceiver_chipShownThenHidden() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + routeInfo, + null + ) + + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER, + routeInfo, + null + ) + + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).removeView(viewCaptor.value) + } + + @Test + fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { + val state = almostCloseToStartCast() controllerSender.displayChip(state) val chipView = getChipView() @@ -72,8 +206,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test - fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { - val state = moveCloserToEndCast() + fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() { + val state = almostCloseToEndCast() controllerSender.displayChip(state) val chipView = getChipView() @@ -250,8 +384,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test - fun changeFromCloserToStartToTransferTriggered_loadingIconAppears() { - controllerSender.displayChip(moveCloserToStartCast()) + fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() { + controllerSender.displayChip(almostCloseToStartCast()) controllerSender.displayChip(transferToReceiverTriggered()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE) @@ -280,9 +414,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test - fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() { + fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() { controllerSender.displayChip(transferToReceiverSucceeded()) - controllerSender.displayChip(moveCloserToStartCast()) + controllerSender.displayChip(almostCloseToStartCast()) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE) } @@ -314,12 +448,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } /** Helper method providing default parameters to not clutter up the tests. */ - private fun moveCloserToStartCast() = - MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME) + private fun almostCloseToStartCast() = + AlmostCloseToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ - private fun moveCloserToEndCast() = - MoveCloserToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME) + private fun almostCloseToEndCast() = + AlmostCloseToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferToReceiverTriggered() = @@ -347,3 +481,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { private const val DEVICE_NAME = "My Tablet" private const val APP_ICON_CONTENT_DESC = "Content description" + +private val routeInfo = MediaRoute2Info.Builder("id", "Test Name") + .addFeature("feature") + .build() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java deleted file mode 100644 index 6059afe8a70b..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2021 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.IActivityManager; -import android.app.IForegroundServiceObserver; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.testing.AndroidTestingRunner; -import android.util.Pair; - -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.test.filters.MediumTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.RunningFgsController; -import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime; -import com.android.systemui.statusbar.policy.RunningFgsControllerImpl; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Random; -import java.util.function.Consumer; - -@MediumTest -@RunWith(AndroidTestingRunner.class) -public class RunningFgsControllerTest extends SysuiTestCase { - - private RunningFgsController mController; - - private FakeSystemClock mSystemClock = new FakeSystemClock(); - private FakeExecutor mExecutor = new FakeExecutor(mSystemClock); - private TestCallback mCallback = new TestCallback(); - - @Mock - private IActivityManager mActivityManager; - @Mock - private Lifecycle mLifecycle; - @Mock - private LifecycleOwner mLifecycleOwner; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle); - mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager); - } - - @Test - public void testInitRegistersListenerInImpl() throws RemoteException { - ((RunningFgsControllerImpl) mController).init(); - verify(mActivityManager, times(1)).registerForegroundServiceObserver(any()); - } - - @Test - public void testAddCallbackCallsInitInImpl() { - verifyInitIsCalled(controller -> controller.addCallback(mCallback)); - } - - @Test - public void testRemoveCallbackCallsInitInImpl() { - verifyInitIsCalled(controller -> controller.removeCallback(mCallback)); - } - - @Test - public void testObserve1CallsInitInImpl() { - verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback)); - } - - @Test - public void testObserve2CallsInitInImpl() { - verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback)); - } - - @Test - public void testGetPackagesWithFgsCallsInitInImpl() { - verifyInitIsCalled(controller -> controller.getPackagesWithFgs()); - } - - @Test - public void testStopFgsCallsInitInImpl() { - verifyInitIsCalled(controller -> controller.stopFgs(0, "")); - } - - /** - * Tests that callbacks can be added - */ - @Test - public void testAddCallback() throws RemoteException { - String testPackageName = "testPackageName"; - int testUserId = 0; - - IForegroundServiceObserver observer = prepareObserver(); - mController.addCallback(mCallback); - - observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); - - mExecutor.advanceClockToLast(); - mExecutor.runAllReady(); - - assertEquals("Callback should have been invoked exactly once.", - 1, mCallback.mInvocations.size()); - - List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0); - assertEquals("There should have only been one package in callback. packages=" - + userPackageTimes, - 1, userPackageTimes.size()); - - UserPackageTime upt = userPackageTimes.get(0); - assertEquals(testPackageName, upt.getPackageName()); - assertEquals(testUserId, upt.getUserId()); - } - - /** - * Tests that callbacks can be removed. This test is only meaningful if - * {@link #testAddCallback()} can pass. - */ - @Test - public void testRemoveCallback() throws RemoteException { - String testPackageName = "testPackageName"; - int testUserId = 0; - - IForegroundServiceObserver observer = prepareObserver(); - mController.addCallback(mCallback); - mController.removeCallback(mCallback); - - observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); - - mExecutor.advanceClockToLast(); - mExecutor.runAllReady(); - - assertEquals("Callback should not have been invoked.", - 0, mCallback.mInvocations.size()); - } - - /** - * Tests packages are added when the controller receives a callback from activity manager for - * a foreground service start. - */ - @Test - public void testGetPackagesWithFgsAddingPackages() throws RemoteException { - int numPackages = 20; - int numUsers = 3; - - IForegroundServiceObserver observer = prepareObserver(); - - assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size()); - - List<Pair<Integer, String>> addedPackages = new ArrayList<>(); - for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { - for (int userId = 0; userId < numUsers; userId++) { - String packageName = "package.name." + pkgNumber; - addedPackages.add(new Pair(userId, packageName)); - - observer.onForegroundStateChanged(new Binder(), packageName, userId, true); - - containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs()); - } - } - } - - /** - * Tests packages are removed when the controller receives a callback from activity manager for - * a foreground service ending. - */ - @Test - public void testGetPackagesWithFgsRemovingPackages() throws RemoteException { - int numPackages = 20; - int numUsers = 3; - int arrayLength = numPackages * numUsers; - - String[] packages = new String[arrayLength]; - int[] users = new int[arrayLength]; - IBinder[] tokens = new IBinder[arrayLength]; - for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { - for (int userId = 0; userId < numUsers; userId++) { - int i = pkgNumber * numUsers + userId; - packages[i] = "package.name." + pkgNumber; - users[i] = userId; - tokens[i] = new Binder(); - } - } - - IForegroundServiceObserver observer = prepareObserver(); - - for (int i = 0; i < packages.length; i++) { - observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true); - } - - assertEquals(packages.length, mController.getPackagesWithFgs().size()); - - List<Integer> removeOrder = new ArrayList<>(); - for (int i = 0; i < packages.length; i++) { - removeOrder.add(i); - } - Collections.shuffle(removeOrder, new Random(12345)); - - for (int idx : removeOrder) { - removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]); - } - - assertEquals(0, mController.getPackagesWithFgs().size()); - } - - /** - * Tests a call on stopFgs forwards to activity manager. - */ - @Test - public void testStopFgs() throws RemoteException { - String pkgName = "package.name"; - mController.stopFgs(0, pkgName); - verify(mActivityManager).stopAppForUser(pkgName, 0); - } - - /** - * Tests a package which starts multiple services is only listed once and is only removed once - * all services are stopped. - */ - @Test - public void testSinglePackageWithMultipleServices() throws RemoteException { - String packageName = "package.name"; - int userId = 0; - IBinder serviceToken1 = new Binder(); - IBinder serviceToken2 = new Binder(); - - IForegroundServiceObserver observer = prepareObserver(); - - assertEquals(0, mController.getPackagesWithFgs().size()); - - observer.onForegroundStateChanged(serviceToken1, packageName, userId, true); - assertSinglePackage(packageName, userId); - - observer.onForegroundStateChanged(serviceToken2, packageName, userId, true); - assertSinglePackage(packageName, userId); - - observer.onForegroundStateChanged(serviceToken2, packageName, userId, false); - assertSinglePackage(packageName, userId); - - observer.onForegroundStateChanged(serviceToken1, packageName, userId, false); - assertEquals(0, mController.getPackagesWithFgs().size()); - } - - private IForegroundServiceObserver prepareObserver() - throws RemoteException { - mController.getPackagesWithFgs(); - - ArgumentCaptor<IForegroundServiceObserver> argumentCaptor = - ArgumentCaptor.forClass(IForegroundServiceObserver.class); - verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture()); - - return argumentCaptor.getValue(); - } - - private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) { - RunningFgsControllerImpl spiedController = Mockito.spy( - ((RunningFgsControllerImpl) mController)); - c.accept(spiedController); - verify(spiedController, atLeastOnce()).init(); - } - - private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages, - List<UserPackageTime> runningFgsPackages) { - for (Pair<Integer, String> userPkg : addedPackages) { - assertTrue(userPkg + " was not found in returned list", - runningFgsPackages.stream().anyMatch( - upt -> userPkg.first == upt.getUserId() - && Objects.equals(upt.getPackageName(), userPkg.second))); - } - for (UserPackageTime upt : runningFgsPackages) { - int userId = upt.getUserId(); - String packageName = upt.getPackageName(); - assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">" - + " in returned list", - addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId - && Objects.equals(packageName, userPkg.second))); - } - } - - private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer, - IBinder token, String pkg, int userId) throws RemoteException { - observer.onForegroundStateChanged(token, pkg, userId, false); - List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs(); - assertFalse("Package \"" + pkg + "\" was not removed", - packagesWithFgs.stream().anyMatch(upt -> - Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId)); - } - - private void assertSinglePackage(String packageName, int userId) { - assertEquals(1, mController.getPackagesWithFgs().size()); - assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName()); - assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId()); - } - - private static class TestCallback implements RunningFgsController.Callback { - - private List<List<UserPackageTime>> mInvocations = new ArrayList<>(); - - @Override - public void onFgsPackagesChanged(List<UserPackageTime> packages) { - mInvocations.add(packages); - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 8fb066bb4a39..cb248b05e4bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; @@ -46,6 +47,7 @@ import android.testing.TestableLooper; import android.util.ArrayMap; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -872,6 +874,73 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test + public void testThatSectionComparatorsAreCalled() { + // GIVEN a section with a comparator that elevates some packages over others + NotifComparator comparator = spy(new HypeComparator(PACKAGE_2, PACKAGE_4)); + NotifSectioner sectioner = new PackageSectioner( + List.of(PACKAGE_1, PACKAGE_2, PACKAGE_4, PACKAGE_5), comparator); + mListBuilder.setSectioners(List.of(sectioner)); + + // WHEN the pipeline is kicked off on a bunch of notifications + addNotif(0, PACKAGE_0); + addNotif(1, PACKAGE_1); + addNotif(2, PACKAGE_2); + addNotif(3, PACKAGE_3); + addNotif(4, PACKAGE_4); + addNotif(5, PACKAGE_5); + dispatchBuild(); + + // THEN the notifs are sorted according to both sectioning and the section's comparator + verifyBuiltList( + notif(2), + notif(4), + notif(1), + notif(5), + notif(0), + notif(3) + ); + + // VERIFY that the comparator is invoked at least 3 times + verify(comparator, atLeast(3)).compare(any(), any()); + + // VERIFY that the comparator is never invoked with the entry from package 0 or 3. + final NotificationEntry package0Entry = mEntrySet.get(0); + verify(comparator, never()).compare(eq(package0Entry), any()); + verify(comparator, never()).compare(any(), eq(package0Entry)); + final NotificationEntry package3Entry = mEntrySet.get(3); + verify(comparator, never()).compare(eq(package3Entry), any()); + verify(comparator, never()).compare(any(), eq(package3Entry)); + } + + @Test + public void testThatSectionComparatorsAreNotCalledForSectionWithSingleEntry() { + // GIVEN a section with a comparator that will have only 1 element + NotifComparator comparator = spy(new HypeComparator(PACKAGE_3)); + NotifSectioner sectioner = new PackageSectioner(List.of(PACKAGE_3), comparator); + mListBuilder.setSectioners(List.of(sectioner)); + + // WHEN the pipeline is kicked off on a bunch of notifications + addNotif(0, PACKAGE_1); + addNotif(1, PACKAGE_2); + addNotif(2, PACKAGE_3); + addNotif(3, PACKAGE_4); + addNotif(4, PACKAGE_5); + dispatchBuild(); + + // THEN the notifs are sorted according to the sectioning + verifyBuiltList( + notif(2), + notif(0), + notif(1), + notif(3), + notif(4) + ); + + // VERIFY that the comparator is never invoked + verify(comparator, never()).compare(any(), any()); + } + + @Test public void testListenersAndPluggablesAreFiredInOrder() { // GIVEN a bunch of registered listeners and pluggables NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1)); @@ -934,7 +1003,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a variety of pluggables NotifFilter packageFilter = new PackageFilter(PACKAGE_1); NotifPromoter idPromoter = new IdPromoter(4); - NotifSectioner section = new PackageSectioner(PACKAGE_1); + NotifComparator sectionComparator = new HypeComparator(PACKAGE_1); + NotifSectioner section = new PackageSectioner(List.of(PACKAGE_1), sectionComparator); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {}; @@ -969,6 +1039,10 @@ public class ShadeListBuilderTest extends SysuiTestCase { verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); + sectionComparator.invalidateList(); + verify(mOnRenderListListener).onRenderList(anyList()); + + clearInvocations(mOnRenderListListener); preRenderInvalidator.invalidateList(); verify(mOnRenderListListener).onRenderList(anyList()); } @@ -2037,16 +2111,30 @@ public class ShadeListBuilderTest extends SysuiTestCase { /** Represents a section for the passed pkg */ private static class PackageSectioner extends NotifSectioner { - private final String mPackage; + private final List<String> mPackages; + private final NotifComparator mComparator; + + PackageSectioner(List<String> pkgs, NotifComparator comparator) { + super("PackageSection_" + pkgs, 0); + mPackages = pkgs; + mComparator = comparator; + } PackageSectioner(String pkg) { super("PackageSection_" + pkg, 0); - mPackage = pkg; + mPackages = List.of(pkg); + mComparator = null; + } + + @Nullable + @Override + public NotifComparator getComparator() { + return mComparator; } @Override public boolean isInSection(ListEntry entry) { - return entry.getRepresentativeEntry().getSbn().getPackageName().equals(mPackage); + return mPackages.contains(entry.getRepresentativeEntry().getSbn().getPackageName()); } } @@ -2157,6 +2245,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } } + private static final String PACKAGE_0 = "com.test0"; private static final String PACKAGE_1 = "com.test1"; private static final String PACKAGE_2 = "com.test2"; private static final String PACKAGE_3 = "org.test3"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index 8deac94214bd..7692a05eb5fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -32,7 +32,6 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON -import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse @@ -41,7 +40,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @@ -79,7 +77,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { } peopleSectioner = coordinator.sectioner - peopleComparator = coordinator.comparator + peopleComparator = peopleSectioner.comparator!! entry = NotificationEntryBuilder().setChannel(channel).build() @@ -108,16 +106,6 @@ class ConversationCoordinatorTest : SysuiTestCase() { } @Test - fun testComparatorIgnoresFromOtherSection() { - val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build() - val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build() - - // wrong section -- never classify - assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0) - verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any()) - } - - @Test fun testComparatorPutsImportantPeopleFirst() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_IMPORTANT_PERSON) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index d3258408b33a..424a40058997 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -128,6 +128,28 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { } @Test + public void testCompareTo_withNullEntries() { + NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); + mHeadsUpManager.showNotification(alertEntry); + + assertThat(mHeadsUpManager.compare(alertEntry, null)).isLessThan(0); + assertThat(mHeadsUpManager.compare(null, alertEntry)).isGreaterThan(0); + assertThat(mHeadsUpManager.compare(null, null)).isEqualTo(0); + } + + @Test + public void testCompareTo_withNonAlertEntries() { + NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag("nae1").build(); + NotificationEntry nonAlertEntry2 = new NotificationEntryBuilder().setTag("nae2").build(); + NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); + mHeadsUpManager.showNotification(alertEntry); + + assertThat(mHeadsUpManager.compare(alertEntry, nonAlertEntry1)).isLessThan(0); + assertThat(mHeadsUpManager.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0); + assertThat(mHeadsUpManager.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0); + } + + @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry(); ongoingCall.setEntry(new NotificationEntryBuilder() diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING index 63f54fa35158..4a37cb812981 100644 --- a/services/companion/TEST_MAPPING +++ b/services/companion/TEST_MAPPING @@ -1,6 +1,12 @@ { "presubmit": [ { + "name": "CtsCompanionDeviceManagerCoreTestCases" + }, + { + "name": "CtsCompanionDeviceManagerUiAutomationTestCases" + }, + { "name": "CtsOsTestCases", "options": [ { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 6a7afd90dc96..2d328d8b0949 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -539,7 +539,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void notifyAllPolicyListenersLocked() { for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) { - Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged()); + Binder.withCleanCallingIdentity(() -> { + try { + policyListener.mListener.onPolicyChanged(); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } @@ -548,8 +554,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) { for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, subGroup)) { - Binder.withCleanCallingIdentity( - () -> cbInfo.mCallback.onVcnStatusChanged(statusCode)); + Binder.withCleanCallingIdentity(() -> { + try { + cbInfo.mCallback.onVcnStatusChanged(statusCode); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } } @@ -1222,13 +1233,17 @@ public class VcnManagementService extends IVcnManagementService.Stub { // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, mSubGroup)) { - Binder.withCleanCallingIdentity( - () -> - cbInfo.mCallback.onGatewayConnectionError( - gatewayConnectionName, - errorCode, - exceptionClass, - exceptionMessage)); + Binder.withCleanCallingIdentity(() -> { + try { + cbInfo.mCallback.onGatewayConnectionError( + gatewayConnectionName, + errorCode, + exceptionClass, + exceptionMessage); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fafe908564ef..cbb40aa9a671 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -459,7 +459,7 @@ public class ActivityManagerService extends IActivityManager.Stub * broadcasts */ private static final boolean ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT = - SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", true); + SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", false); static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM; static final String TAG_BACKUP = TAG + POSTFIX_BACKUP; @@ -16089,6 +16089,23 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Returns package name by pid. + */ + @Override + @Nullable + public String getPackageNameByPid(int pid) { + synchronized (mPidsSelfLocked) { + final ProcessRecord app = mPidsSelfLocked.get(pid); + + if (app != null && app.info != null) { + return app.info.packageName; + } + + return null; + } + } + + /** * Sets if the given pid has an overlay UI or not. * * @param pid The pid we are setting overlay UI for. diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index 960fbf1d9330..145a298af95e 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -43,6 +43,7 @@ import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.util.Slog; +import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import com.android.internal.annotations.GuardedBy; @@ -237,7 +238,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan mTaskSystemBarsVisibilityListener); for (GameSessionRecord gameSessionRecord : mGameSessions.values()) { - destroyGameSessionFromRecord(gameSessionRecord); + destroyGameSessionFromRecordLocked(gameSessionRecord); } mGameSessions.clear(); @@ -510,10 +511,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } return; } - destroyGameSessionFromRecord(gameSessionRecord); + destroyGameSessionFromRecordLocked(gameSessionRecord); } - private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) { + @GuardedBy("mLock") + private void destroyGameSessionFromRecordLocked(@NonNull GameSessionRecord gameSessionRecord) { SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage(); if (surfacePackage != null) { try { @@ -586,17 +588,29 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @VisibleForTesting void takeScreenshot(int taskId, @NonNull AndroidFuture callback) { + GameSessionRecord gameSessionRecord; synchronized (mLock) { - boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId); - if (!isTaskAssociatedWithGameSession) { + gameSessionRecord = mGameSessions.get(taskId); + if (gameSessionRecord == null) { Slog.w(TAG, "No game session found for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); return; } } + final SurfacePackage overlaySurfacePackage = gameSessionRecord.getSurfacePackage(); + final SurfaceControl overlaySurfaceControl = + overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null; mBackgroundExecutor.execute(() -> { - final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId); + final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder = + new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null); + if (overlaySurfaceControl != null) { + SurfaceControl[] excludeLayers = new SurfaceControl[1]; + excludeLayers[0] = overlaySurfaceControl; + layerCaptureArgsBuilder.setExcludeLayers(excludeLayers); + } + final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId, + layerCaptureArgsBuilder); if (bitmap == null) { Slog.w(TAG, "Could not get bitmap for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index a1d722b6df9d..c46ae851b752 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -69,8 +69,10 @@ public abstract class BrightnessMappingStrategy { */ @Nullable public static BrightnessMappingStrategy create(Resources resources, - DisplayDeviceConfig displayDeviceConfig) { - return create(resources, displayDeviceConfig, /* isForIdleMode= */ false, null); + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return create(resources, displayDeviceConfig, /* isForIdleMode= */ false, + displayWhiteBalanceController); } /** @@ -845,7 +847,7 @@ public abstract class BrightnessMappingStrategy { float nits = mBrightnessSpline.interpolate(lux); // Adjust nits to compensate for display white balance colour strength. - if (mDisplayWhiteBalanceController != null && isForIdleMode()) { + if (mDisplayWhiteBalanceController != null) { nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index a4a6eb4312cc..6866137e294e 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -30,6 +30,8 @@ import android.util.Slog; import android.util.Spline; import android.view.DisplayAddress; +import com.android.internal.annotations.VisibleForTesting; + import com.android.internal.R; import com.android.internal.display.BrightnessSynchronizer; import com.android.server.display.config.BrightnessThresholds; @@ -40,9 +42,12 @@ import com.android.server.display.config.DisplayConfiguration; import com.android.server.display.config.DisplayQuirks; import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; +import com.android.server.display.config.Interpolation; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; import com.android.server.display.config.RefreshRateRange; +import com.android.server.display.config.SdrHdrRatioMap; +import com.android.server.display.config.SdrHdrRatioPoint; import com.android.server.display.config.SensorDetails; import com.android.server.display.config.ThermalStatus; import com.android.server.display.config.ThermalThrottling; @@ -66,10 +71,126 @@ import java.util.List; import javax.xml.datatype.DatatypeConfigurationException; /** - * Reads and stores display-specific configurations. + * Reads and stores display-specific configurations. + * File format: + * <pre> + * {@code + * <displayConfiguration> + * <densityMap> + * <density> + * <height>480</height> + * <width>720</width> + * <density>120</density> + * </density> + * <density> + * <height>720</height> + * <width>1280</width> + * <density>213</density> + * </density> + * <density> + * <height>1080</height> + * <width>1920</width> + * <density>320</density> + * </density> + * <density> + * <height>2160</height> + * <width>3840</width> + * <density>640</density> + * </density> + * </densityMap> + * + * <screenBrightnessMap> + * <point> + * <value>0.0</value> + * <nits>2.0</nits> + * </point> + * <point> + * <value>0.62</value> + * <nits>500.0</nits> + * </point> + * <point> + * <value>1.0</value> + * <nits>800.0</nits> + * </point> + * </screenBrightnessMap> + * + * <screenBrightnessDefault>0.65</screenBrightnessDefault> + * + * <thermalThrottling> + * <brightnessThrottlingMap> + * <brightnessThrottlingPoint> + * <thermalStatus>severe</thermalStatus> + * <brightness>0.1</brightness> + * </brightnessThrottlingPoint> + * <brightnessThrottlingPoint> + * <thermalStatus>critical</thermalStatus> + * <brightness>0.01</brightness> + * </brightnessThrottlingPoint> + * </brightnessThrottlingMap> + * </thermalThrottling> + * + * <highBrightnessMode enabled="true"> + * <transitionPoint>0.62</transitionPoint> + * <minimumLux>10000</minimumLux> + * <timing> + * <timeWindowSecs>1800</timeWindowSecs> // Window in which we restrict HBM. + * <timeMaxSecs>300</timeMaxSecs> // Maximum time of HBM allowed in that window. + * <timeMinSecs>60</timeMinSecs> // Minimum time remaining required to switch + * </timing> // HBM on for. + * <refreshRate> + * <minimum>120</minimum> + * <maximum>120</maximum> + * </refreshRate> + * <thermalStatusLimit>light</thermalStatusLimit> + * <allowInLowPowerMode>false</allowInLowPowerMode> + * </highBrightnessMode> + * + * <quirks> + * <quirk>canSetBrightnessViaHwc</quirk> + * </quirks> + * + * <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease> + * <screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease> + * <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease> + * <screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease> + * + * <lightSensor> + * <type>android.sensor.light</type> + * <name>1234 Ambient Light Sensor</name> + * </lightSensor> + * <proxSensor> + * <type>android.sensor.proximity</type> + * <name>1234 Proximity Sensor</name> + * </proxSensor> + * + * <ambientLightHorizonLong>10001</ambientLightHorizonLong> + * <ambientLightHorizonShort>2001</ambientLightHorizonShort> + * + * <displayBrightnessChangeThresholds> + * <brighteningThresholds> + * <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten. + * </brighteningThresholds> + * <darkeningThresholds> + * <minimum>0.002</minimum> // Minimum change needed in screen brightness to darken. + * </darkeningThresholds> + * </displayBrightnessChangeThresholds> + * + * <ambientBrightnessChangeThresholds> + * <brighteningThresholds> + * <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten. + * </brighteningThresholds> + * <darkeningThresholds> + * <minimum>0.004</minimum> // Minimum change needed in ambient brightness to darken. + * </darkeningThresholds> + * </ambientBrightnessChangeThresholds> + * + * </displayConfiguration> + * } + * </pre> */ public class DisplayDeviceConfig { private static final String TAG = "DisplayDeviceConfig"; + private static final boolean DEBUG = false; public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN; @@ -86,6 +207,9 @@ public class DisplayDeviceConfig { private static final String NO_SUFFIX_FORMAT = "%d"; private static final long STABLE_FLAG = 1L << 62; + private static final int INTERPOLATION_DEFAULT = 0; + private static final int INTERPOLATION_LINEAR = 1; + // Float.NaN (used as invalid for brightness) cannot be stored in config.xml // so -2 is used instead private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f; @@ -99,6 +223,9 @@ public class DisplayDeviceConfig { // Length of the ambient light horizon used to calculate short-term estimate of ambient light. private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000; + @VisibleForTesting + static final float HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT = 0.5f; + private final Context mContext; // The details of the ambient light sensor associated with this display. @@ -114,6 +241,7 @@ public class DisplayDeviceConfig { // config.xml. These are the raw values and just used for the dumpsys private float[] mRawNits; private float[] mRawBacklight; + private int mInterpolationType; // These arrays are calculated from the raw arrays, but clamped to contain values equal to and // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same @@ -142,6 +270,7 @@ public class DisplayDeviceConfig { private Spline mBrightnessToBacklightSpline; private Spline mBacklightToBrightnessSpline; private Spline mBacklightToNitsSpline; + private Spline mNitsToBacklightSpline; private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; @@ -149,6 +278,7 @@ public class DisplayDeviceConfig { private String mLoadedFrom = null; private BrightnessThrottlingData mBrightnessThrottlingData; + private Spline mSdrToHdrRatioSpline; private DisplayDeviceConfig(Context context) { mContext = context; @@ -333,6 +463,45 @@ public class DisplayDeviceConfig { } /** + * Calculate the HDR brightness for the specified SDR brightenss. + * + * @return the HDR brightness or BRIGHTNESS_INVALID when no mapping exists. + */ + public float getHdrBrightnessFromSdr(float brightness) { + if (mSdrToHdrRatioSpline == null) { + return PowerManager.BRIGHTNESS_INVALID; + } + + float backlight = getBacklightFromBrightness(brightness); + float nits = getNitsFromBacklight(backlight); + if (nits == NITS_INVALID) { + return PowerManager.BRIGHTNESS_INVALID; + } + + float ratio = mSdrToHdrRatioSpline.interpolate(nits); + float hdrNits = nits * ratio; + if (mNitsToBacklightSpline == null) { + return PowerManager.BRIGHTNESS_INVALID; + } + + float hdrBacklight = mNitsToBacklightSpline.interpolate(hdrNits); + hdrBacklight = Math.max(mBacklightMinimum, Math.min(mBacklightMaximum, hdrBacklight)); + float hdrBrightness = mBacklightToBrightnessSpline.interpolate(hdrBacklight); + + if (DEBUG) { + Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness + + " backlight " + backlight + + " nits " + nits + + " ratio " + ratio + + " hdrNits " + hdrNits + + " hdrBacklight " + hdrBacklight + + " hdrBrightness " + hdrBrightness + ); + } + return hdrBrightness; + } + + /** * Return an array of equal length to backlight and nits, that covers the entire system * brightness range of 0.0-1.0. * @@ -444,15 +613,18 @@ public class DisplayDeviceConfig { + ", mNits=" + Arrays.toString(mNits) + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + ", mRawNits=" + Arrays.toString(mRawNits) + + ", mInterpolationType=" + mInterpolationType + ", mBrightness=" + Arrays.toString(mBrightness) + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline + + ", mNitsToBacklightSpline=" + mNitsToBacklightSpline + ", mBacklightMinimum=" + mBacklightMinimum + ", mBacklightMaximum=" + mBacklightMaximum + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled + ", mHbmData=" + mHbmData + + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline + ", mBrightnessThrottlingData=" + mBrightnessThrottlingData + ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease @@ -653,6 +825,7 @@ public class DisplayDeviceConfig { float[] nits = new float[size]; float[] backlight = new float[size]; + mInterpolationType = convertInterpolationType(map.getInterpolation()); int i = 0; for (Point point : points) { nits[i] = point.getNits().floatValue(); @@ -678,6 +851,40 @@ public class DisplayDeviceConfig { constrainNitsAndBacklightArrays(); } + private Spline loadSdrHdrRatioMap(HighBrightnessMode hbmConfig) { + final SdrHdrRatioMap sdrHdrRatioMap = hbmConfig.getSdrHdrRatioMap_all(); + + if (sdrHdrRatioMap == null) { + return null; + } + + final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint(); + final int size = points.size(); + if (size <= 0) { + return null; + } + + float[] nits = new float[size]; + float[] ratios = new float[size]; + + int i = 0; + for (SdrHdrRatioPoint point : points) { + nits[i] = point.getSdrNits().floatValue(); + if (i > 0) { + if (nits[i] < nits[i - 1]) { + Slog.e(TAG, "sdrHdrRatioMap must be non-decreasing, ignoring rest " + + " of configuration. nits: " + nits[i] + " < " + + nits[i - 1]); + return null; + } + } + ratios[i] = point.getHdrRatio().floatValue(); + ++i; + } + + return Spline.createSpline(nits, ratios); + } + private void loadBrightnessThrottlingMap(DisplayConfiguration config) { final ThermalThrottling throttlingConfig = config.getThermalThrottling(); if (throttlingConfig == null) { @@ -823,9 +1030,18 @@ public class DisplayDeviceConfig { mBacklight[mBacklight.length - 1], PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]); } - mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight); - mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness); - mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits); + mBrightnessToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR + ? Spline.createLinearSpline(mBrightness, mBacklight) + : Spline.createSpline(mBrightness, mBacklight); + mBacklightToBrightnessSpline = mInterpolationType == INTERPOLATION_LINEAR + ? Spline.createLinearSpline(mBacklight, mBrightness) + : Spline.createSpline(mBacklight, mBrightness); + mBacklightToNitsSpline = mInterpolationType == INTERPOLATION_LINEAR + ? Spline.createLinearSpline(mBacklight, mNits) + : Spline.createSpline(mBacklight, mNits); + mNitsToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR + ? Spline.createLinearSpline(mNits, mBacklight) + : Spline.createSpline(mNits, mBacklight); } private void loadQuirks(DisplayConfiguration config) { @@ -862,6 +1078,20 @@ public class DisplayDeviceConfig { mRefreshRateLimitations.add(new RefreshRateLimitation( DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max)); } + BigDecimal minHdrPctOfScreen = hbm.getMinimumHdrPercentOfScreen_all(); + if (minHdrPctOfScreen != null) { + mHbmData.minimumHdrPercentOfScreen = minHdrPctOfScreen.floatValue(); + if (mHbmData.minimumHdrPercentOfScreen > 1 + || mHbmData.minimumHdrPercentOfScreen < 0) { + Slog.w(TAG, "Invalid minimum HDR percent of screen: " + + String.valueOf(mHbmData.minimumHdrPercentOfScreen)); + mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT; + } + } else { + mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT; + } + + mSdrToHdrRatioSpline = loadSdrHdrRatioMap(hbm); } } @@ -1024,6 +1254,21 @@ public class DisplayDeviceConfig { } } + private int convertInterpolationType(Interpolation value) { + if (value == null) { + return INTERPOLATION_DEFAULT; + } + switch (value) { + case _default: + return INTERPOLATION_DEFAULT; + case linear: + return INTERPOLATION_LINEAR; + default: + Slog.wtf(TAG, "Unexpected Interpolation Type: " + value); + return INTERPOLATION_DEFAULT; + } + } + private void loadAmbientHorizonFromDdc(DisplayConfiguration config) { final BigInteger configLongHorizon = config.getAmbientLightHorizonLong(); if (configLongHorizon != null) { @@ -1088,11 +1333,15 @@ public class DisplayDeviceConfig { /** Minimum time that HBM can be on before being enabled. */ public long timeMinMillis; + /** Minimum HDR video size to enter high brightness mode */ + public float minimumHdrPercentOfScreen; + HighBrightnessModeData() {} HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis, long timeMaxMillis, long timeMinMillis, - @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) { + @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode, + float minimumHdrPercentOfScreen) { this.minimumLux = minimumLux; this.transitionPoint = transitionPoint; this.timeWindowMillis = timeWindowMillis; @@ -1100,6 +1349,7 @@ public class DisplayDeviceConfig { this.timeMinMillis = timeMinMillis; this.thermalStatusLimit = thermalStatusLimit; this.allowInLowPowerMode = allowInLowPowerMode; + this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen; } /** @@ -1114,6 +1364,7 @@ public class DisplayDeviceConfig { other.transitionPoint = transitionPoint; other.thermalStatusLimit = thermalStatusLimit; other.allowInLowPowerMode = allowInLowPowerMode; + other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen; } @Override @@ -1126,6 +1377,7 @@ public class DisplayDeviceConfig { + ", timeMin: " + timeMinMillis + "ms" + ", thermalStatusLimit: " + thermalStatusLimit + ", allowInLowPowerMode: " + allowInLowPowerMode + + ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen + "} "; } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 6ae1a5a96a24..d71e07ab573f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -546,28 +546,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); - setUpAutoBrightness(resources, handler); - - mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic(); - mColorFadeFadesConfig = resources.getBoolean( - com.android.internal.R.bool.config_animateScreenLights); - - mDisplayBlanksAfterDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBlanksAfterDoze); - - mBrightnessBucketsInDozeConfig = resources.getBoolean( - com.android.internal.R.bool.config_displayBrightnessBucketsInDoze); - - loadProximitySensor(); - - mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); - mScreenBrightnessForVr = getScreenBrightnessForVrSetting(); - mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); - mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; - mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; - DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; if (mDisplayId == Display.DEFAULT_DISPLAY) { @@ -610,6 +588,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else { mCdsi = null; } + + setUpAutoBrightness(resources, handler); + + mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic(); + mColorFadeFadesConfig = resources.getBoolean( + com.android.internal.R.bool.config_animateScreenLights); + + mDisplayBlanksAfterDozeConfig = resources.getBoolean( + com.android.internal.R.bool.config_displayBlanksAfterDoze); + + mBrightnessBucketsInDozeConfig = resources.getBoolean( + com.android.internal.R.bool.config_displayBrightnessBucketsInDoze); + + loadProximitySensor(); + + mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); + mScreenBrightnessForVr = getScreenBrightnessForVrSetting(); + mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting(); + mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } private void applyReduceBrightColorsSplineAdjustment( @@ -831,7 +832,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call setUpAutoBrightness(mContext.getResources(), mHandler); reloadReduceBrightColours(); mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, - mDisplayDeviceConfig.getHighBrightnessModeData()); + mDisplayDeviceConfig.getHighBrightnessModeData(), + new HighBrightnessModeController.HdrBrightnessDeviceConfig() { + @Override + public float getHdrBrightnessFromSdr(float sdrBrightness) { + return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness); + } + }); mBrightnessThrottler.resetThrottlingData( mDisplayDeviceConfig.getBrightnessThrottlingData()); } @@ -901,7 +908,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( R.bool.config_enableIdleScreenBrightnessMode); mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources, - mDisplayDeviceConfig); + mDisplayDeviceConfig, mDisplayWhiteBalanceController); if (isIdleScreenBrightnessEnabled) { mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources, mDisplayDeviceConfig, mDisplayWhiteBalanceController); @@ -1713,6 +1720,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, + new HighBrightnessModeController.HdrBrightnessDeviceConfig() { + @Override + public float getHdrBrightnessFromSdr(float sdrBrightness) { + return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness); + } + }, () -> { sendUpdatePowerStateLocked(); postBrightnessChangeRunnable(); diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 23c17f5af10d..0b9d4debd16f 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -58,11 +58,13 @@ class HighBrightnessModeController { private static final boolean DEBUG = false; - private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f; - @VisibleForTesting static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; + public interface HdrBrightnessDeviceConfig { + float getHdrBrightnessFromSdr(float sdrBrightness); + } + private final float mBrightnessMin; private final float mBrightnessMax; private final Handler mHandler; @@ -76,6 +78,7 @@ class HighBrightnessModeController { private HdrListener mHdrListener; private HighBrightnessModeData mHbmData; + private HdrBrightnessDeviceConfig mHdrBrightnessCfg; private IBinder mRegisteredDisplayToken; private boolean mIsInAllowedAmbientRange = false; @@ -115,16 +118,17 @@ class HighBrightnessModeController { HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, - HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) { + HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, Context context) { this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin, - brightnessMax, hbmData, hbmChangeCallback, context); + brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context); } @VisibleForTesting HighBrightnessModeController(Injector injector, Handler handler, int width, int height, IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, - HighBrightnessModeData hbmData, Runnable hbmChangeCallback, - Context context) { + HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, Context context) { mInjector = injector; mContext = context; mClock = injector.getClock(); @@ -138,7 +142,7 @@ class HighBrightnessModeController { mRecalcRunnable = this::recalculateTimeAllowance; mHdrListener = new HdrListener(); - resetHbmData(width, height, displayToken, displayUniqueId, hbmData); + resetHbmData(width, height, displayToken, displayUniqueId, hbmData, hdrBrightnessCfg); } void setAutoBrightnessEnabled(int state) { @@ -178,6 +182,13 @@ class HighBrightnessModeController { } float getHdrBrightnessValue() { + if (mHdrBrightnessCfg != null) { + float hdrBrightness = mHdrBrightnessCfg.getHdrBrightnessFromSdr(mBrightness); + if (hdrBrightness != PowerManager.BRIGHTNESS_INVALID) { + return hdrBrightness; + } + } + // For HDR brightness, we take the current brightness and scale it to the max. The reason // we do this is because we want brightness to go to HBM max when it would normally go // to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition @@ -250,10 +261,11 @@ class HighBrightnessModeController { } void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId, - HighBrightnessModeData hbmData) { + HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) { mWidth = width; mHeight = height; mHbmData = hbmData; + mHdrBrightnessCfg = hdrBrightnessCfg; mDisplayStatsId = displayUniqueId.hashCode(); unregisterHdrListener(); @@ -602,8 +614,8 @@ class HighBrightnessModeController { int maxW, int maxH, int flags) { mHandler.post(() -> { mIsHdrLayerPresent = numberOfHdrLayers > 0 - && (float) (maxW * maxH) - >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED); + && (float) (maxW * maxH) >= ((float) (mWidth * mHeight) + * mHbmData.minimumHdrPercentOfScreen); // Calling the brightness update so that we can recalculate // brightness with HDR in mind. onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason); diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java new file mode 100644 index 000000000000..6b442a6a395e --- /dev/null +++ b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.logcat; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.ServiceManager; +import android.os.logcat.ILogcatManagerService; +import android.util.Slog; +import android.view.View; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + + +/** + * This dialog is shown to the user before an activity in a harmful app is launched. + * + * See {@code PackageManager.setLogcatAppInfo} for more info. + */ +public class LogAccessConfirmationActivity extends AlertActivity implements + DialogInterface.OnClickListener { + private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName(); + + private String mPackageName; + private IntentSender mTarget; + private final ILogcatManagerService mLogcatManagerService = + ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat")); + + private int mUid; + private int mGid; + private int mPid; + private int mFd; + + private static final String EXTRA_UID = "uid"; + private static final String EXTRA_GID = "gid"; + private static final String EXTRA_PID = "pid"; + private static final String EXTRA_FD = "fd"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Intent intent = getIntent(); + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mUid = intent.getIntExtra("uid", 0); + mGid = intent.getIntExtra("gid", 0); + mPid = intent.getIntExtra("pid", 0); + mFd = intent.getIntExtra("fd", 0); + + final AlertController.AlertParams p = mAlertParams; + p.mTitle = getString(R.string.log_access_confirmation_title); + p.mView = createView(); + + p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny); + p.mNegativeButtonListener = this; + + mAlert.installContent(mAlertParams); + } + + private View createView() { + final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog, + null /*root*/); + ((TextView) view.findViewById(R.id.app_name_text)) + .setText(mPackageName); + ((TextView) view.findViewById(R.id.message)) + .setText(getIntent().getExtras().getString("body")); + return view; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + try { + mLogcatManagerService.approve(mUid, mGid, mPid, mFd); + } catch (Throwable t) { + Slog.e(TAG, "Could not start the LogcatManagerService.", t); + } + finish(); + break; + case DialogInterface.BUTTON_NEGATIVE: + try { + mLogcatManagerService.decline(mUid, mGid, mPid, mFd); + } catch (Throwable t) { + Slog.e(TAG, "Could not start the LogcatManagerService.", t); + } + finish(); + break; + } + } + + /** + * Create the Intent for a LogAccessConfirmationActivity. + */ + public static Intent createIntent(Context context, String targetPackageName, + IntentSender target, int uid, int gid, int pid, int fd) { + final Intent intent = new Intent(); + intent.setClass(context, LogAccessConfirmationActivity.class); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName); + intent.putExtra(EXTRA_UID, uid); + intent.putExtra(EXTRA_GID, gid); + intent.putExtra(EXTRA_PID, pid); + intent.putExtra(EXTRA_FD, fd); + + return intent; + } + +} diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index ff6372aec3bd..140c6d48b57b 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -16,20 +16,36 @@ package com.android.server.logcat; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.ActivityManagerInternal; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.ILogd; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.logcat.ILogcatManagerService; import android.util.Slog; +import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import com.android.server.SystemService; +import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * Service responsible for manage the access to Logcat. + * Service responsible for managing the access to Logcat. */ public final class LogcatManagerService extends SystemService { @@ -38,6 +54,43 @@ public final class LogcatManagerService extends SystemService { private final BinderService mBinderService; private final ExecutorService mThreadExecutor; private ILogd mLogdService; + private NotificationManager mNotificationManager; + private @NonNull ActivityManager mActivityManager; + private ActivityManagerInternal mActivityManagerInternal; + private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2; + private static int sUidImportanceListenerCount = 0; + private static final int AID_SHELL_UID = 2000; + + // TODO This allowlist is just a temporary workaround for the tests: + // FrameworksServicesTests + // PlatformRuleTests + // After adapting the test suites, the allowlist will be removed in + // the upcoming bug fix patches. + private static final String[] ALLOWABLE_TESTING_PACKAGES = { + "android.platform.test.rule.tests", + "com.android.frameworks.servicestests" + }; + + // TODO Same as the above ALLOWABLE_TESTING_PACKAGES. + private boolean isAllowableTestingPackage(int uid) { + PackageManager pm = mContext.getPackageManager(); + + String[] packageNames = pm.getPackagesForUid(uid); + + if (ArrayUtils.isEmpty(packageNames)) { + return false; + } + + for (String name : packageNames) { + Slog.e(TAG, "isAllowableTestingPackage: " + name); + + if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) { + return true; + } + } + + return false; + }; private final class BinderService extends ILogcatManagerService.Stub { @Override @@ -51,6 +104,197 @@ public final class LogcatManagerService extends SystemService { // the logd data access is finished. mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false)); } + + @Override + public void approve(int uid, int gid, int pid, int fd) { + try { + getLogdService().approve(uid, gid, pid, fd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + public void decline(int uid, int gid, int pid, int fd) { + try { + getLogdService().decline(uid, gid, pid, fd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private ILogd getLogdService() { + synchronized (LogcatManagerService.this) { + if (mLogdService == null) { + LogcatManagerService.this.addLogdService(); + } + return mLogdService; + } + } + + private String getBodyString(Context context, String callingPackage, int uid) { + PackageManager pm = context.getPackageManager(); + try { + return context.getString( + com.android.internal.R.string.log_access_confirmation_body, + pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO, + UserHandle.getUserId(uid)).loadLabel(pm)); + } catch (NameNotFoundException e) { + // App name is unknown. + return null; + } + } + + private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid, + int fd) { + + final ActivityManagerInternal activityManagerInternal = + LocalServices.getService(ActivityManagerInternal.class); + + PackageManager pm = mContext.getPackageManager(); + String packageName = activityManagerInternal.getPackageNameByPid(pid); + if (packageName != null) { + String notificationBody = getBodyString(mContext, packageName, uid); + + final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext, + packageName, null, uid, gid, pid, fd); + + if (notificationBody == null) { + // Decline the logd access if the nofitication body is unknown + Slog.e(TAG, "Unknown notification body, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + // TODO Next version will replace notification with dialogue + // per UX guidance. + generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody, + mIntent); + return; + + } + + String[] packageNames = pm.getPackagesForUid(uid); + + if (ArrayUtils.isEmpty(packageNames)) { + // Decline the logd access if the app name is unknown + Slog.e(TAG, "Unknown calling package name, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + String firstPackageName = packageNames[0]; + + if (firstPackageName == null || firstPackageName.length() == 0) { + // Decline the logd access if the package name from uid is unknown + Slog.e(TAG, "Unknown calling package name, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + String notificationBody = getBodyString(mContext, firstPackageName, uid); + + final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext, + firstPackageName, null, uid, gid, pid, fd); + + if (notificationBody == null) { + Slog.e(TAG, "Unknown notification body, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + // TODO Next version will replace notification with dialogue + // per UX guidance. + generateNotificationWithBodyContent(notificationId, clientInfo, + notificationBody, mIntent); + } + + private void declineLogdAccess(int uid, int gid, int pid, int fd) { + try { + getLogdService().decline(uid, gid, pid, fd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions ", ex); + } + } + + private void generateNotificationWithBodyContent(int notificationId, String clientInfo, + String notificationBody, Intent intent) { + final Notification.Builder notificationBuilder = new Notification.Builder( + mContext, + SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.setIdentifier(String.valueOf(notificationId) + clientInfo); + intent.putExtra("body", notificationBody); + + notificationBuilder + .setSmallIcon(R.drawable.ic_info) + .setContentTitle( + mContext.getString(R.string.log_access_confirmation_title)) + .setContentText(notificationBody) + .setContentIntent( + PendingIntent.getActivity(mContext, 0, intent, + PendingIntent.FLAG_IMMUTABLE)) + .setTicker(mContext.getString(R.string.log_access_confirmation_title)) + .setOnlyAlertOnce(true) + .setAutoCancel(true); + mNotificationManager.notify(notificationId, notificationBuilder.build()); + } + + /** + * A class which watches an uid for background access and notifies the logdMonitor when + * the package status becomes foreground (importance change) + */ + private class UidImportanceListener implements ActivityManager.OnUidImportanceListener { + private final int mExpectedUid; + private final int mExpectedGid; + private final int mExpectedPid; + private final int mExpectedFd; + private int mExpectedImportance; + private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE; + + UidImportanceListener(int uid, int gid, int pid, int fd, int importance) { + mExpectedUid = uid; + mExpectedGid = gid; + mExpectedPid = pid; + mExpectedFd = fd; + mExpectedImportance = importance; + } + + @Override + public void onUidImportance(int uid, int importance) { + if (uid == mExpectedUid) { + mCurrentImportance = importance; + + /** + * 1) If the process status changes to foreground, send a notification + * for user consent. + * 2) If the process status remains background, we decline logd access request. + **/ + if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { + String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd); + sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid, + mExpectedFd); + mActivityManager.removeOnUidImportanceListener(this); + + synchronized (LogcatManagerService.this) { + sUidImportanceListenerCount--; + } + } else { + try { + getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions ", ex); + } + } + } + } + } + + private static String getClientInfo(int uid, int gid, int pid, int fd) { + return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID=" + + Integer.toString(pid) + " FD=" + Integer.toString(fd); } private class LogdMonitor implements Runnable { @@ -74,9 +318,7 @@ public final class LogcatManagerService extends SystemService { } /** - * The current version grant the permission by default. - * And track the logd access. - * The next version will generate a prompt for users. + * LogdMonitor generates a prompt for users. * The users decide whether the logd access is allowed. */ @Override @@ -86,10 +328,61 @@ public final class LogcatManagerService extends SystemService { } if (mStart) { - try { - mLogdService.approve(mUid, mGid, mPid, mFd); - } catch (RemoteException ex) { - Slog.e(TAG, "Fails to call remote functions ", ex); + + // TODO See the comments of ALLOWABLE_TESTING_PACKAGES. + if (isAllowableTestingPackage(mUid)) { + try { + getLogdService().approve(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + return; + } + + // If the access request is coming from adb shell, approve the logd access + if (mUid == AID_SHELL_UID) { + try { + getLogdService().approve(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + return; + } + + final int procState = LocalServices.getService(ActivityManagerInternal.class) + .getUidProcessState(mUid); + // If the process is foreground, send a notification for user consent + if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + String clientInfo = getClientInfo(mUid, mGid, mPid, mFd); + sendNotification(0, clientInfo, mUid, mGid, mPid, mFd); + } else { + /** + * If the process is background, add a background process change listener and + * monitor if the process status changes. + * To avoid clients registering multiple listeners, we limit the number of + * maximum listeners to MAX_UID_IMPORTANCE_COUNT_LISTENER. + **/ + if (mActivityManager == null) { + return; + } + + synchronized (LogcatManagerService.this) { + if (sUidImportanceListenerCount < MAX_UID_IMPORTANCE_COUNT_LISTENER) { + // Trigger addOnUidImportanceListener when there is an update from + // the importance of the process + mActivityManager.addOnUidImportanceListener(new UidImportanceListener( + mUid, mGid, mPid, mFd, + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE), + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); + sUidImportanceListenerCount++; + } else { + try { + getLogdService().decline(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } } } } @@ -100,6 +393,8 @@ public final class LogcatManagerService extends SystemService { mContext = context; mBinderService = new BinderService(); mThreadExecutor = Executors.newCachedThreadPool(); + mActivityManager = context.getSystemService(ActivityManager.class); + mNotificationManager = mContext.getSystemService(NotificationManager.class); } @Override @@ -114,5 +409,4 @@ public final class LogcatManagerService extends SystemService { private void addLogdService() { mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd")); } - } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 2e9ad50f23f6..2d870997bed7 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -32,9 +32,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.SigningDetails; -import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.component.ParsedApexSystemService; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.os.Binder; @@ -59,6 +56,9 @@ import com.android.modules.utils.build.UnboundedSdkLevel; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.pkg.component.ParsedApexSystemService; +import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.TimingsTraceAndSlog; import com.google.android.collect.Lists; @@ -414,9 +414,11 @@ public abstract class ApexManager { throws PackageManagerException; /** - * Get a map of system services defined in an apex mapped to the jar files they reside in. + * Get a list of apex system services implemented in an apex. + * + * <p>The list is sorted by initOrder for consistency. */ - public abstract Map<String, String> getApexSystemServices(); + public abstract List<ApexSystemServiceInfo> getApexSystemServices(); /** * Dumps various state information to the provided {@link PrintWriter} object. @@ -449,7 +451,7 @@ public abstract class ApexManager { * Map of all apex system services to the jar files they are contained in. */ @GuardedBy("mLock") - private Map<String, String> mApexSystemServices = new ArrayMap<>(); + private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>(); /** * Contains the list of {@code packageName}s of apks-in-apex for given @@ -605,14 +607,19 @@ public abstract class ApexManager { } String name = service.getName(); - if (mApexSystemServices.containsKey(name)) { - throw new IllegalStateException(String.format( - "Duplicate apex-system-service %s from %s, %s", - name, mApexSystemServices.get(name), service.getJarPath())); + for (ApexSystemServiceInfo info : mApexSystemServices) { + if (info.getName().equals(name)) { + throw new IllegalStateException(String.format( + "Duplicate apex-system-service %s from %s, %s", + name, info.mJarPath, service.getJarPath())); + } } - mApexSystemServices.put(name, service.getJarPath()); + ApexSystemServiceInfo info = new ApexSystemServiceInfo( + service.getName(), service.getJarPath(), service.getInitOrder()); + mApexSystemServices.add(info); } + Collections.sort(mApexSystemServices); mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); if (ai.isActive) { if (activePackagesSet.contains(packageInfo.packageName)) { @@ -1133,7 +1140,7 @@ public abstract class ApexManager { } @Override - public Map<String, String> getApexSystemServices() { + public List<ApexSystemServiceInfo> getApexSystemServices() { synchronized (mLock) { Preconditions.checkState(mApexSystemServices != null, "APEX packages have not been scanned"); @@ -1423,10 +1430,10 @@ public abstract class ApexManager { } @Override - public Map<String, String> getApexSystemServices() { + public List<ApexSystemServiceInfo> getApexSystemServices() { // TODO(satayev): we can't really support flattened apex use case, and need to migrate // the manifest entries into system's manifest asap. - return Collections.emptyMap(); + return Collections.emptyList(); } @Override diff --git a/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java new file mode 100644 index 000000000000..f75ba6d165c9 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.Nullable; + +/** + * A helper class that contains information about apex-system-service to be used within system + * server process. + */ +public final class ApexSystemServiceInfo implements Comparable<ApexSystemServiceInfo> { + + final String mName; + @Nullable + final String mJarPath; + final int mInitOrder; + + public ApexSystemServiceInfo(String name, String jarPath, int initOrder) { + this.mName = name; + this.mJarPath = jarPath; + this.mInitOrder = initOrder; + } + + public String getName() { + return mName; + } + + public String getJarPath() { + return mJarPath; + } + + public int getInitOrder() { + return mInitOrder; + } + + @Override + public int compareTo(ApexSystemServiceInfo other) { + if (mInitOrder == other.mInitOrder) { + return mName.compareTo(other.mName); + } + // higher initOrder values take precedence + return -Integer.compare(mInitOrder, other.mInitOrder); + } +} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java index 586d2c48d27d..cf478b1da2e4 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java @@ -34,4 +34,7 @@ public interface ParsedApexSystemService { @Nullable String getMaxSdkVersion(); + + int getInitOrder(); + } diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java index 1e427d015cc1..167aba301f35 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java @@ -48,18 +48,18 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @Nullable private String maxSdkVersion; + private int initOrder; + public ParsedApexSystemServiceImpl() { } - - // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -71,13 +71,15 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @NonNull String name, @Nullable String jarPath, @Nullable String minSdkVersion, - @Nullable String maxSdkVersion) { + @Nullable String maxSdkVersion, + int initOrder) { this.name = name; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, name); this.jarPath = jarPath; this.minSdkVersion = minSdkVersion; this.maxSdkVersion = maxSdkVersion; + this.initOrder = initOrder; // onConstructed(); // You can define this method to get a callback } @@ -103,6 +105,11 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @DataClass.Generated.Member + public int getInitOrder() { + return initOrder; + } + + @DataClass.Generated.Member public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) { name = value; com.android.internal.util.AnnotationValidations.validate( @@ -129,6 +136,12 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setInitOrder( int value) { + initOrder = value; + return this; + } + + @DataClass.Generated.Member static Parcelling<String> sParcellingForName = Parcelling.Cache.get( Parcelling.BuiltIn.ForInternedString.class); @@ -187,6 +200,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par sParcellingForJarPath.parcel(jarPath, dest, flags); sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags); sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags); + dest.writeInt(initOrder); } @Override @@ -205,6 +219,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par String _jarPath = sParcellingForJarPath.unparcel(in); String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in); String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in); + int _initOrder = in.readInt(); this.name = _name; com.android.internal.util.AnnotationValidations.validate( @@ -212,6 +227,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par this.jarPath = _jarPath; this.minSdkVersion = _minSdkVersion; this.maxSdkVersion = _maxSdkVersion; + this.initOrder = _initOrder; // onConstructed(); // You can define this method to get a callback } @@ -231,10 +247,10 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par }; @DataClass.Generated( - time = 1641431950080L, + time = 1643723578605L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java", - inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") + sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java index 38a6f5a356e7..ed9aa2e6860a 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java @@ -53,10 +53,13 @@ public class ParsedApexSystemServiceUtils { R.styleable.AndroidManifestApexSystemService_minSdkVersion); String maxSdkVersion = sa.getString( R.styleable.AndroidManifestApexSystemService_maxSdkVersion); + int initOrder = sa.getInt(R.styleable.AndroidManifestApexSystemService_initOrder, 0); systemService.setName(className) .setMinSdkVersion(minSdkVersion) - .setMaxSdkVersion(maxSdkVersion); + .setMaxSdkVersion(maxSdkVersion) + .setInitOrder(initOrder); + if (!TextUtils.isEmpty(jarPath)) { systemService.setJarPath(jarPath); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5f04b7e2621a..6836e315437c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7655,13 +7655,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true. * <li>The activity is eligible for fixed orientation letterbox. * <li>The activity is in fullscreen. + * <li>The activity is portrait-only. * </ul> */ // TODO(b/215316431): Add tests boolean isEligibleForLetterboxEducation() { return mWmService.mLetterboxConfiguration.getIsEducationEnabled() && mIsEligibleForFixedOrientationLetterbox - && getWindowingMode() == WINDOWING_MODE_FULLSCREEN; + && getWindowingMode() == WINDOWING_MODE_FULLSCREEN + && getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT; } /** diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index a8779fa79675..45a6cb9d3920 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -29,6 +29,7 @@ import android.os.SystemProperties; import android.util.Slog; import android.view.SurfaceControl; import android.window.BackNavigationInfo; +import android.window.IOnBackInvokedCallback; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; @@ -91,29 +92,41 @@ class BackNavigationController { HardwareBuffer screenshotBuffer = null; int prevTaskId; int prevUserId; + IOnBackInvokedCallback callback; synchronized (task.mWmService.mGlobalLock) { activityRecord = task.topRunningActivity(); removedWindowContainer = activityRecord; taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s", - task, activityRecord); - - // IME is visible, back gesture will dismiss it, nothing to preview. - if (task.getDisplayContent().getImeContainer().isVisible()) { - return null; - } - - // Current Activity is home, there is no previous activity to display - if (activityRecord.isActivityTypeHome()) { - return null; + WindowState topChild = activityRecord.getTopChild(); + callback = topChild.getOnBackInvokedCallback(); + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " + + "topRunningActivity=%s, topWindow=%s backCallback=%s", + task, activityRecord, topChild, + callback != null ? callback.getClass().getSimpleName() : null); + + // For IME and Home, either a callback is registered, or we do nothing. In both cases, + // we don't need to pass the leashes below. + if (task.getDisplayContent().getImeContainer().isVisible() + || activityRecord.isActivityTypeHome()) { + if (callback != null) { + return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, + null /* topWindowLeash */, null /* screenshotSurface */, + null /* screenshotBuffer */, null /* taskWindowConfiguration */, + null /* onBackNavigationDone */, callback /* onBackInvokedCallback */); + } else { + return null; + } } prev = task.getActivity( (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity()); - if (prev != null) { + if (callback != null) { + backType = BackNavigationInfo.TYPE_CALLBACK; + } else if (prev != null) { backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else if (task.returnsToHomeRootTask()) { prevTask = null; @@ -148,7 +161,7 @@ class BackNavigationController { // Prepare a leash to animate the current top window animLeash = removedWindowContainer.makeAnimationLeash() - .setName("BackPreview Leash") + .setName("BackPreview Leash for " + removedWindowContainer) .setHidden(false) .setBLASTLayer() .build(); @@ -158,7 +171,7 @@ class BackNavigationController { } SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot") + .setName("BackPreview Screenshot for " + prev) .setParent(animationLeashParent) .setHidden(false) .setBLASTLayer(); @@ -171,6 +184,10 @@ class BackNavigationController { screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); } } + + // The Animation leash needs to be above the screenshot surface, but the animation leash + // needs to be added before to be in the synchronized block. + tx.setLayer(animLeash, 1); tx.apply(); WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; @@ -183,13 +200,16 @@ class BackNavigationController { return null; } + RemoteCallback onBackNavigationDone = new RemoteCallback( + result -> resetSurfaces(finalRemovedWindowContainer + )); return new BackNavigationInfo(backType, animLeash, screenshotSurface, screenshotBuffer, taskWindowConfiguration, - new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer - ))); + onBackNavigationDone, + callback); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 2ae2b4370522..f26c53938077 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -885,8 +885,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public void setOnBackInvokedCallback(IWindow iWindow, - IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException { - // TODO: Set the callback to the WindowState of the window. + public void setOnBackInvokedCallback(IWindow window, + IOnBackInvokedCallback onBackInvokedCallback) throws RemoteException { + synchronized (mService.mGlobalLock) { + WindowState windowState = mService.windowForClientLocked(this, window, false); + if (windowState == null) { + Slog.e(TAG_WM, + "setOnBackInvokedCallback(): Can't find window state for window:" + window); + } else { + windowState.setOnBackInvokedCallback(onBackInvokedCallback); + } + } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 97735a29b027..b84ef773f85a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2197,10 +2197,6 @@ class Task extends TaskFragment { final Rect taskBounds = getBounds(); width = taskBounds.width(); height = taskBounds.height(); - - final int outset = getTaskOutset(); - width += 2 * outset; - height += 2 * outset; } if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { return; @@ -2209,28 +2205,6 @@ class Task extends TaskFragment { mLastSurfaceSize.set(width, height); } - /** - * Calculate an amount by which to expand the task bounds in each direction. - * Used to make room for shadows in the pinned windowing mode. - */ - int getTaskOutset() { - // If we are drawing shadows on the task then don't outset the root task. - if (mWmService.mRenderShadowsInCompositor) { - return 0; - } - DisplayContent displayContent = getDisplayContent(); - if (inPinnedWindowingMode() && displayContent != null) { - final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); - - // We multiply by two to match the client logic for converting view elevation - // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets} - return (int) Math.ceil( - mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, displayMetrics) - * 2); - } - return 0; - } - @VisibleForTesting Point getLastSurfaceSize() { return mLastSurfaceSize; @@ -4338,7 +4312,7 @@ class Task extends TaskFragment { */ private void updateShadowsRadius(boolean taskIsFocused, SurfaceControl.Transaction pendingTransaction) { - if (!mWmService.mRenderShadowsInCompositor || !isRootTask()) return; + if (!isRootTask()) return; final float newShadowRadius = getShadowRadius(taskIsFocused); if (mShadowRadius != newShadowRadius) { @@ -6015,14 +5989,6 @@ class Task extends TaskFragment { scheduleAnimation(); } - @Override - void getRelativePosition(Point outPos) { - super.getRelativePosition(outPos); - final int outset = getTaskOutset(); - outPos.x -= outset; - outPos.y -= outset; - } - private Point getRelativePosition() { Point position = new Point(); getRelativePosition(position); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1167cb531c76..22c430ff8016 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -462,11 +462,6 @@ public class WindowManagerService extends IWindowManager.Stub int mVr2dDisplayId = INVALID_DISPLAY; boolean mVrModeEnabled = false; - /* If true, shadows drawn around the window will be rendered by the system compositor. If - * false, shadows will be drawn by the client by setting an elevation on the root view and - * the contents will be inset by the shadow radius. */ - boolean mRenderShadowsInCompositor = false; - /** * Tracks a map of input tokens to info that is used to decide whether to intercept * a key event. @@ -811,8 +806,6 @@ public class WindowManagerService extends IWindowManager.Stub resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL); resolver.registerContentObserver(mDevEnableNonResizableMultiWindowUri, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, - UserHandle.USER_ALL); resolver.registerContentObserver(mDisplaySettingsPathUri, false, this, UserHandle.USER_ALL); } @@ -853,11 +846,6 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mRenderShadowsInCompositorUri.equals(uri)) { - setShadowRenderer(); - return; - } - if (mDisplaySettingsPathUri.equals(uri)) { updateDisplaySettingsLocation(); return; @@ -972,11 +960,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void setShadowRenderer() { - mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; - } - PowerManager mPowerManager; PowerManagerInternal mPowerManagerInternal; @@ -1395,7 +1378,6 @@ public class WindowManagerService extends IWindowManager.Stub float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha}; SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ, lightRadius); - setShadowRenderer(); } /** @@ -3855,14 +3837,20 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned - * bitmap will be full size and will not include any secure content. + * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. * - * @param taskId The task ID of the task for which a snapshot is requested. + * @param taskId The task ID of the task for which a Bitmap is requested. + * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with + * arguments for how to capture the Bitmap. The caller can + * specify any arguments, but this method will ensure that the + * specified task's SurfaceControl is used and the crop is set to + * the bounds of that task. * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could * not be generated. */ - @Nullable public Bitmap captureTaskBitmap(int taskId) { + @Nullable + public Bitmap captureTaskBitmap(int taskId, + @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) { if (mTaskSnapshotController.shouldDisableSnapshots()) { return null; } @@ -3876,9 +3864,7 @@ public class WindowManagerService extends IWindowManager.Stub task.getBounds(mTmpRect); final SurfaceControl sc = task.getSurfaceControl(); final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( - new SurfaceControl.LayerCaptureArgs.Builder(sc) - .setSourceCrop(mTmpRect) - .build()); + layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build()); if (buffer == null) { Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId); return null; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 79c64b19882d..0d72e9a567bc 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -106,6 +106,7 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; @@ -245,6 +246,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.window.ClientWindowFrames; +import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.KeyInterceptionInfo; @@ -845,6 +847,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } }; + /** + * @see #setOnBackInvokedCallback(IOnBackInvokedCallback) + */ + private IOnBackInvokedCallback mOnBackInvokedCallback; + @Override WindowState asWindowState() { return this; @@ -1061,6 +1068,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return true; } + /** + * Used by {@link android.window.WindowOnBackInvokedDispatcher} to set the callback to be + * called when a back navigation action is initiated. + * @see BackNavigationController + */ + void setOnBackInvokedCallback(@Nullable IOnBackInvokedCallback onBackInvokedCallback) { + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s", + this, onBackInvokedCallback); + mOnBackInvokedCallback = onBackInvokedCallback; + } + + @Nullable + IOnBackInvokedCallback getOnBackInvokedCallback() { + return mOnBackInvokedCallback; + } + interface PowerManagerWrapper { void wakeUp(long time, @WakeReason int reason, String details); @@ -5398,17 +5421,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outPoint.offset(-parentBounds.left, -parentBounds.top); } - Task rootTask = getRootTask(); - - // If we have root task outsets, that means the top-left - // will be outset, and we need to inset ourselves - // to account for it. If we actually have shadows we will - // then un-inset ourselves by the surfaceInsets. - if (rootTask != null) { - final int outset = rootTask.getTaskOutset(); - outPoint.offset(outset, outset); - } - // The surface size is larger than the window if the window has positive surface insets. transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets); outPoint.offset(-mTmpPoint.x, -mTmpPoint.y); diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 79d80366efd3..be0ddc155ae0 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -123,6 +123,18 @@ <xs:annotation name="nonnull"/> <xs:annotation name="final"/> </xs:element> + <!-- The minimum HDR video size at which high-brightness-mode is allowed to operate. + Default is 0.5 if not specified--> + <xs:element name="minimumHdrPercentOfScreen" type="nonNegativeDecimal" + minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> + <!-- This LUT specifies how to boost HDR brightness at given SDR brightness (nits). --> + <xs:element type="sdrHdrRatioMap" name="sdrHdrRatioMap" minOccurs="0" maxOccurs="1"> + <xs:annotation name="nullable"/> + <xs:annotation name="final"/> + </xs:element> </xs:all> <xs:attribute name="enabled" type="xs:boolean" use="optional"/> </xs:complexType> @@ -158,6 +170,14 @@ </xs:restriction> </xs:simpleType> + <!-- Maps to DisplayDeviceConfig.INTERPOLATION_* values. --> + <xs:simpleType name="interpolation"> + <xs:restriction base="xs:string"> + <xs:enumeration value="default"/> + <xs:enumeration value="linear"/> + </xs:restriction> + </xs:simpleType> + <xs:complexType name="thermalThrottling"> <xs:complexType> <xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap"> @@ -196,6 +216,7 @@ <xs:annotation name="final"/> </xs:element> </xs:sequence> + <xs:attribute name="interpolation" type="interpolation" use="optional"/> </xs:complexType> <xs:complexType name="point"> @@ -211,6 +232,28 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="sdrHdrRatioMap"> + <xs:sequence> + <xs:element name="point" type="sdrHdrRatioPoint" maxOccurs="unbounded" minOccurs="2"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="sdrHdrRatioPoint"> + <xs:sequence> + <xs:element type="nonNegativeDecimal" name="sdrNits"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element type="nonNegativeDecimal" name="hdrRatio"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:simpleType name="nonNegativeDecimal"> <xs:restriction base="xs:decimal"> <xs:minInclusive value="0.0"/> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 0b7df4d0bc7c..2890d686186e 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -90,23 +90,35 @@ package com.android.server.display.config { ctor public HighBrightnessMode(); method @NonNull public final boolean getAllowInLowPowerMode_all(); method public boolean getEnabled(); + method @Nullable public final java.math.BigDecimal getMinimumHdrPercentOfScreen_all(); method @NonNull public final java.math.BigDecimal getMinimumLux_all(); method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all(); + method @Nullable public final com.android.server.display.config.SdrHdrRatioMap getSdrHdrRatioMap_all(); method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatusLimit_all(); method public com.android.server.display.config.HbmTiming getTiming_all(); method @NonNull public final java.math.BigDecimal getTransitionPoint_all(); method public final void setAllowInLowPowerMode_all(@NonNull boolean); method public void setEnabled(boolean); + method public final void setMinimumHdrPercentOfScreen_all(@Nullable java.math.BigDecimal); method public final void setMinimumLux_all(@NonNull java.math.BigDecimal); method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange); + method public final void setSdrHdrRatioMap_all(@Nullable com.android.server.display.config.SdrHdrRatioMap); method public final void setThermalStatusLimit_all(@NonNull com.android.server.display.config.ThermalStatus); method public void setTiming_all(com.android.server.display.config.HbmTiming); method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal); } + public enum Interpolation { + method public String getRawName(); + enum_constant public static final com.android.server.display.config.Interpolation _default; + enum_constant public static final com.android.server.display.config.Interpolation linear; + } + public class NitsMap { ctor public NitsMap(); + method public com.android.server.display.config.Interpolation getInterpolation(); method @NonNull public final java.util.List<com.android.server.display.config.Point> getPoint(); + method public void setInterpolation(com.android.server.display.config.Interpolation); } public class Point { @@ -125,6 +137,19 @@ package com.android.server.display.config { method public final void setMinimum(java.math.BigInteger); } + public class SdrHdrRatioMap { + ctor public SdrHdrRatioMap(); + method @NonNull public final java.util.List<com.android.server.display.config.SdrHdrRatioPoint> getPoint(); + } + + public class SdrHdrRatioPoint { + ctor public SdrHdrRatioPoint(); + method @NonNull public final java.math.BigDecimal getHdrRatio(); + method @NonNull public final java.math.BigDecimal getSdrNits(); + method public final void setHdrRatio(@NonNull java.math.BigDecimal); + method public final void setSdrNits(@NonNull java.math.BigDecimal); + } + public class SensorDetails { ctor public SensorDetails(); method @Nullable public final String getName(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d0c861fa912e..c2dec067c870 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -154,6 +154,7 @@ import com.android.server.os.NativeTombstoneManagerService; import com.android.server.os.SchedulingPolicyService; import com.android.server.people.PeopleService; import com.android.server.pm.ApexManager; +import com.android.server.pm.ApexSystemServiceInfo; import com.android.server.pm.CrossProfileAppsService; import com.android.server.pm.DataLoaderManagerService; import com.android.server.pm.DynamicCodeLoggingService; @@ -224,8 +225,8 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; +import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Timer; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; @@ -1392,7 +1393,6 @@ public final class SystemServer implements Dumpable { DynamicSystemService dynamicSystem = null; IStorageManager storageManager = null; NetworkManagementService networkManagement = null; - IpSecService ipSecService = null; VpnManagerService vpnManager = null; VcnManagementService vcnManagement = null; NetworkStatsService networkStats = null; @@ -1472,7 +1472,7 @@ public final class SystemServer implements Dumpable { // TelecomLoader hooks into classes with defined HFP logic, // so check for either telephony or microphone. if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) || - mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { t.traceBegin("StartTelecomLoaderService"); mSystemServiceManager.startService(TelecomLoaderService.class); t.traceEnd(); @@ -1480,7 +1480,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartTelephonyRegistry"); telephonyRegistry = new TelephonyRegistry( - context, new TelephonyRegistry.ConfigurationProvider()); + context, new TelephonyRegistry.ConfigurationProvider()); ServiceManager.addService("telephony.registry", telephonyRegistry); t.traceEnd(); @@ -1897,15 +1897,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartIpSecService"); - try { - ipSecService = IpSecService.create(context); - ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); - } catch (Throwable e) { - reportWtf("starting IpSec Service", e); - } - t.traceEnd(); - t.traceBegin("StartFontManagerService"); mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode)); t.traceEnd(); @@ -2792,7 +2783,6 @@ public final class SystemServer implements Dumpable { final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; - final IpSecService ipSecServiceF = ipSecService; final VpnManagerService vpnManagerF = vpnManager; final VcnManagementService vcnManagementF = vcnManagement; final WindowManagerService windowManagerF = wm; @@ -2884,15 +2874,6 @@ public final class SystemServer implements Dumpable { .networkScoreAndNetworkManagementServiceReady(); } t.traceEnd(); - t.traceBegin("MakeIpSecServiceReady"); - try { - if (ipSecServiceF != null) { - ipSecServiceF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making IpSec Service ready", e); - } - t.traceEnd(); t.traceBegin("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) { @@ -3017,7 +2998,9 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("MakeTelephonyRegistryReady"); try { - if (telephonyRegistryF != null) telephonyRegistryF.systemRunning(); + if (telephonyRegistryF != null) { + telephonyRegistryF.systemRunning(); + } } catch (Throwable e) { reportWtf("Notifying TelephonyRegistry running", e); } @@ -3082,10 +3065,12 @@ public final class SystemServer implements Dumpable { */ private void startApexServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startApexServices"); - Map<String, String> services = ApexManager.getInstance().getApexSystemServices(); - // TODO(satayev): introduce android:order for services coming the same apexes - for (String name : new TreeSet<>(services.keySet())) { - String jarPath = services.get(name); + // TODO(b/192880996): get the list from "android" package, once the manifest entries + // are migrated to system manifest. + List<ApexSystemServiceInfo> services = ApexManager.getInstance().getApexSystemServices(); + for (ApexSystemServiceInfo info : services) { + String name = info.getName(); + String jarPath = info.getJarPath(); t.traceBegin("starting " + name); if (TextUtils.isEmpty(jarPath)) { mSystemServiceManager.startService(name); diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp index 16d624199d5a..0a9b7b1302cc 100644 --- a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp +++ b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp @@ -32,7 +32,7 @@ apex_test { name: "test_com.android.server", manifest: "manifest.json", androidManifest: "AndroidManifest.xml", - java_libs: ["FakeApexSystemService"], + java_libs: ["FakeApexSystemServices"], file_contexts: ":apex.test-file_contexts", key: "test_com.android.server.key", updatable: false, diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml index eb741cad4ad9..6bec28463dc3 100644 --- a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml +++ b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml @@ -21,21 +21,29 @@ <application android:hasCode="false" android:testOnly="true"> <apex-system-service android:name="com.android.server.testing.FakeApexSystemService" - android:path="/apex/test_com.android.server/javalib/FakeApexSystemService.jar" - android:minSdkVersion="30"/> + android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar" + android:minSdkVersion="30" + /> + + <apex-system-service + android:name="com.android.server.testing.FakeApexSystemService2" + android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar" + android:minSdkVersion="30" + android:initOrder="1" + /> <!-- Always inactive system service, since maxSdkVersion is low --> <apex-system-service - android:name="com.android.apex.test.OldApexSystemService" - android:path="/apex/com.android.apex.test/javalib/fake.jar" + android:name="com.android.server.testing.OldApexSystemService" + android:path="/apex/test_com.android.server/javalib/fake.jar" android:minSdkVersion="1" android:maxSdkVersion="1" /> <!-- Always inactive system service, since minSdkVersion is high --> <apex-system-service - android:name="com.android.apex.test.NewApexSystemService" - android:path="/apex/com.android.apex.test/javalib/fake.jar" + android:name="com.android.server.testing.NewApexSystemService" + android:path="/apex/test_com.android.server/javalib/fake.jar" android:minSdkVersion="999999" /> </application> diff --git a/services/tests/apexsystemservices/service/Android.bp b/services/tests/apexsystemservices/services/Android.bp index 9d04f39f2237..477ea4cdad37 100644 --- a/services/tests/apexsystemservices/service/Android.bp +++ b/services/tests/apexsystemservices/services/Android.bp @@ -8,7 +8,7 @@ package { } java_library { - name: "FakeApexSystemService", + name: "FakeApexSystemServices", srcs: ["**/*.java"], sdk_version: "system_server_current", libs: [ diff --git a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java index 4947c3455cc7..4947c3455cc7 100644 --- a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java +++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java diff --git a/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java new file mode 100644 index 000000000000..e83343b9c996 --- /dev/null +++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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.testing; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.server.SystemService; + +/** + * A fake system service that just logs when it is started. + */ +public class FakeApexSystemService2 extends SystemService { + + private static final String TAG = "FakeApexSystemService"; + + public FakeApexSystemService2(@NonNull Context context) { + super(context); + } + + @Override + public void onStart() { + Log.d(TAG, "FakeApexSystemService2 onStart"); + } +} diff --git a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java index 2b453a9265bb..10635a138eb9 100644 --- a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java +++ b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java @@ -37,9 +37,15 @@ import org.junit.rules.RuleChain; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + @RunWith(DeviceJUnit4ClassRunner.class) public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { + private static final int REBOOT_TIMEOUT = 1 * 60 * 1000; + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); private final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice); @@ -67,7 +73,7 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @Test - public void noApexSystemServerStartsWithoutApex() throws Exception { + public void testNoApexSystemServiceStartsWithoutApex() throws Exception { mPreparer.reboot(); assertThat(getFakeApexSystemServiceLogcat()) @@ -75,20 +81,55 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @Test - public void apexSystemServerStarts() throws Exception { + public void testApexSystemServiceStarts() throws Exception { // Pre-install the apex String apex = "test_com.android.server.apex"; mPreparer.pushResourceFile(apex, "/system/apex/" + apex); // Reboot activates the apex mPreparer.reboot(); + mDevice.waitForBootComplete(REBOOT_TIMEOUT); + assertThat(getFakeApexSystemServiceLogcat()) .contains("FakeApexSystemService onStart"); } + @Test + public void testInitOrder() throws Exception { + // Pre-install the apex + String apex = "test_com.android.server.apex"; + mPreparer.pushResourceFile(apex, "/system/apex/" + apex); + // Reboot activates the apex + mPreparer.reboot(); + + mDevice.waitForBootComplete(REBOOT_TIMEOUT); + + assertThat(getFakeApexSystemServiceLogcat().lines() + .map(ApexSystemServicesTestCases::getDebugMessage) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .containsExactly( + // Second service has a higher initOrder and must be started first + "FakeApexSystemService2 onStart", + "FakeApexSystemService onStart" + ) + .inOrder(); + } + private String getFakeApexSystemServiceLogcat() throws DeviceNotAvailableException { return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "FakeApexSystemService:D", "*:S"); } + private static final Pattern DEBUG_MESSAGE = + Pattern.compile("(FakeApexSystemService[0-9]* onStart)"); + + private static String getDebugMessage(String logcatLine) { + return DEBUG_MESSAGE.matcher(logcatLine) + .results() + .map(m -> m.group(1)) + .findFirst() + .orElse(null); + } + } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 08de62bc6537..7f571195f5f8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -60,6 +60,7 @@ import android.service.games.IGameServiceController; import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; +import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import androidx.test.filters.SmallTest; @@ -746,6 +747,11 @@ public final class GameServiceProviderInstanceImplTest { mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); + IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); @@ -754,18 +760,28 @@ public final class GameServiceProviderInstanceImplTest { GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, result.getStatus()); - verify(mMockWindowManagerService).captureTaskBitmap(10); + + verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any()); } @Test public void takeScreenshot_success() throws Exception { - when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP); + SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class); + SurfaceControl[] excludeLayers = new SurfaceControl[1]; + excludeLayers[0] = mockOverlaySurfaceControl; + when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); + when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); + IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index 24a47516a366..f352de4ea54e 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -36,8 +36,11 @@ import android.util.Spline; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.display.whitebalance.DisplayWhiteBalanceController; + import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import java.util.Arrays; @@ -147,11 +150,14 @@ public class BrightnessMappingStrategyTest { private static final float TOLERANCE = 0.0001f; + @Mock + DisplayWhiteBalanceController mMockDwbc; + @Test public void testSimpleStrategyMappingAtControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1, @@ -166,7 +172,7 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyMappingBetweenControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -181,7 +187,7 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyIgnoresNewConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; @@ -196,7 +202,7 @@ public class BrightnessMappingStrategyTest { public void testSimpleStrategyIgnoresNullConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); strategy.setBrightnessConfiguration(null); final int N = DISPLAY_LEVELS_BACKLIGHT.length; @@ -210,7 +216,7 @@ public class BrightnessMappingStrategyTest { public void testPhysicalStrategyMappingAtControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1], @@ -227,7 +233,7 @@ public class BrightnessMappingStrategyTest { public void testPhysicalStrategyMappingBetweenControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", physical); Spline brightnessToNits = Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS); @@ -244,7 +250,7 @@ public class BrightnessMappingStrategyTest { public void testPhysicalStrategyUsesNewConfigurations() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); final float[] lux = { 0f, 1f }; final float[] nits = { @@ -269,7 +275,7 @@ public class BrightnessMappingStrategyTest { public void testPhysicalStrategyRecalculateSplines() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f; @@ -301,7 +307,7 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @@ -314,13 +320,13 @@ public class BrightnessMappingStrategyTest { lux[idx+1] = tmp; Resources res = createResources(lux, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx+1]; res = createResources(lux, DISPLAY_LEVELS_NITS); - strategy = BrightnessMappingStrategy.create(res, ddc); + strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); } @@ -333,11 +339,11 @@ public class BrightnessMappingStrategyTest { lux[lux.length - 1] = lux[lux.length - 2] + 1; Resources res = createResources(lux, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); - strategy = BrightnessMappingStrategy.create(res, ddc); + strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // Extra backlight level @@ -345,14 +351,14 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; res = createResources(LUX_LEVELS, backlight); - strategy = BrightnessMappingStrategy.create(res, ddc); + strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); // Extra nits level final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); nits[nits.length - 1] = nits[nits.length - 2] + 1; res = createResources(LUX_LEVELS, nits); - strategy = BrightnessMappingStrategy.create(res, ddc); + strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(strategy); } @@ -361,17 +367,17 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, DISPLAY_LEVELS_NITS); - physical = BrightnessMappingStrategy.create(res, ddc); + physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, DISPLAY_LEVELS_NITS); - physical = BrightnessMappingStrategy.create(res, ddc); + physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc); assertNull(physical); } @@ -380,10 +386,10 @@ public class BrightnessMappingStrategyTest { Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, DISPLAY_LEVELS_NITS); DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc)); } @Test @@ -394,7 +400,7 @@ public class BrightnessMappingStrategyTest { // Create an idle mode bms // This will fail if it tries to fetch the wrong configuration. BrightnessMappingStrategy bms = BrightnessMappingStrategy.createForIdleMode(res, ddc, - null); + mMockDwbc); assertNotNull("BrightnessMappingStrategy should not be null", bms); // Ensure that the config is the one we set @@ -586,7 +592,8 @@ public class BrightnessMappingStrategyTest { Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, + mMockDwbc); // Let's start with a validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -614,7 +621,8 @@ public class BrightnessMappingStrategyTest { final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, + mMockDwbc); // Validity check: assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); @@ -639,7 +647,8 @@ public class BrightnessMappingStrategyTest { // just make sure the adjustment reflects the change. Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, + mMockDwbc); assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); strategy.addUserDataPoint(2500, 1.0f); assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); @@ -660,7 +669,8 @@ public class BrightnessMappingStrategyTest { final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); DisplayDeviceConfig ddc = createDdc(); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc, + mMockDwbc); // Validity, as per tradition: assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */); assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java index 6203c2f54f07..53fa3e2db376 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -22,11 +22,14 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; + import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; import static com.android.server.display.AutomaticBrightnessController .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE; +import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT; + import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; import static org.junit.Assert.assertEquals; @@ -111,7 +114,8 @@ public class HighBrightnessModeControllerTest { private static final HighBrightnessModeData DEFAULT_HBM_DATA = new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS, TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS, - THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE); + THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE, + HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT); @Before public void setUp() { @@ -136,7 +140,7 @@ public class HighBrightnessModeControllerTest { initHandler(null); final HighBrightnessModeController hbmc = new HighBrightnessModeController( mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, - mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy); + mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f); } @@ -146,7 +150,7 @@ public class HighBrightnessModeControllerTest { initHandler(null); final HighBrightnessModeController hbmc = new HighBrightnessModeController( mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, - mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy); + mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy); hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); @@ -703,7 +707,7 @@ public class HighBrightnessModeControllerTest { initHandler(clock); return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, - DEFAULT_HBM_DATA, () -> {}, mContextSpy); + DEFAULT_HBM_DATA, null, () -> {}, mContextSpy); } private void initHandler(OffsettableClock clock) { diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 7f7c716bc1f0..2f5993d1d989 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -61,7 +61,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Map; +import java.util.List; @SmallTest @Presubmit @@ -136,9 +136,10 @@ public class ApexManagerTest { mApexManager.scanApexPackagesTraced(mPackageParser2, ParallelPackageParser.makeExecutorService()); - Map<String, String> services = mApexManager.getApexSystemServices(); + List<ApexSystemServiceInfo> services = mApexManager.getApexSystemServices(); assertThat(services).hasSize(1); - assertThat(services).containsKey("com.android.apex.test.ApexSystemService"); + assertThat(services.stream().map(ApexSystemServiceInfo::getName).findFirst().orElse(null)) + .matches("com.android.apex.test.ApexSystemService"); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 687779d686d1..fb3a6264169a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.window.BackNavigationInfo.typeToString; import static com.google.common.truth.Truth.assertThat; @@ -71,7 +72,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backTypeCrossActivityWhenBackToPreviousActivity() { Task task = createTopTaskWithActivity(); - mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task)); + mAtm.setFocusedTask(task.mTaskId, + createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord); BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); @@ -85,7 +87,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfoFullyPopulated() { Task task = createTopTaskWithActivity(); - createActivityRecord(task); + createAppWindow(task, FIRST_APPLICATION_WINDOW, "window"); // We need a mock screenshot so TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController(); @@ -115,6 +117,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { private Task createTopTaskWithActivity() { Task task = createTask(mDefaultDisplay); ActivityRecord record = createActivityRecord(task); + createWindow(null, FIRST_APPLICATION_WINDOW, record, "window"); when(record.mSurfaceControl.isValid()).thenReturn(true); mAtm.setFocusedTask(task.mTaskId, record); return task; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 65b5cf5f13c4..dcaa511bf7cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -80,7 +80,6 @@ import android.app.IApplicationThread; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.pm.ActivityInfo; -import android.graphics.Rect; import android.os.Binder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -231,34 +230,6 @@ public class RootTaskTests extends WindowTestsBase { } @Test - public void testTaskOutset() { - final Task task = createTask(mDisplayContent); - final int taskOutset = 10; - spyOn(task); - doReturn(taskOutset).when(task).getTaskOutset(); - doReturn(true).when(task).inMultiWindowMode(); - - // Mock the resolved override windowing mode to non-fullscreen - final WindowConfiguration windowConfiguration = - task.getResolvedOverrideConfiguration().windowConfiguration; - spyOn(windowConfiguration); - doReturn(WINDOWING_MODE_MULTI_WINDOW) - .when(windowConfiguration).getWindowingMode(); - - // Prevent adjust task dimensions - doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any()); - - final Rect taskBounds = new Rect(200, 200, 800, 1000); - // Update surface position and size by the given bounds. - task.setBounds(taskBounds); - - assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x); - assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y); - assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x); - assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y); - } - - @Test public void testActivityAndTaskGetsProperType() { final Task task1 = new TaskBuilder(mSupervisor).build(); ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent); diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index e88106cb95fe..86b98f1cbe79 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -127,7 +127,9 @@ public class Annotation { ApnSetting.TYPE_EMERGENCY, ApnSetting.TYPE_MCX, ApnSetting.TYPE_XCAP, - // ApnSetting.TYPE_ENTERPRISE + ApnSetting.TYPE_BIP, + ApnSetting.TYPE_VSIM, + ApnSetting.TYPE_ENTERPRISE }) @Retention(RetentionPolicy.SOURCE) public @interface ApnType { @@ -707,6 +709,9 @@ public class Annotation { NetworkCapabilities.NET_CAPABILITY_VSIM, NetworkCapabilities.NET_CAPABILITY_BIP, NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT, + NetworkCapabilities.NET_CAPABILITY_MMTEL, + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY, + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH }) public @interface NetCapability { } @@ -721,4 +726,16 @@ public class Annotation { NetworkAgent.VALIDATION_STATUS_NOT_VALID }) public @interface ValidationStatus {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = { + NetworkCapabilities.NET_ENTERPRISE_ID_1, + NetworkCapabilities.NET_ENTERPRISE_ID_2, + NetworkCapabilities.NET_ENTERPRISE_ID_3, + NetworkCapabilities.NET_ENTERPRISE_ID_4, + NetworkCapabilities.NET_ENTERPRISE_ID_5 + }) + + public @interface EnterpriseId {} } diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index a166a5d6404c..fa1bae41f433 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -26,10 +26,10 @@ import android.annotation.SystemApi; import android.net.NetworkCapabilities; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetCapability; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.NetworkTypeBitMask; +import android.telephony.data.ApnSetting.ApnType; import android.telephony.data.ApnSetting.AuthType; import android.text.TextUtils; @@ -245,8 +245,7 @@ public final class DataProfile implements Parcelable { * @return The supported APN types bitmask. * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnTypeBitmask()} instead. */ - @Deprecated - public @ApnType int getSupportedApnTypesBitmask() { + @Deprecated public @ApnType int getSupportedApnTypesBitmask() { if (mApnSetting != null) { return mApnSetting.getApnTypeBitmask(); } @@ -425,6 +424,12 @@ public final class DataProfile implements Parcelable { return ApnSetting.TYPE_MCX; case NetworkCapabilities.NET_CAPABILITY_IA: return ApnSetting.TYPE_IA; + case NetworkCapabilities.NET_CAPABILITY_BIP: + return ApnSetting.TYPE_BIP; + case NetworkCapabilities.NET_CAPABILITY_VSIM: + return ApnSetting.TYPE_VSIM; + case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE: + return ApnSetting.TYPE_ENTERPRISE; default: return ApnSetting.TYPE_NONE; } diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java index 2178fc1717b9..66dcf8f49e9c 100644 --- a/telephony/java/android/telephony/data/TrafficDescriptor.java +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -21,8 +21,13 @@ import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; +import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection @@ -31,24 +36,215 @@ import java.util.Objects; * not specify the end point to be used for the data call. */ public final class TrafficDescriptor implements Parcelable { + /** + * The OS/App id + * + * @hide + */ + public static final class OsAppId { + /** + * OSId for "Android", using UUID version 5 with namespace ISO OSI. + * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching. + */ + public static final UUID ANDROID_OS_ID = + UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47"); + + /** + * Allowed app ids. + */ + // The following app ids are the only apps id Android supports. OEMs or vendors are + // prohibited to modify/extend the allowed list, especially passing the real package name to + // the network. + private static final Set<String> ALLOWED_APP_IDS = Set.of( + "ENTERPRISE", "PRIORITIZE_LATENCY", "PRIORITIZE_BANDWIDTH", "CBS" + ); + + /** OS id in UUID format. */ + private final @NonNull UUID mOsId; + + /** + * App id in string format. Note that Android will not allow use specific app id. This must + * be a category/capability identifier. + */ + private final @NonNull String mAppId; + + /** + * The differentiator when multiple traffic descriptor has the same OS and app id. Must be + * greater than 1. + */ + private final int mDifferentiator; + + /** + * Constructor + * + * @param osId OS id in UUID format. + * @param appId App id in string format. Note that Android will not allow use specific app + * id. This must be a category/capability identifier. + */ + public OsAppId(@NonNull UUID osId, @NonNull String appId) { + this(osId, appId, 1); + } + + /** + * Constructor + * + * @param osId OS id in UUID format. + * @param appId App id in string format. Note that Android will not allow use specific app + * id. This must be a category/capability identifier. + * @param differentiator The differentiator when multiple traffic descriptor has the same + * OS and app id. Must be greater than 0. + */ + public OsAppId(@NonNull UUID osId, @NonNull String appId, int differentiator) { + Objects.requireNonNull(osId); + Objects.requireNonNull(appId); + if (differentiator < 1) { + throw new IllegalArgumentException("Invalid differentiator " + differentiator); + } + + mOsId = osId; + mAppId = appId; + mDifferentiator = differentiator; + } + + /** + * Constructor from raw byte array. + * + * @param rawOsAppId The raw OS/App id. + */ + public OsAppId(@NonNull byte[] rawOsAppId) { + try { + ByteBuffer bb = ByteBuffer.wrap(rawOsAppId); + // OS id is the first 16 bytes. + mOsId = new UUID(bb.getLong(), bb.getLong()); + // App id length is 1 byte. + int appIdLen = bb.get(); + // The remaining is the app id + differentiator. + byte[] appIdAndDifferentiator = new byte[appIdLen]; + bb.get(appIdAndDifferentiator, 0, appIdLen); + // Extract trailing numbers, for example, "ENTERPRISE", "ENTERPRISE3". + String appIdAndDifferentiatorStr = new String(appIdAndDifferentiator); + Pattern pattern = Pattern.compile("[^0-9]+([0-9]+)$"); + Matcher matcher = pattern.matcher(new String(appIdAndDifferentiator)); + if (matcher.find()) { + mDifferentiator = Integer.parseInt(matcher.group(1)); + mAppId = appIdAndDifferentiatorStr.replace(matcher.group(1), ""); + } else { + mDifferentiator = 1; + mAppId = appIdAndDifferentiatorStr; + } + } catch (Exception e) { + throw new IllegalArgumentException("Failed to decode " + (rawOsAppId != null + ? new BigInteger(1, rawOsAppId).toString(16) : null)); + } + } + + /** + * @return The OS id in UUID format. + */ + public @NonNull UUID getOsId() { + return mOsId; + } + + /** + * @return App id in string format. Note that Android will not allow use specific app id. + * This must be a category/capability identifier. + */ + public @NonNull String getAppId() { + return mAppId; + } + + /** + * @return The differentiator when multiple traffic descriptor has the same OS and app id. + * Must be greater than 1. + */ + public int getDifferentiator() { + return mDifferentiator; + } + + /** + * @return OS/App id in raw byte format. + */ + public @NonNull byte[] getBytes() { + byte[] osAppId = (mAppId + (mDifferentiator > 1 ? mDifferentiator : "")).getBytes(); + // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId + ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length); + bb.putLong(mOsId.getMostSignificantBits()); + bb.putLong(mOsId.getLeastSignificantBits()); + bb.put((byte) osAppId.length); + bb.put(osAppId); + return bb.array(); + } + + @Override + public String toString() { + return "[OsAppId: OS=" + mOsId + ", App=" + mAppId + ", differentiator=" + + mDifferentiator + ", raw=" + + new BigInteger(1, getBytes()).toString(16) + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OsAppId osAppId = (OsAppId) o; + return mDifferentiator == osAppId.mDifferentiator && mOsId.equals(osAppId.mOsId) + && mAppId.equals(osAppId.mAppId); + } + + @Override + public int hashCode() { + return Objects.hash(mOsId, mAppId, mDifferentiator); + } + } + private final String mDnn; - private final byte[] mOsAppId; + private final OsAppId mOsAppId; private TrafficDescriptor(@NonNull Parcel in) { mDnn = in.readString(); - mOsAppId = in.createByteArray(); + byte[] osAppIdBytes = in.createByteArray(); + OsAppId osAppId = null; + if (osAppIdBytes != null) { + osAppId = new OsAppId(osAppIdBytes); + } + mOsAppId = osAppId; + + enforceAllowedIds(); } /** * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 * @param dnn optional DNN, which must be used for traffic matching, if present - * @param osAppId OsId + osAppId of the traffic descriptor + * @param osAppIdRawBytes Raw bytes of OsId + osAppId of the traffic descriptor * * @hide */ - public TrafficDescriptor(String dnn, byte[] osAppId) { + public TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes) { mDnn = dnn; + OsAppId osAppId = null; + if (osAppIdRawBytes != null) { + osAppId = new OsAppId(osAppIdRawBytes); + } mOsAppId = osAppId; + + enforceAllowedIds(); + } + + /** + * Enforce the OS id and app id are in the allowed list. + * + * @throws IllegalArgumentException if ids are not allowed. + */ + private void enforceAllowedIds() { + if (mOsAppId != null && !mOsAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) { + throw new IllegalArgumentException("OS id " + mOsAppId.getOsId() + " does not match " + + OsAppId.ANDROID_OS_ID); + } + + if (mOsAppId != null && !OsAppId.ALLOWED_APP_IDS.contains(mOsAppId.getAppId())) { + throw new IllegalArgumentException("Illegal app id " + mOsAppId.getAppId() + + ". Only allowing one of the following " + OsAppId.ALLOWED_APP_IDS); + } } /** @@ -61,13 +257,13 @@ public final class TrafficDescriptor implements Parcelable { } /** - * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic - * category. It includes the OS Id component of the field as defined in the specs. - * @return the OS App ID of this traffic descriptor if one is included by the network, null - * otherwise. + * OsAppId identifies a broader traffic category. Although it names Os/App id, it only includes + * OS version with a general/broader category id used as app id. + * + * @return The id in byte format. {@code null} if not available. */ public @Nullable byte[] getOsAppId() { - return mOsAppId; + return mOsAppId != null ? mOsAppId.getBytes() : null; } @Override @@ -77,13 +273,13 @@ public final class TrafficDescriptor implements Parcelable { @NonNull @Override public String toString() { - return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}"; + return "TrafficDescriptor={mDnn=" + mDnn + ", " + mOsAppId + "}"; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mDnn); - dest.writeByteArray(mOsAppId); + dest.writeByteArray(mOsAppId != null ? mOsAppId.getBytes() : null); } public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR = @@ -104,7 +300,7 @@ public final class TrafficDescriptor implements Parcelable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TrafficDescriptor that = (TrafficDescriptor) o; - return Objects.equals(mDnn, that.mDnn) && Arrays.equals(mOsAppId, that.mOsAppId); + return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId); } @Override @@ -148,7 +344,7 @@ public final class TrafficDescriptor implements Parcelable { } /** - * Set the OS App ID (including OS Id as defind in the specs). + * Set the OS App ID (including OS Id as defined in the specs). * * @return The same instance of the builder. */ diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java index c25ace0c6a62..f367e404a35b 100644 --- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java +++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java @@ -34,7 +34,7 @@ public final class RcsClientConfiguration implements Parcelable { /**@hide*/ @StringDef(prefix = "RCS_PROFILE_", - value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3}) + value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4}) public @interface StringRcsProfile {} /** @@ -45,6 +45,10 @@ public final class RcsClientConfiguration implements Parcelable { * RCS profile UP 2.3 */ public static final String RCS_PROFILE_2_3 = "UP_2.3"; + /** + * RCS profile UP 2.4 + */ + public static final String RCS_PROFILE_2_4 = "UP_2.4"; private String mRcsVersion; private String mRcsProfile; @@ -58,8 +62,8 @@ public final class RcsClientConfiguration implements Parcelable { * @param rcsVersion The parameter identifies the RCS version supported * by the client. Refer to GSMA RCC.07 "rcs_version" parameter. * @param rcsProfile Identifies a fixed set of RCS services that are - * supported by the client. See {@link #RCS_PROFILE_1_0 } or - * {@link #RCS_PROFILE_2_3 } + * supported by the client. See {@link #RCS_PROFILE_1_0 }, + * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 } * @param clientVendor Identifies the vendor providing the RCS client. * @param clientVersion Identifies the RCS client version. Refer to GSMA * RCC.07 "client_version" parameter. @@ -80,8 +84,8 @@ public final class RcsClientConfiguration implements Parcelable { * @param rcsVersion The parameter identifies the RCS version supported * by the client. Refer to GSMA RCC.07 "rcs_version" parameter. * @param rcsProfile Identifies a fixed set of RCS services that are - * supported by the client. See {@link #RCS_PROFILE_1_0 } or - * {@link #RCS_PROFILE_2_3 } + * supported by the client. See {@link #RCS_PROFILE_1_0 }, + * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 } * @param clientVendor Identifies the vendor providing the RCS client. * @param clientVersion Identifies the RCS client version. Refer to GSMA * RCC.07 "client_version" parameter. diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index 566c725a3414..98d13e81551d 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -26,8 +26,16 @@ <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6600s" /> <option name="hidden-api-checks" value="false" /> + <option name="device-listeners" + value="com.android.server.wm.flicker.TraceFileReadyListener" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="(\w)+\.winscope" /> + <option name="pull-pattern-keys" value="(\w)+\.mp4" /> + <option name="collect-on-run-ended-only" value="false" /> + <option name="clean-up" value="true" /> + </metrics_collector> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index 8fe00297139a..b66c45c7c9f0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -19,11 +19,13 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.support.test.launcherhelper.ILauncherStrategy import android.support.test.launcherhelper.LauncherStrategyFactory +import android.view.Display import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper @@ -36,6 +38,10 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( .getInstance(instr) .launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { + + private val secondActivityComponent = + ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent() + fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) { val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY) val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT) @@ -47,8 +53,11 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( button.click() device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT) - wmHelper.waitForAppTransitionIdle() - wmHelper.waitForFullScreenApp(component) + wmHelper.waitForFullScreenApp(secondActivityComponent) + wmHelper.waitFor( + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), + WindowManagerConditionsFactory.hasLayersAnimating().negate() + ) } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index 648353e34f92..195af589d77c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -70,7 +70,7 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) { fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { - eachRun { + test { testApp.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(testApp.component) } |