diff options
283 files changed, 7076 insertions, 3257 deletions
diff --git a/Android.bp b/Android.bp index e03f8449891a..d5d5150886d4 100644 --- a/Android.bp +++ b/Android.bp @@ -155,32 +155,9 @@ java_library_with_nonpublic_deps { name: "framework-all", installable: false, static_libs: [ - "android.net.ipsec.ike.impl", + "all-framework-module-impl", "framework-minus-apex", - "framework-appsearch.impl", - "framework-connectivity.impl", - "framework-connectivity-tiramisu.impl", - "framework-graphics.impl", - "framework-mediaprovider.impl", - "framework-permission.impl", - "framework-permission-s.impl", - "framework-scheduling.impl", - "framework-sdkextensions.impl", - "framework-statsd.impl", - "framework-supplementalprocess.impl", - "framework-tethering.impl", - "framework-uwb.impl", - "framework-wifi.impl", - "updatable-media", ], - soong_config_variables: { - include_nonpublic_framework_api: { - static_libs: [ - "framework-auxiliary.impl", - "framework-supplementalapi.impl", - ], - }, - }, apex_available: ["//apex_available:platform"], sdk_version: "core_platform", visibility: [ @@ -308,6 +285,8 @@ java_defaults { include_dirs: [ "frameworks/av/aidl", "frameworks/native/libs/permission/aidl", + // TODO: remove when moved to the below package + "frameworks/base/packages/ConnectivityT/framework-t/aidl-export", "packages/modules/Connectivity/framework/aidl-export", ], }, @@ -416,7 +395,6 @@ java_library { static_libs: [ "app-compat-annotations", "framework-minus-apex", - "framework-appsearch.impl", // TODO(b/146218515): should be removed "framework-updatable-stubs-module_libs_api", ], sdk_version: "core_platform", @@ -557,6 +535,9 @@ stubs_defaults { include_dirs: [ "frameworks/av/aidl", "frameworks/native/libs/permission/aidl", + // TODO: remove when moved to the below package + "frameworks/base/packages/ConnectivityT/framework-t/aidl-export", + "packages/modules/Connectivity/framework/aidl-export", ], }, // These are libs from framework-internal-utils that are required (i.e. being referenced) diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index f098e10ef020..cea19451f005 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -2768,7 +2768,7 @@ public class JobSchedulerService extends com.android.server.SystemService return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS); - } else if (job.getEffectivePriority() == JobInfo.PRIORITY_HIGH) { + } else if (job.getEffectivePriority() >= JobInfo.PRIORITY_HIGH) { return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS; } else { return mConstants.RUNTIME_MIN_GUARANTEE_MS; diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index b4edd39894f9..2c2af28c7560 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -66,8 +66,8 @@ java_library { }, visibility: [ "//frameworks/av/apex:__subpackages__", - "//frameworks/base", // For framework-all "//frameworks/base/apex/media/service", + "//frameworks/base/api", // For framework-all ], } diff --git a/api/api.go b/api/api.go index 17649e80e81a..5e5f60ee993f 100644 --- a/api/api.go +++ b/api/api.go @@ -27,6 +27,7 @@ import ( const art = "art.module.public.api" const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" +var core_libraries_modules = []string{art, conscrypt, i18n} // The intention behind this soong plugin is to generate a number of "merged" // API-related modules that would otherwise require a large amount of very @@ -199,9 +200,28 @@ func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { ctx.CreateModule(java.LibraryFactory, &props) } -func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) { +func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { + // This module is for the "framework-all" module, which should not include the core libraries. + modules = removeAll(modules, core_libraries_modules) + // TODO(b/214988855): remove the line below when framework-bluetooth has an impl jar. + modules = remove(modules, "framework-bluetooth") + props := libraryProps{} + props.Name = proptools.StringPtr("all-framework-module-impl") + props.Static_libs = transformArray(modules, "", ".impl") + // Media module's impl jar is called "updatable-media" + for i, v := range props.Static_libs { + if v == "framework-media.impl" { + props.Static_libs[i] = "updatable-media" + } + } + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) +} + +func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) { // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes. - modules = removeAll(modules, []string{art, conscrypt, i18n}) + modules = removeAll(modules, core_libraries_modules) props := libraryProps{} props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api") props.Static_libs = transformArray(modules, "", ".stubs.module_lib") @@ -269,7 +289,8 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { createMergedPublicStubs(ctx, bootclasspath) createMergedSystemStubs(ctx, bootclasspath) - createMergedModuleLibStubs(ctx, bootclasspath) + createMergedFrameworkModuleLibStubs(ctx, bootclasspath) + createMergedFrameworkImpl(ctx, bootclasspath) createMergedAnnotations(ctx, bootclasspath) 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 fe8b979a55e5..da14dfeec5c8 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -3074,6 +3074,7 @@ package android.accessibilityservice { method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); method public void setAccessibilityFocusAppearance(int, @ColorInt int); + method public void setAnimationScale(float); method public boolean setCacheEnabled(boolean); method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); @@ -4069,7 +4070,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); @@ -4875,6 +4876,7 @@ package android.app { field public static final int REASON_DEPENDENCY_DIED = 12; // 0xc field public static final int REASON_EXCESSIVE_RESOURCE_USAGE = 9; // 0x9 field public static final int REASON_EXIT_SELF = 1; // 0x1 + field public static final int REASON_FREEZER = 14; // 0xe field public static final int REASON_INITIALIZATION_FAILURE = 7; // 0x7 field public static final int REASON_LOW_MEMORY = 3; // 0x3 field public static final int REASON_OTHER = 13; // 0xd @@ -4970,7 +4972,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(); @@ -6949,6 +6951,7 @@ package android.app { method public boolean performGlobalAction(int); method public void revokeRuntimePermission(String, String); method public void revokeRuntimePermissionAsUser(String, String, android.os.UserHandle); + method public void setAnimationScale(float); method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener); method public boolean setRotation(int); method public void setRunAsMonkey(boolean); @@ -7611,7 +7614,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"; @@ -9824,7 +9827,6 @@ package android.content { field public static final String STATUS_BAR_SERVICE = "statusbar"; field public static final String STORAGE_SERVICE = "storage"; field public static final String STORAGE_STATS_SERVICE = "storagestats"; - field public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process"; field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final String TELECOM_SERVICE = "telecom"; field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; @@ -19677,10 +19679,13 @@ package android.media { method public android.media.AudioAttributes.Builder setUsage(int); } - public class AudioDescriptor { + public class AudioDescriptor implements android.os.Parcelable { + method public int describeContents(); method @NonNull public byte[] getDescriptor(); method public int getEncapsulationType(); method public int getStandard(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDescriptor> CREATOR; field public static final int STANDARD_EDID = 1; // 0x1 field public static final int STANDARD_NONE = 0; // 0x0 } @@ -20196,14 +20201,17 @@ package android.media { method @NonNull public android.media.AudioPresentation.Builder setProgramId(int); } - public class AudioProfile { + public class AudioProfile implements android.os.Parcelable { + method public int describeContents(); method @NonNull public int[] getChannelIndexMasks(); method @NonNull public int[] getChannelMasks(); method public int getEncapsulationType(); method public int getFormat(); method @NonNull public int[] getSampleRates(); + method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1; // 0x1 field public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioProfile> CREATOR; } public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { @@ -26392,75 +26400,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; @@ -43010,6 +42949,7 @@ package android.telephony { field public static final int ENCODING_16BIT = 3; // 0x3 field public static final int ENCODING_7BIT = 1; // 0x1 field public static final int ENCODING_8BIT = 2; // 0x2 + field public static final int ENCODING_KSC5601 = 4; // 0x4 field public static final int ENCODING_UNKNOWN = 0; // 0x0 field public static final String FORMAT_3GPP = "3gpp"; field public static final String FORMAT_3GPP2 = "3gpp2"; @@ -48973,7 +48913,7 @@ package android.view { } public interface OnBackInvokedDispatcherOwner { - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); } public interface OnReceiveContentListener { @@ -49401,7 +49341,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); @@ -49605,7 +49545,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 6e7bc765c157..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 } } @@ -192,7 +197,7 @@ package android.media { method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp(); method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio(); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); @@ -202,18 +207,18 @@ package android.media { field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } - public final class BtProfileConnectionInfo implements android.os.Parcelable { - method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int); - method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int); + public final class BluetoothProfileConnectionInfo implements android.os.Parcelable { + method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpInfo(boolean, int); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpSinkInfo(int); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createHearingAidInfo(boolean); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioInfo(boolean, boolean); method public int describeContents(); - method public boolean getIsLeOutput(); method public int getProfile(); - method public boolean getSuppressNoisyIntent(); method public int getVolume(); - method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean); - method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean); + method public boolean isLeOutput(); + method public boolean isSuppressNoisyIntent(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.media.BluetoothProfileConnectionInfo> CREATOR; } public class MediaMetadataRetriever implements java.lang.AutoCloseable { @@ -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..a97ccc91d1ff 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -166,6 +166,7 @@ package android { field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS"; field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING"; field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS"; + field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS"; field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY"; field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE"; @@ -1084,6 +1085,7 @@ package android.app.admin { method public boolean isDeviceManaged(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied(); + method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk(); method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk(); @@ -1096,6 +1098,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied(); + method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>); method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName); method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean); @@ -1174,6 +1177,13 @@ package android.app.admin { field public static final String UNDEFINED = "UNDEFINED"; } + public static final class DevicePolicyResources.Strings.Dialer { + field public static final String NOTIFICATION_INCOMING_WORK_CALL_TITLE = "Dialer.NOTIFICATION_INCOMING_WORK_CALL_TITLE"; + field public static final String NOTIFICATION_MISSED_WORK_CALL_TITLE = "Dialer.NOTIFICATION_MISSED_WORK_CALL_TITLE"; + field public static final String NOTIFICATION_ONGOING_WORK_CALL_TITLE = "Dialer.NOTIFICATION_ONGOING_WORK_CALL_TITLE"; + field public static final String NOTIFICATION_WIFI_WORK_CALL_LABEL = "Dialer.NOTIFICATION_WIFI_WORK_CALL_LABEL"; + } + public static final class DevicePolicyResources.Strings.DocumentsUi { field public static final String CANT_SAVE_TO_PERSONAL_MESSAGE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE"; field public static final String CANT_SAVE_TO_PERSONAL_TITLE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE"; @@ -2826,8 +2836,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"; @@ -5925,11 +5935,20 @@ package android.media { method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int); } + public class AudioDescriptor implements android.os.Parcelable { + ctor public AudioDescriptor(int, int, @NonNull byte[]); + } + public final class AudioDeviceAttributes implements android.os.Parcelable { ctor public AudioDeviceAttributes(@NonNull android.media.AudioDeviceInfo); ctor public AudioDeviceAttributes(int, int, @NonNull String); + ctor public AudioDeviceAttributes(int, int, @NonNull String, @NonNull String, @NonNull java.util.List<android.media.AudioProfile>, @NonNull java.util.List<android.media.AudioDescriptor>); method public int describeContents(); + method public boolean equalTypeAddress(@Nullable Object); method @NonNull public String getAddress(); + method @NonNull public java.util.List<android.media.AudioDescriptor> getAudioDescriptors(); + method @NonNull public java.util.List<android.media.AudioProfile> getAudioProfiles(); + method @NonNull public String getName(); method public int getRole(); method public int getType(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -6086,6 +6105,10 @@ package android.media { field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff } + public class AudioProfile implements android.os.Parcelable { + ctor public AudioProfile(int, @NonNull int[], @NonNull int[], @NonNull int[], int); + } + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException; method public static long getMaxSharedAudioHistoryMillis(); @@ -7952,7 +7975,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 +8359,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(); @@ -9964,7 +9970,8 @@ package android.permission { method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); - method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int); + method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int); + method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String); field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS"; field public static final int PERMISSION_GRANTED = 0; // 0x0 @@ -11214,6 +11221,7 @@ package android.service.games { method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame(); method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams); + method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult(@NonNull android.content.Intent, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSessionActivityCallback); method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback); } @@ -11223,6 +11231,11 @@ package android.service.games { field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0 } + public interface GameSessionActivityCallback { + method public void onActivityResult(int, @Nullable android.content.Intent); + method public default void onActivityStartFailed(@NonNull Throwable); + } + public abstract class GameSessionService extends android.app.Service { ctor public GameSessionService(); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); @@ -12251,6 +12264,7 @@ package android.telecom { field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2 field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0 field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT"; + field public static final String EXTRA_CALL_HAS_IN_BAND_RINGTONE = "android.telecom.extra.CALL_HAS_IN_BAND_RINGTONE"; field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE"; field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE"; field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT"; @@ -14734,6 +14748,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.bp b/core/java/Android.bp index 5649c5f1f7db..edf9ece4661c 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -176,6 +176,18 @@ filegroup { "com/android/internal/util/IndentingPrintWriter.java", "com/android/internal/util/MessageUtils.java", "com/android/internal/util/WakeupMessage.java", + // TODO: delete as soon as NetworkStatsFactory stops using + "com/android/internal/util/ProcFileReader.java", + ], +} + +// keep these files in sync with the packages/modules/Connectivity jarjar-rules.txt for +// the connectivity module. +filegroup { + name: "framework-connectivity-api-shared-srcs", + srcs: [ + "android/util/IndentingPrintWriter.java", + "com/android/internal/util/FileRotator.java", ], } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 42d2d28ae928..50473f1d8c84 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -3120,6 +3120,33 @@ public abstract class AccessibilityService extends Service { } } + /** + * Sets the system settings values that control the scaling factor for animations. The scale + * controls the animation playback speed for animations that respect these settings. Animations + * that do not respect the settings values will not be affected by this function. A lower scale + * value results in a faster speed. A value of <code>0</code> disables animations entirely. When + * animations are disabled services receive window change events more quickly which can reduce + * the potential by confusion by reducing the time during which windows are in transition. + * + * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED + * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED + * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE + * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE + * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE + * @param scale The scaling factor for all animations. + */ + public void setAnimationScale(float scale) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); + if (connection != null) { + try { + connection.setAnimationScale(scale); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + } + } + private static class AccessibilityContext extends ContextWrapper { private final int mConnectionId; diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 2cc15b40106b..0d6b19941afe 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -144,4 +144,6 @@ interface IAccessibilityServiceConnection { void onDoubleTap(int displayId); void onDoubleTapAndHold(int displayId); + + void setAnimationScale(float scale); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 530666b8f1d7..8935022639e4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5503,6 +5503,17 @@ public class Activity extends ContextThemeWrapper */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { + startActivityAsCaller(intent, options, permissionToken, ignoreTargetSecurity, userId, -1); + } + + /** + * @see #startActivityAsCaller(Intent, Bundle, IBinder, boolean, int) + * @param requestCode The request code used for returning a result or -1 if no result should be + * returned. + * @hide + */ + public void startActivityAsCaller(Intent intent, @Nullable Bundle options, + IBinder permissionToken, boolean ignoreTargetSecurity, int userId, int requestCode) { if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } @@ -5510,11 +5521,11 @@ public class Activity extends ContextThemeWrapper Instrumentation.ActivityResult ar = mInstrumentation.execStartActivityAsCaller( this, mMainThread.getApplicationThread(), mToken, this, - intent, -1, options, permissionToken, ignoreTargetSecurity, userId); + intent, requestCode, options, permissionToken, ignoreTargetSecurity, + userId); if (ar != null) { mMainThread.sendActivityResult( - mToken, mEmbeddedID, -1, ar.getResultCode(), - ar.getResultData()); + mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } cancelInputsAndStartExitTransition(options); } @@ -6139,6 +6150,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 +8757,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/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 9039bbdf62cf..60e22f4ecd12 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -156,6 +156,12 @@ public final class ApplicationExitInfo implements Parcelable { public static final int REASON_OTHER = 13; /** + * Application process was killed by App Freezer, for example, because it receives + * sync binder transactions while being frozen. + */ + public static final int REASON_FREEZER = 14; + + /** * Application process kills subreason is unknown. * * For internal use only. @@ -487,6 +493,7 @@ public final class ApplicationExitInfo implements Parcelable { REASON_USER_STOPPED, REASON_DEPENDENCY_DIED, REASON_OTHER, + REASON_FREEZER, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} @@ -1138,6 +1145,8 @@ public final class ApplicationExitInfo implements Parcelable { return "DEPENDENCY DIED"; case REASON_OTHER: return "OTHER KILLS BY SYSTEM"; + case REASON_FREEZER: + return "FREEZER"; default: return "UNKNOWN"; } 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/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index e4ef12c250ab..7c48a5738e51 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -679,6 +679,10 @@ interface IActivityManager { */ boolean isAppFreezerSupported(); + /** + * Return whether the app freezer is enabled (true) or not (false) by this system. + */ + boolean isAppFreezerEnabled(); /** * Kills uid with the reason of permission change. diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 0801b2481f0c..c5add66e0a14 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -130,6 +130,9 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId); + int startActivityFromGameSession(IApplicationThread caller, in String callingPackage, + in String callingFeatureId, int callingPid, int callingUid, in Intent intent, + int taskId, int userId); void startRecentsActivity(in Intent intent, in long eventTime, in IRecentsAnimationRunner recentsAnimationRunner); int startActivityFromRecents(int taskId, in Bundle options); 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/UiAutomation.java b/core/java/android/app/UiAutomation.java index 00903a880834..b41b5f005f1f 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -780,6 +780,33 @@ public final class UiAutomation { } /** + * Sets the system settings values that control the scaling factor for animations. The scale + * controls the animation playback speed for animations that respect these settings. Animations + * that do not respect the settings values will not be affected by this function. A lower scale + * value results in a faster speed. A value of <code>0</code> disables animations entirely. When + * animations are disabled services receive window change events more quickly which can reduce + * the potential by confusion by reducing the time during which windows are in transition. + * + * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED + * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED + * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE + * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE + * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE + * @param scale The scaling factor for all animations. + */ + public void setAnimationScale(float scale) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + connection.setAnimationScale(scale); + } catch (RemoteException re) { + throw new RuntimeException(re); + } + } + } + + /** * A request for WindowManagerService to wait until all animations have completed and input * information has been sent from WindowManager to native InputManager. * diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 487ce4848419..4704a24d2ca5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1332,7 +1332,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"; @@ -10135,7 +10138,9 @@ public class DevicePolicyManager { /** * Called by a profile owner of secondary user that is affiliated with the device to stop the - * calling user and switch back to primary user. + * calling user and switch back to primary user (when the user was + * {@link #switchUser(ComponentName, UserHandle)} switched to) or stop the user (when it was + * {@link #startUserInBackground(ComponentName, UserHandle) started in background}. * * <p>Notice that on devices running with * {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, there is no primary @@ -10163,7 +10168,12 @@ public class DevicePolicyManager { } /** - * Same as {@link #logoutUser(ComponentName)}, but called by system (like Settings), not admin. + * Similar to {@link #logoutUser(ComponentName)}, except: + * + * <ul> + * <li>Called by system (like Settings), not admin. + * <li>It logs out the current user, not the caller. + * </ul> * * @hide */ @@ -10180,7 +10190,10 @@ public class DevicePolicyManager { } /** * Gets the user a {@link #logoutUser(ComponentName)} call would switch to, - * or {@code null} if the current user is not in a session. + * or {@code null} if the current user is not in a session (i.e., if it was not + * {@link #switchUser(ComponentName, UserHandle) switched} or + * {@link #startUserInBackground(ComponentName, UserHandle) started in background} by the + * device admin. * * @hide */ @@ -15370,4 +15383,45 @@ public class DevicePolicyManager { } return ParcelableResource.loadDefaultString(defaultStringLoader); } + + /** + * Returns a boolean for whether the DPC has been downloaded during provisioning. + * + * <p>If true is returned, then any attempts to begin setup again should result in factory reset + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public boolean isDpcDownloaded() { + throwIfParentInstance("isDpcDownloaded"); + if (mService != null) { + try { + return mService.isDpcDownloaded(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** + * Use to indicate that the DPC has or has not been downloaded during provisioning. + * + * @param downloaded {@code true} if the dpc has been downloaded during provisioning. false otherwise. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void setDpcDownloaded(boolean downloaded) { + throwIfParentInstance("setDpcDownloaded"); + if (mService != null) { + try { + mService.setDpcDownloaded(downloaded); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } } diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index ac39cb4f1e23..7f2e5fde70f1 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -58,6 +58,10 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_ import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_INCOMING_WORK_CALL_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_MISSED_WORK_CALL_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_ONGOING_WORK_CALL_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_WIFI_WORK_CALL_LABEL; import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE; import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE; import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE; @@ -512,7 +516,11 @@ public final class DevicePolicyResources { WORK_PROFILE_DEFAULT_APPS_TITLE, HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE, BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE, BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, - LOCATION_AUTO_GRANTED_MESSAGE + LOCATION_AUTO_GRANTED_MESSAGE, + + // Dialer Strings + NOTIFICATION_INCOMING_WORK_CALL_TITLE, NOTIFICATION_ONGOING_WORK_CALL_TITLE, + NOTIFICATION_MISSED_WORK_CALL_TITLE, NOTIFICATION_WIFI_WORK_CALL_LABEL, }) public @interface UpdatableStringId { } @@ -709,6 +717,7 @@ public final class DevicePolicyResources { strings.addAll(DocumentsUi.buildStringsSet()); strings.addAll(MediaProvider.buildStringsSet()); strings.addAll(PermissionController.buildStringsSet()); + strings.addAll(Dialer.buildStringsSet()); return strings; } @@ -2934,5 +2943,54 @@ public final class DevicePolicyResources { return strings; } } + + /** + * Class containing the identifiers used to update device management-related system strings + * in the Dialer app. + */ + public static final class Dialer { + + private Dialer() { + } + + private static final String PREFIX = "Dialer."; + + /** + * The title of the in-call notification for an incoming work call. + */ + public static final String NOTIFICATION_INCOMING_WORK_CALL_TITLE = + PREFIX + "NOTIFICATION_INCOMING_WORK_CALL_TITLE"; + + /** + * The title of the in-call notification for an ongoing work call. + */ + public static final String NOTIFICATION_ONGOING_WORK_CALL_TITLE = + PREFIX + "NOTIFICATION_ONGOING_WORK_CALL_TITLE"; + + /** + * Missed call notification label, used when there's exactly one missed call from work + * contact. + */ + public static final String NOTIFICATION_MISSED_WORK_CALL_TITLE = + PREFIX + "NOTIFICATION_MISSED_WORK_CALL_TITLE"; + + /** + * Label for notification indicating that call is being made over wifi. + */ + public static final String NOTIFICATION_WIFI_WORK_CALL_LABEL = + PREFIX + "NOTIFICATION_WIFI_WORK_CALL_LABEL"; + + /** + * @hide + */ + static Set<String> buildStringsSet() { + Set<String> strings = new HashSet<>(); + strings.add(NOTIFICATION_INCOMING_WORK_CALL_TITLE); + strings.add(NOTIFICATION_ONGOING_WORK_CALL_TITLE); + strings.add(NOTIFICATION_MISSED_WORK_CALL_TITLE); + strings.add(NOTIFICATION_WIFI_WORK_CALL_LABEL); + return strings; + } + } } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index a7a51f8f6caa..0e1caca2670a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -555,6 +555,9 @@ interface IDevicePolicyManager { void resetDrawables(in String[] drawableIds); ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource); + boolean isDpcDownloaded(); + void setDpcDownloaded(boolean downloaded); + void setStrings(in List<DevicePolicyStringResource> strings); void resetStrings(in String[] stringIds); ParcelableResource getString(String stringId); 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 52681630dab0..207412511198 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; /** @@ -3888,7 +3890,6 @@ public abstract class Context { //@hide: SPEECH_RECOGNITION_SERVICE, UWB_SERVICE, MEDIA_METRICS_SERVICE, - SUPPLEMENTAL_PROCESS_SERVICE, //@hide: ATTESTATION_VERIFICATION_SERVICE, //@hide: SAFETY_CENTER_SERVICE, }) @@ -5970,13 +5971,6 @@ public abstract class Context { public static final String LOCALE_SERVICE = "locale"; /** - * Use with {@link #getSystemService(String)} to retrieve a Supplemental Process Manager. - * - * @see #getSystemService(String) - */ - public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process"; - - /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.safetycenter.SafetyCenterManager} instance for interacting with the safety center. * @@ -6510,15 +6504,26 @@ public abstract class Context { * <li>Each permission in {@code permissions} must be a runtime permission. * </ul> * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. The revocation happens asynchronously and kills all processes running in the - * calling UID. It will be triggered once it is safe to do so. In particular, it will not be - * triggered as long as the package remains in the foreground, or has any active manifest - * components (e.g. when another app is accessing a content provider in the package). + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * The revocation happens asynchronously and kills all processes running in the calling UID. It + * will be triggered once it is safe to do so. In particular, it will not be triggered as long + * as the package remains in the foreground, or has any active manifest components (e.g. when + * another app is accessing a content provider in the package). * <p> * If you want to revoke the permissions right away, you could call {@code System.exit()}, but * this could affect other apps that are accessing your app at the moment. For example, apps * accessing a content provider in your app will all crash. + * <p> + * Note that the settings UI shows a permission group as granted as long as at least one + * permission in the group is granted. If you want the user to observe the revocation in the + * settings, you should revoke every permission in the target group. To learn the current list + * of permissions in a group, you may use + * {@link PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)} and + * {@link PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)}. This list + * of permissions may evolve over time, so it is recommended to check whether it contains any + * permission you wish to retain before trying to revoke an entire group. * * @param permissions Collection of permissions to be revoked. * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3e527f8d5215..28bef566b59c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5576,6 +5576,7 @@ public class Intent implements Parcelable, Cloneable { /** * A String[] holding attribution tags when used with * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE * * E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc. */ @@ -5584,17 +5585,20 @@ public class Intent implements Parcelable, Cloneable { /** * A long representing the start timestamp (epoch time in millis) of the permission usage * when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE */ public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME"; /** * A long representing the end timestamp (epoch time in millis) of the permission usage when * used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE */ public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME"; /** - * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}, + * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and {@link #ACTION_MANAGE_PERMISSION_USAGE}, * that specifies whether the permission usage system UI is showing attribution information * for the chosen entry. * 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/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index c12e8195eeb4..d6d3a97687b5 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -25,6 +25,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Point; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; @@ -458,12 +459,14 @@ public final class CameraManager { (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); if (display != null) { - int width = display.getWidth(); - int height = display.getHeight(); + Point sz = new Point(); + display.getRealSize(sz); + int width = sz.x; + int height = sz.y; if (height > width) { height = width; - width = display.getHeight(); + width = sz.y; } ret = new Size(width, height); @@ -471,7 +474,7 @@ public final class CameraManager { Log.e(TAG, "Invalid default display!"); } } catch (Exception e) { - Log.e(TAG, "getDisplaySize Failed. " + e.toString()); + Log.e(TAG, "getDisplaySize Failed. " + e); } return ret; diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index fc2fbc39dbeb..223b8ccf44c8 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -346,7 +346,7 @@ public class InputMethodService extends AbstractInputMethodService { */ @AnyThread public static boolean canImeRenderGesturalNavButtons() { - return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true); + return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false); } /** diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index e5c22e4de08e..83fc7276eabc 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -546,7 +546,8 @@ final class NavigationBarController { public String toDebugString() { return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + " mNavigationBarFrame=" + mNavigationBarFrame - + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown + + " mShouldShowImeSwitcherWhenImeIsShown=" + + mShouldShowImeSwitcherWhenImeIsShown + " mAppearance=0x" + Integer.toHexString(mAppearance) + " mDarkIntensity=" + mDarkIntensity + " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java index f16bbc66e6cd..071bdea5e3ac 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -24,8 +24,6 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.content.Context; import android.net.NetworkStack; import android.os.connectivity.CellularBatteryStats; @@ -523,8 +521,6 @@ public final class BatteryStatsManager { * @param reason why Bluetooth has been turned on * @param packageName package responsible for this change */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOn(int uid, int reason, @NonNull String packageName) { try { @@ -541,8 +537,6 @@ public final class BatteryStatsManager { * @param reason why Bluetooth has been turned on * @param packageName package responsible for this change */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOff(int uid, int reason, @NonNull String packageName) { try { 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/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 1c0320e9a86e..619c8705ddae 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -79,7 +79,8 @@ interface IPermissionManager { void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions); void startOneTimePermissionSession(String packageName, int userId, long timeout, - int importanceToResetTimer, int importanceToKeepSessionAlive); + long revokeAfterKilledDelay, int importanceToResetTimer, + int importanceToKeepSessionAlive); void stopOneTimePermissionSession(String packageName, int userId); diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0cf06aa364ec..a005ab4e6ac7 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -907,21 +907,23 @@ public final class PermissionControllerManager { * <li>Each permission in {@code permissions} must be a runtime permission. * </ul> * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. This revocation happens asynchronously and kills all processes running in the - * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * This revocation happens asynchronously and kills all processes running in the same UID as + * {@code packageName}. It will be triggered once it is safe to do so. * * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. - * @param callback Callback called when the revocation request has been completed. * - * @see Context#revokeOwnPermissionsOnKill(Collection) + * @see Context#revokeOwnPermissionsOnKill(java.util.Collection) * * @hide */ public void revokeOwnPermissionsOnKill(@NonNull String packageName, - @NonNull List<String> permissions, AndroidFuture<Void> callback) { + @NonNull List<String> permissions) { mRemoteService.postAsync(service -> { + AndroidFuture<Void> callback = new AndroidFuture<>(); service.revokeOwnPermissionsOnKill(packageName, permissions, callback); return callback; }).whenComplete((result, err) -> { diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 8d9f82b04b54..3292e7110ee5 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -291,7 +291,7 @@ public abstract class PermissionControllerService extends Service { /** * Called when a package is considered inactive based on the criteria given by - * {@link PermissionManager#startOneTimePermissionSession(String, long, int, int)}. + * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}. * This method is called at the end of a one-time permission session * * @param packageName The package that has been inactive @@ -329,9 +329,11 @@ public abstract class PermissionControllerService extends Service { * Triggers the revocation of one or more permissions for a package. This should only be called * at the request of {@code packageName}. * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. This revocation happens asynchronously and kills all processes running in the - * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * This revocation happens asynchronously and kills all processes running in the same UID as + * {@code packageName}. It will be triggered once it is safe to do so. * * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 15f13eb89cb9..12fa0ddfc648 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -20,6 +20,7 @@ import static android.os.Build.VERSION_CODES.S; import android.Manifest; import android.annotation.CheckResult; +import android.annotation.DurationMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1282,6 +1283,22 @@ public final class PermissionManager { } /** + * Starts a one-time permission session for a given package. + * @see #startOneTimePermissionSession(String, long, long, int, int) + * @hide + * @deprecated Use {@link #startOneTimePermissionSession(String, long, long, int, int)} instead + */ + @Deprecated + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) + public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis, + @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer, + @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) { + startOneTimePermissionSession(packageName, timeoutMillis, -1, + importanceToResetTimer, importanceToKeepSessionAlive); + } + + /** * Starts a one-time permission session for a given package. A one-time permission session is * ended if app becomes inactive. Inactivity is defined as the package's uid importance level * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid @@ -1301,25 +1318,33 @@ public final class PermissionManager { * {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked. * </p> * <p> - * Note that if there is currently an active session for a package a new one isn't created and - * the existing one isn't changed. + * Note that if there is currently an active session for a package a new one isn't created but + * each parameter of the existing one will be updated to the more aggressive of both sessions. + * This means that durations will be set to the shortest parameter and importances will be set + * to the lowest one. * </p> * @param packageName The package to start a one-time permission session for * @param timeoutMillis Number of milliseconds for an app to be in an inactive state + * @param revokeAfterKilledDelayMillis Number of milliseconds to wait before revoking on the + * event an app is terminated. Set to -1 to use default + * value for the device. * @param importanceToResetTimer The least important level to uid must be to reset the timer * @param importanceToKeepSessionAlive The least important level the uid must be to keep the - * session alive + * session alive * * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) - public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis, + public void startOneTimePermissionSession(@NonNull String packageName, + @DurationMillisLong long timeoutMillis, + @DurationMillisLong long revokeAfterKilledDelayMillis, @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer, @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) { try { mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(), - timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive); + timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer, + importanceToKeepSessionAlive); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java index e33f1801129b..468e087c941b 100644 --- a/core/java/android/service/games/GameSession.java +++ b/core/java/android/service/games/GameSession.java @@ -20,15 +20,23 @@ import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.ActivityTaskManager; +import android.app.Instrumentation; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Slog; import android.view.SurfaceControlViewHost; import android.view.View; @@ -41,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -120,6 +129,7 @@ public abstract class GameSession { private LifecycleState mLifecycleState = LifecycleState.INITIALIZED; private boolean mAreTransientInsetsVisibleDueToGesture = false; private IGameSessionController mGameSessionController; + private Context mContext; private int mTaskId; private GameSessionRootView mGameSessionRootView; private SurfaceControlViewHost mSurfaceControlViewHost; @@ -137,6 +147,7 @@ public abstract class GameSession { int heightPx) { mGameSessionController = gameSessionController; mTaskId = taskId; + mContext = context; mSurfaceControlViewHost = surfaceControlViewHost; mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost); surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx); @@ -299,6 +310,8 @@ public abstract class GameSession { * {@code View} may not be cleared once set, but may be replaced by invoking * {@link #setTaskOverlayView(View, ViewGroup.LayoutParams)} again. * + * <p><b>WARNING</b>: Callers <b>must</b> ensure that only trusted views are provided. + * * @param view The desired content to display. * @param layoutParams Layout parameters for the view. */ @@ -456,4 +469,67 @@ public abstract class GameSession { break; } } + + /** + * Launches an activity within the same activity stack as the {@link GameSession}. When the + * target activity exits, {@link GameSessionActivityCallback#onActivityResult(int, Intent)} will + * be invoked with the result code and result data directly from the target activity (in other + * words, the result code and data set via the target activity's + * {@link android.app.Activity#startActivityForResult} call). The caller is expected to handle + * the results that the target activity returns. + * + * <p>Any activity that an app would normally be able to start via {@link + * android.app.Activity#startActivityForResult} will be startable via this method. + * + * <p>Started activities may see a different calling package than the game session's package + * when calling {@link android.app.Activity#getCallingPackage()}. + * + * <p> If an exception is thrown while handling {@code intent}, + * {@link GameSessionActivityCallback#onActivityStartFailed(Throwable)} will be called instead + * of {@link GameSessionActivityCallback#onActivityResult(int, Intent)}. + * + * @param intent The intent to start. + * @param options Additional options for how the Activity should be started. See + * {@link android.app.Activity#startActivityForResult(Intent, int, Bundle)} for + * more details. This value may be null. + * @param executor Executor on which {@code callback} should be invoked. + * @param callback Callback to be invoked once the started activity has finished. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) + public final void startActivityFromGameSessionForResult( + @NonNull Intent intent, @Nullable Bundle options, @NonNull Executor executor, + @NonNull GameSessionActivityCallback callback) { + Objects.requireNonNull(intent); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + AndroidFuture<GameSessionActivityResult> future = + new AndroidFuture<GameSessionActivityResult>() + .whenCompleteAsync((result, ex) -> { + if (ex != null) { + callback.onActivityStartFailed(ex); + return; + } + callback.onActivityResult(result.getResultCode(), result.getData()); + }, executor); + + final Intent trampolineIntent = new Intent(); + trampolineIntent.setComponent( + new ComponentName( + "android", "android.service.games.GameSessionTrampolineActivity")); + trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent); + trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options); + trampolineIntent.putExtra( + GameSessionTrampolineActivity.FUTURE_KEY, future); + + try { + int result = ActivityTaskManager.getService().startActivityFromGameSession( + mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession", + Binder.getCallingPid(), Binder.getCallingUid(), trampolineIntent, mTaskId, + UserHandle.myUserId()); + Instrumentation.checkStartActivityResult(result, trampolineIntent); + } catch (Throwable t) { + executor.execute(() -> callback.onActivityStartFailed(t)); + } + } } diff --git a/core/java/android/service/games/GameSessionActivityCallback.java b/core/java/android/service/games/GameSessionActivityCallback.java new file mode 100644 index 000000000000..3b11df1fe644 --- /dev/null +++ b/core/java/android/service/games/GameSessionActivityCallback.java @@ -0,0 +1,54 @@ +/* + * 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.service.games; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Intent; +import android.os.Bundle; + +import java.util.concurrent.Executor; + +/** + * Callback invoked when an activity launched via + * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, + * GameSessionActivityCallback)}} has returned a result or failed to start. + * + * @hide + */ +@SystemApi +public interface GameSessionActivityCallback { + /** + * Callback invoked when an activity launched via + * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, + * GameSessionActivityCallback)}} has returned a result. + * + * @param resultCode The result code of the launched activity. See {@link + * android.app.Activity#setResult(int)}. + * @param data Any data returned by the launched activity. See {@link + * android.app.Activity#setResult(int, Intent)}. + */ + void onActivityResult(int resultCode, @Nullable Intent data); + + /** + * Callback invoked when a throwable was thrown when launching the {@link Intent} in + * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, + * GameSessionActivityCallback)}}. + */ + default void onActivityStartFailed(@NonNull Throwable t) {} +} diff --git a/core/java/android/service/games/GameSessionActivityResult.java b/core/java/android/service/games/GameSessionActivityResult.java new file mode 100644 index 000000000000..a2ec6ada010c --- /dev/null +++ b/core/java/android/service/games/GameSessionActivityResult.java @@ -0,0 +1,71 @@ +/* + * 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.service.games; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; + + +final class GameSessionActivityResult implements Parcelable { + + public static final Creator<GameSessionActivityResult> CREATOR = + new Creator<GameSessionActivityResult>() { + @Override + public GameSessionActivityResult createFromParcel(Parcel in) { + int resultCode = in.readInt(); + Intent data = in.readParcelable(Intent.class.getClassLoader(), Intent.class); + return new GameSessionActivityResult(resultCode, data); + } + + @Override + public GameSessionActivityResult[] newArray(int size) { + return new GameSessionActivityResult[size]; + } + }; + + private final int mResultCode; + @Nullable + private final Intent mData; + + GameSessionActivityResult(int resultCode, @Nullable Intent data) { + mResultCode = resultCode; + mData = data; + } + + int getResultCode() { + return mResultCode; + } + + @Nullable + Intent getData() { + return mData; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mResultCode); + dest.writeParcelable(mData, flags); + } +} diff --git a/core/java/android/service/games/GameSessionTrampolineActivity.java b/core/java/android/service/games/GameSessionTrampolineActivity.java new file mode 100644 index 000000000000..ddea098680ea --- /dev/null +++ b/core/java/android/service/games/GameSessionTrampolineActivity.java @@ -0,0 +1,79 @@ +/* + * 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.service.games; + +import android.annotation.Nullable; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; + +import java.util.concurrent.Executor; + +/** + * Trampoline activity that enables the + * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, + * GameSessionActivityCallback)} API by reusing existing activity result infrastructure in the + * {@link Activity} class. This activity forwards activity results back to the calling + * {@link GameSession} via {@link AndroidFuture}. + * + * @hide + */ +public final class GameSessionTrampolineActivity extends Activity { + private static final String TAG = "GameSessionTrampoline"; + private static final int REQUEST_CODE = 1; + + static final String FUTURE_KEY = "GameSessionTrampolineActivity.future"; + static final String INTENT_KEY = "GameSessionTrampolineActivity.intent"; + static final String OPTIONS_KEY = "GameSessionTrampolineActivity.options"; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + startActivityAsCaller( + getIntent().getParcelableExtra(INTENT_KEY), + getIntent().getBundleExtra(OPTIONS_KEY), + null, + false, + getUserId(), + REQUEST_CODE); + } catch (Exception e) { + Slog.w(TAG, "Unable to launch activity from game session"); + AndroidFuture<GameSessionActivityResult> future = getIntent().getParcelableExtra( + FUTURE_KEY); + future.completeExceptionally(e); + finish(); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode != REQUEST_CODE) { + // Something went very wrong if we hit this code path, and we should bail. + throw new IllegalStateException("Unexpected request code: " + requestCode); + } + + AndroidFuture<GameSessionActivityResult> future = getIntent().getParcelableExtra( + FUTURE_KEY); + future.complete(new GameSessionActivityResult(resultCode, data)); + finish(); + } +} 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..22c66dc7aee6 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; @@ -12290,7 +12290,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return whether this view should have haptic feedback enabled for events - * long presses. + * such as long presses. * * @see #setHapticFeedbackEnabled(boolean) * @see #performHapticFeedback(int) @@ -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/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 3914a3c963b6..fadbdbbe8746 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -1251,18 +1251,19 @@ public abstract class Animation implements Cloneable { public float value; /** - * Size descriptions can appear inthree forms: + * Size descriptions can appear in four forms: * <ol> * <li>An absolute size. This is represented by a number.</li> * <li>A size relative to the size of the object being animated. This - * is represented by a number followed by "%".</li> * + * is represented by a number followed by "%".</li> * <li>A size relative to the size of the parent of object being * animated. This is represented by a number followed by "%p".</li> + * <li>(Starting from API 32) A complex number.</li> * </ol> * @param value The typed value to parse * @return The parsed version of the description */ - static Description parseValue(TypedValue value) { + static Description parseValue(TypedValue value, Context context) { Description d = new Description(); if (value == null) { d.type = ABSOLUTE; @@ -1283,6 +1284,11 @@ public abstract class Animation implements Cloneable { d.type = ABSOLUTE; d.value = value.data; return d; + } else if (value.type == TypedValue.TYPE_DIMENSION) { + d.type = ABSOLUTE; + d.value = TypedValue.complexToDimension(value.data, + context.getResources().getDisplayMetrics()); + return d; } } diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java index 21509d3a1159..3f4b3e7b4c80 100644 --- a/core/java/android/view/animation/ClipRectAnimation.java +++ b/core/java/android/view/animation/ClipRectAnimation.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.DisplayMetrics; /** * An animation that controls the clip of an object. See the @@ -66,43 +65,43 @@ public class ClipRectAnimation extends Animation { com.android.internal.R.styleable.ClipRectAnimation); Description d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_fromLeft)); + com.android.internal.R.styleable.ClipRectAnimation_fromLeft), context); mFromLeftType = d.type; mFromLeftValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_fromTop)); + com.android.internal.R.styleable.ClipRectAnimation_fromTop), context); mFromTopType = d.type; mFromTopValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_fromRight)); + com.android.internal.R.styleable.ClipRectAnimation_fromRight), context); mFromRightType = d.type; mFromRightValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_fromBottom)); + com.android.internal.R.styleable.ClipRectAnimation_fromBottom), context); mFromBottomType = d.type; mFromBottomValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_toLeft)); + com.android.internal.R.styleable.ClipRectAnimation_toLeft), context); mToLeftType = d.type; mToLeftValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_toTop)); + com.android.internal.R.styleable.ClipRectAnimation_toTop), context); mToTopType = d.type; mToTopValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_toRight)); + com.android.internal.R.styleable.ClipRectAnimation_toRight), context); mToRightType = d.type; mToRightValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ClipRectAnimation_toBottom)); + com.android.internal.R.styleable.ClipRectAnimation_toBottom), context); mToBottomType = d.type; mToBottomValue = d.value; diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java index fd627e50ab0e..210eb8a1ca9d 100644 --- a/core/java/android/view/animation/ExtendAnimation.java +++ b/core/java/android/view/animation/ExtendAnimation.java @@ -63,43 +63,43 @@ public class ExtendAnimation extends Animation { com.android.internal.R.styleable.ExtendAnimation); Description d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft)); + com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft), context); mFromLeftType = d.type; mFromLeftValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_fromExtendTop)); + com.android.internal.R.styleable.ExtendAnimation_fromExtendTop), context); mFromTopType = d.type; mFromTopValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_fromExtendRight)); + com.android.internal.R.styleable.ExtendAnimation_fromExtendRight), context); mFromRightType = d.type; mFromRightValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom)); + com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom), context); mFromBottomType = d.type; mFromBottomValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_toExtendLeft)); + com.android.internal.R.styleable.ExtendAnimation_toExtendLeft), context); mToLeftType = d.type; mToLeftValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_toExtendTop)); + com.android.internal.R.styleable.ExtendAnimation_toExtendTop), context); mToTopType = d.type; mToTopValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_toExtendRight)); + com.android.internal.R.styleable.ExtendAnimation_toExtendRight), context); mToRightType = d.type; mToRightValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ExtendAnimation_toExtendBottom)); + com.android.internal.R.styleable.ExtendAnimation_toExtendBottom), context); mToBottomType = d.type; mToBottomValue = d.value; diff --git a/core/java/android/view/animation/GridLayoutAnimationController.java b/core/java/android/view/animation/GridLayoutAnimationController.java index 0f189ae98030..c77f54fa889e 100644 --- a/core/java/android/view/animation/GridLayoutAnimationController.java +++ b/core/java/android/view/animation/GridLayoutAnimationController.java @@ -116,10 +116,12 @@ public class GridLayoutAnimationController extends LayoutAnimationController { com.android.internal.R.styleable.GridLayoutAnimation); Animation.Description d = Animation.Description.parseValue( - a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay)); + a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay), + context); mColumnDelay = d.value; d = Animation.Description.parseValue( - a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay)); + a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay), + context); mRowDelay = d.value; //noinspection PointlessBitwiseExpression mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction, diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java index e2b7519b1912..1d56d293e7df 100644 --- a/core/java/android/view/animation/LayoutAnimationController.java +++ b/core/java/android/view/animation/LayoutAnimationController.java @@ -106,7 +106,7 @@ public class LayoutAnimationController { TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation); Animation.Description d = Animation.Description.parseValue( - a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay)); + a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay), context); mDelay = d.value; mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL); diff --git a/core/java/android/view/animation/RotateAnimation.java b/core/java/android/view/animation/RotateAnimation.java index 3c325d9b2aa9..0613cd2ea5ad 100644 --- a/core/java/android/view/animation/RotateAnimation.java +++ b/core/java/android/view/animation/RotateAnimation.java @@ -56,12 +56,12 @@ public class RotateAnimation extends Animation { mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f); Description d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.RotateAnimation_pivotX)); + com.android.internal.R.styleable.RotateAnimation_pivotX), context); mPivotXType = d.type; mPivotXValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.RotateAnimation_pivotY)); + com.android.internal.R.styleable.RotateAnimation_pivotY), context); mPivotYType = d.type; mPivotYValue = d.value; diff --git a/core/java/android/view/animation/ScaleAnimation.java b/core/java/android/view/animation/ScaleAnimation.java index e9a84364452c..533ef45e7fe5 100644 --- a/core/java/android/view/animation/ScaleAnimation.java +++ b/core/java/android/view/animation/ScaleAnimation.java @@ -118,12 +118,12 @@ public class ScaleAnimation extends Animation { } Description d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ScaleAnimation_pivotX)); + com.android.internal.R.styleable.ScaleAnimation_pivotX), context); mPivotXType = d.type; mPivotXValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.ScaleAnimation_pivotY)); + com.android.internal.R.styleable.ScaleAnimation_pivotY), context); mPivotYType = d.type; mPivotYValue = d.value; diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java index 3365c70b5b34..e27469c0729a 100644 --- a/core/java/android/view/animation/TranslateAnimation.java +++ b/core/java/android/view/animation/TranslateAnimation.java @@ -73,22 +73,22 @@ public class TranslateAnimation extends Animation { com.android.internal.R.styleable.TranslateAnimation); Description d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.TranslateAnimation_fromXDelta)); + com.android.internal.R.styleable.TranslateAnimation_fromXDelta), context); mFromXType = d.type; mFromXValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.TranslateAnimation_toXDelta)); + com.android.internal.R.styleable.TranslateAnimation_toXDelta), context); mToXType = d.type; mToXValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.TranslateAnimation_fromYDelta)); + com.android.internal.R.styleable.TranslateAnimation_fromYDelta), context); mFromYType = d.type; mFromYValue = d.value; d = Description.parseValue(a.peekValue( - com.android.internal.R.styleable.TranslateAnimation_toYDelta)); + com.android.internal.R.styleable.TranslateAnimation_toYDelta), context); mToYType = d.type; mToYValue = d.value; 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/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java index 65526eba3e54..fbdbbfb06b78 100644 --- a/core/java/com/android/internal/app/BlockedAppActivity.java +++ b/core/java/com/android/internal/app/BlockedAppActivity.java @@ -17,7 +17,6 @@ package com.android.internal.app; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; @@ -36,9 +35,6 @@ public class BlockedAppActivity extends AlertActivity { private static final String TAG = "BlockedAppActivity"; private static final String PACKAGE_NAME = "com.android.internal.app"; private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE"; - private static final String EXTRA_BLOCKED_ACTIVITY_INFO = - PACKAGE_NAME + ".extra.BLOCKED_ACTIVITY_INFO"; - private static final String EXTRA_STREAMED_DEVICE = PACKAGE_NAME + ".extra.STREAMED_DEVICE"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -52,30 +48,17 @@ public class BlockedAppActivity extends AlertActivity { return; } - CharSequence appLabel = null; String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE); - ActivityInfo activityInfo = intent.getParcelableExtra(EXTRA_BLOCKED_ACTIVITY_INFO); - if (activityInfo != null) { - appLabel = activityInfo.loadLabel(getPackageManager()); - } else if (!TextUtils.isEmpty(packageName)) { - appLabel = getAppLabel(userId, packageName); - } - - if (TextUtils.isEmpty(appLabel)) { - Slog.wtf(TAG, "Invalid package: " + packageName + " or activity info: " + activityInfo); + if (TextUtils.isEmpty(packageName)) { + Slog.wtf(TAG, "Invalid package: " + packageName); finish(); return; } - CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE); - if (!TextUtils.isEmpty(streamedDeviceName)) { - mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel); - mAlertParams.mMessage = - getString(R.string.app_streaming_blocked_message, streamedDeviceName); - } else { - mAlertParams.mTitle = getString(R.string.app_blocked_title); - mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel); - } + CharSequence appLabel = getAppLabel(userId, packageName); + + mAlertParams.mTitle = getString(R.string.app_blocked_title); + mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel); mAlertParams.mPositiveButtonText = getString(android.R.string.ok); setupAlert(); } @@ -100,19 +83,4 @@ public class BlockedAppActivity extends AlertActivity { .putExtra(Intent.EXTRA_USER_ID, userId) .putExtra(EXTRA_BLOCKED_PACKAGE, packageName); } - - /** - * Creates an intent that launches {@link BlockedAppActivity} when app streaming is blocked. - * - * Using this method and providing a non-empty {@code streamedDeviceName} will cause the dialog - * to use streaming-specific error messages. - */ - public static Intent createStreamingBlockedIntent(int userId, ActivityInfo activityInfo, - CharSequence streamedDeviceName) { - return new Intent() - .setClassName("android", BlockedAppActivity.class.getName()) - .putExtra(Intent.EXTRA_USER_ID, userId) - .putExtra(EXTRA_BLOCKED_ACTIVITY_INFO, activityInfo) - .putExtra(EXTRA_STREAMED_DEVICE, streamedDeviceName); - } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c0fec62bdd94..5ba45c9b8bc3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -607,11 +607,15 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_BT = 0x08; int UPDATE_RPM = 0x10; int UPDATE_DISPLAY = 0x20; + int RESET = 0x40; + int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY; int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT; + int UPDATE_ON_RESET = UPDATE_ALL | RESET; + @IntDef(flag = true, prefix = "UPDATE_", value = { UPDATE_CPU, UPDATE_WIFI, @@ -12909,7 +12913,7 @@ public class BatteryStatsImpl extends BatteryStats { // Flush external data, gathering snapshots, but don't process it since it is pre-reset data mIgnoreNextExternalStats = true; - mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ALL); + mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ON_RESET); mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS); } 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/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index a52ae107d983..7262e846d9b0 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -438,10 +438,16 @@ public class MeasuredEnergyStats { mState = state; mStateChangeTimestampMs = timestampMs; if (mAccumulatedMultiStateChargeMicroCoulomb == null) { - return; + mAccumulatedMultiStateChargeMicroCoulomb = + new LongMultiStateCounter[mAccumulatedChargeMicroCoulomb.length]; } for (int i = 0; i < mAccumulatedMultiStateChargeMicroCoulomb.length; i++) { LongMultiStateCounter counter = mAccumulatedMultiStateChargeMicroCoulomb[i]; + if (counter == null && mConfig.isSupportedMultiStateBucket(i)) { + counter = new LongMultiStateCounter(mConfig.mStateNames.length); + counter.updateValue(0, timestampMs); + mAccumulatedMultiStateChargeMicroCoulomb[i] = counter; + } if (counter != null) { counter.setState(state, timestampMs); } 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/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index edc8c5b99ebe..2bec733d954c 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -18,25 +18,25 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioSystem-JNI" -#include <utils/Log.h> - -#include <sstream> -#include <vector> -#include <jni.h> -#include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" - #include <android/media/AudioVibratorInfo.h> #include <android/media/INativeSpatializerCallback.h> #include <android/media/ISpatializer.h> +#include <android_os_Parcel.h> #include <audiomanager/AudioManager.h> +#include <jni.h> #include <media/AudioContainers.h> #include <media/AudioPolicy.h> #include <media/AudioSystem.h> #include <media/MicrophoneInfo.h> +#include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <system/audio.h> #include <system/audio_policy.h> +#include <utils/Log.h> + +#include <sstream> +#include <vector> + #include "android_media_AudioAttributes.h" #include "android_media_AudioDescriptor.h" #include "android_media_AudioDeviceAttributes.h" @@ -46,6 +46,7 @@ #include "android_media_AudioProfile.h" #include "android_media_MicrophoneInfo.h" #include "android_util_Binder.h" +#include "core_jni_helpers.h" // ---------------------------------------------------------------------------- @@ -584,18 +585,26 @@ android_media_AudioSystem_routing_callback() env->DeleteLocalRef(clazz); } -static jint -android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name, - jint codec) -{ - const char *c_address = env->GetStringUTFChars(device_address, NULL); - const char *c_name = env->GetStringUTFChars(device_name, NULL); - int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device), - static_cast <audio_policy_dev_state_t>(state), - c_address, c_name, - static_cast <audio_format_t>(codec))); - env->ReleaseStringUTFChars(device_address, c_address); - env->ReleaseStringUTFChars(device_name, c_name); +static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, + jint state, jobject jParcel, + jint codec) { + int status; + if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) { + android::media::audio::common::AudioPort port{}; + if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) { + status = check_AudioSystem_Command( + AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>( + state), + port, + static_cast<audio_format_t>(codec))); + } else { + ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str()); + status = kAudioStatusError; + } + } else { + ALOGE("Failed to retrieve the native parcel from Java parcel"); + status = kAudioStatusError; + } return (jint) status; } @@ -2912,7 +2921,7 @@ static const JNINativeMethod gMethods[] = {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId}, {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId}, - {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I", + {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 7c67cbcbd23c..a6fbf094a030 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -235,9 +235,7 @@ void android_os_Process_setThreadGroupAndCpuset(JNIEnv* env, jobject clazz, int void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp); - DIR *d; char proc_path[255]; - struct dirent *de; if (!verifyGroup(env, grp)) { return; @@ -277,84 +275,8 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin } } - sprintf(proc_path, "/proc/%d/task", pid); - if (!(d = opendir(proc_path))) { - // If the process exited on us, don't generate an exception - if (errno != ENOENT) - signalExceptionForGroupError(env, errno, pid); - return; - } - - while ((de = readdir(d))) { - int t_pid; - int t_pri; - std::string taskprofile; - - if (de->d_name[0] == '.') - continue; - t_pid = atoi(de->d_name); - - if (!t_pid) { - ALOGE("Error getting pid for '%s'\n", de->d_name); - continue; - } - - t_pri = getpriority(PRIO_PROCESS, t_pid); - - if (t_pri <= ANDROID_PRIORITY_AUDIO) { - int scheduler = sched_getscheduler(t_pid) & ~SCHED_RESET_ON_FORK; - if ((scheduler == SCHED_FIFO) || (scheduler == SCHED_RR)) { - // This task wants to stay in its current audio group so it can keep its budget - // don't update its cpuset or cgroup - continue; - } - } - - errno = 0; - // grp == SP_BACKGROUND. Set background cpuset policy profile for all threads. - if (grp == SP_BACKGROUND) { - if (!SetTaskProfiles(t_pid, {"CPUSET_SP_BACKGROUND"}, true)) { - signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid); - break; - } - continue; - } - - // grp != SP_BACKGROUND. Only change the cpuset cgroup for low priority thread, so it could - // preserve it sched policy profile setting. - if (t_pri >= ANDROID_PRIORITY_BACKGROUND) { - switch (grp) { - case SP_SYSTEM: - taskprofile = "ServiceCapacityLow"; - break; - case SP_RESTRICTED: - taskprofile = "ServiceCapacityRestricted"; - break; - case SP_FOREGROUND: - case SP_AUDIO_APP: - case SP_AUDIO_SYS: - taskprofile = "ProcessCapacityHigh"; - break; - case SP_TOP_APP: - taskprofile = "ProcessCapacityMax"; - break; - default: - taskprofile = "ProcessCapacityNormal"; - break; - } - if (!SetTaskProfiles(t_pid, {taskprofile}, true)) { - signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid); - break; - } - // Change the cpuset policy profile for non-low priority thread according to the grp - } else { - if (!SetTaskProfiles(t_pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}, true)) { - signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid); - break; - } - } - } - closedir(d); + if (!SetProcessProfilesCached(0, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)})) + signalExceptionForGroupError(env, errno ? errno : EPERM, pid); } void android_os_Process_setProcessFrozen( 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..506a0c09b7c9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -398,6 +398,7 @@ <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" /> <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" /> <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" /> + <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED" /> <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" /> <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" /> <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" /> @@ -2027,6 +2028,11 @@ <permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" android:protectionLevel="signature" /> + <!-- @SystemApi @hide Allows an application to manage ethernet networks. + <p>Not for use by third-party or privileged applications. --> + <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS" + android:protectionLevel="signature" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> @@ -6540,12 +6546,27 @@ 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" android:exported="false"> </activity> + <activity android:name="android.service.games.GameSessionTrampolineActivity" + android:excludeFromRecents="true" + android:exported="true" + android:permission="android.permission.MANAGE_GAME_ACTIVITY" + android:theme="@style/Theme.Translucent.NoTitleBar"> + </activity> + <receiver android:name="com.android.server.BootReceiver" android:exported="true" android:systemUserOnly="true"> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index afe0f1bf0001..d774fd4e397a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6953,10 +6953,10 @@ </declare-styleable> <declare-styleable name="TranslateAnimation"> - <attr name="fromXDelta" format="float|fraction" /> - <attr name="toXDelta" format="float|fraction" /> - <attr name="fromYDelta" format="float|fraction" /> - <attr name="toYDelta" format="float|fraction" /> + <attr name="fromXDelta" format="float|fraction|dimension" /> + <attr name="toXDelta" format="float|fraction|dimension" /> + <attr name="fromYDelta" format="float|fraction|dimension" /> + <attr name="toYDelta" format="float|fraction|dimension" /> </declare-styleable> <declare-styleable name="AlphaAnimation"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 52c62050f6a1..d7ebb0f18c4b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5439,15 +5439,6 @@ <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now. </string> - <!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> - <string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string> - <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> - <string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string> - <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> - <string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string> - <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] --> - <string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string> - <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] --> <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string> <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] --> @@ -5689,6 +5680,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..33efbce15eea 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3285,9 +3285,6 @@ <java-symbol type="string" name="app_blocked_title" /> <java-symbol type="string" name="app_blocked_message" /> - <java-symbol type="string" name="app_streaming_blocked_title" /> - <java-symbol type="string" name="app_streaming_blocked_message" /> - <!-- Used internally for assistant to launch activity transitions --> <java-symbol type="id" name="cross_task_transition" /> @@ -3865,6 +3862,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/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 02e5942a3544..fc385a013d00 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -204,4 +204,6 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void logTrace(long timestamp, String where, long loggingTypes, String callingParams, int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {} + + public void setAnimationScale(float scale) {} } 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/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index dbb2cf15dd8e..88349b38040c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -383,10 +383,10 @@ public class MeasuredEnergyStatsTest { assertEquals(13, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0)); // 6 * (6000-4000)/(6000-2000) assertEquals(3, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1)); - - // POWER_BUCKET_SCREEN_OTHER was only present along with state=1 - assertEquals(0, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0)); - assertEquals(40, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1)); + // 40 * (4000-1000)/(5000-1000) + assertEquals(30, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0)); + // 40 * (5000-4000)/(5000-1000) + assertEquals(10, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1)); } @Test 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/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index a477bd7f8295..7ab683513570 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1790,6 +1790,7 @@ public class BubbleStackView extends FrameLayout /** * Changes the expanded state of the stack. + * Don't call this directly, call mBubbleData#setExpanded. * * @param shouldExpand whether the bubble stack should appear expanded */ @@ -1836,7 +1837,7 @@ public class BubbleStackView extends FrameLayout } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) { mManageEduView.hide(); } else { - setExpanded(false); + mBubbleData.setExpanded(false); } } } 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/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 72ead0023366..32861b698daa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -103,7 +103,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis }; context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */, - mainHandler); + mainHandler, Context.RECEIVER_EXPORTED); pipMediaController.addActionListener(this::onMediaActionsChanged); } 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/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt index 6524182e9082..906123914731 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt @@ -93,7 +93,7 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 20) + repetitions = 5) } } }
\ No newline at end of file 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/Android.bp b/media/Android.bp index fcdfd72c91d5..5aedcfbc22e9 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -108,6 +108,11 @@ aidl_interface { vndk: { enabled: true, }, + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth", + ], }, }, } diff --git a/media/java/android/media/AudioDescriptor.java b/media/java/android/media/AudioDescriptor.java index 11371b11e905..df648be4c157 100644 --- a/media/java/android/media/AudioDescriptor.java +++ b/media/java/android/media/AudioDescriptor.java @@ -18,16 +18,21 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; /** * The AudioDescriptor contains the information to describe the audio playback/capture * capabilities. The capabilities are described by a byte array, which is defined by a * particular standard. This is used when the format is unrecognized to the platform. */ -public class AudioDescriptor { +public class AudioDescriptor implements Parcelable { /** * The audio standard is not specified. */ @@ -49,7 +54,15 @@ public class AudioDescriptor { private final byte[] mDescriptor; private final int mEncapsulationType; - AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) { + /** + * @hide + * Constructor from standard, encapsulation type and descriptor + * @param standard the standard of the audio descriptor + * @param encapsulationType the encapsulation type of the audio descriptor + * @param descriptor the audio descriptor + */ + @SystemApi + public AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) { mStandard = standard; mEncapsulationType = encapsulationType; mDescriptor = descriptor; @@ -87,4 +100,66 @@ public class AudioDescriptor { public @AudioProfile.EncapsulationType int getEncapsulationType() { return mEncapsulationType; } + + @Override + public int hashCode() { + return Objects.hash(mStandard, mEncapsulationType, Arrays.hashCode(mDescriptor)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioDescriptor that = (AudioDescriptor) o; + return ((mStandard == that.mStandard) + && (mEncapsulationType == that.mEncapsulationType) + && (Arrays.equals(mDescriptor, that.mDescriptor))); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + sb.append("standard=" + mStandard); + sb.append(", encapsulation type=" + mEncapsulationType); + if (mDescriptor != null && mDescriptor.length > 0) { + sb.append(", descriptor=").append(Arrays.toString(mDescriptor)); + } + sb.append("}"); + return sb.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStandard); + dest.writeInt(mEncapsulationType); + dest.writeByteArray(mDescriptor); + } + + private AudioDescriptor(@NonNull Parcel in) { + mStandard = in.readInt(); + mEncapsulationType = in.readInt(); + mDescriptor = in.createByteArray(); + } + + public static final @NonNull Parcelable.Creator<AudioDescriptor> CREATOR = + new Parcelable.Creator<AudioDescriptor>() { + /** + * Rebuilds an AudioDescriptor previously stored with writeToParcel(). + * @param p Parcel object to read the AudioDescriptor from + * @return a new AudioDescriptor created from the data in the parcel + */ + public AudioDescriptor createFromParcel(Parcel p) { + return new AudioDescriptor(p); + } + + public AudioDescriptor[] newArray(int size) { + return new AudioDescriptor[size]; + } + }; } diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java index 1448c49105b2..af3c295b8d6c 100644 --- a/media/java/android/media/AudioDeviceAttributes.java +++ b/media/java/android/media/AudioDeviceAttributes.java @@ -18,12 +18,16 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -65,16 +69,27 @@ public final class AudioDeviceAttributes implements Parcelable { * The unique address of the device. Some devices don't have addresses, only an empty string. */ private final @NonNull String mAddress; - + /** + * The non-unique name of the device. Some devices don't have names, only an empty string. + * Should not be used as a unique identifier for a device. + */ + private final @NonNull String mName; /** * Is input or output device */ private final @Role int mRole; - /** * The internal audio device type */ private final int mNativeType; + /** + * List of AudioProfiles supported by the device + */ + private final @NonNull List<AudioProfile> mAudioProfiles; + /** + * List of AudioDescriptors supported by the device + */ + private final @NonNull List<AudioDescriptor> mAudioDescriptors; /** * @hide @@ -88,7 +103,10 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT; mType = deviceInfo.getType(); mAddress = deviceInfo.getAddress(); + mName = String.valueOf(deviceInfo.getProductName()); mNativeType = deviceInfo.getInternalType(); + mAudioProfiles = deviceInfo.getAudioProfiles(); + mAudioDescriptors = deviceInfo.getAudioDescriptors(); } /** @@ -100,7 +118,24 @@ public final class AudioDeviceAttributes implements Parcelable { */ @SystemApi public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, - @NonNull String address) { + @NonNull String address) { + this(role, type, address, "", new ArrayList<>(), new ArrayList<>()); + } + + /** + * @hide + * Constructor with specification of all attributes + * @param role indicates input or output role + * @param type the device type, as defined in {@link AudioDeviceInfo} + * @param address the address of the device, or an empty string for devices without one + * @param name the name of the device, or an empty string for devices without one + * @param profiles the list of AudioProfiles supported by the device + * @param descriptors the list of AudioDescriptors supported by the device + */ + @SystemApi + public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type, + @NonNull String address, @NonNull String name, @NonNull List<AudioProfile> profiles, + @NonNull List<AudioDescriptor> descriptors) { Objects.requireNonNull(address); if (role != ROLE_OUTPUT && role != ROLE_INPUT) { throw new IllegalArgumentException("Invalid role " + role); @@ -118,19 +153,37 @@ public final class AudioDeviceAttributes implements Parcelable { mRole = role; mType = type; mAddress = address; + mName = name; + mAudioProfiles = profiles; + mAudioDescriptors = descriptors; } /** * @hide - * Constructor from internal device type and address - * @param type the internal device type, as defined in {@link AudioSystem} + * Constructor called from AudioSystem JNI when creating an AudioDeviceAttributes from a native + * AudioDeviceTypeAddr instance. + * @param nativeType the internal device type, as defined in {@link AudioSystem} * @param address the address of the device, or an empty string for devices without one */ public AudioDeviceAttributes(int nativeType, @NonNull String address) { + this(nativeType, address, ""); + } + + /** + * @hide + * Constructor called from BtHelper to connect or disconnect a Bluetooth device. + * @param nativeType the internal device type, as defined in {@link AudioSystem} + * @param address the address of the device, or an empty string for devices without one + * @param name the name of the device, or an empty string for devices without one + */ + public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull String name) { mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT; mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType); mAddress = address; + mName = name; mNativeType = nativeType; + mAudioProfiles = new ArrayList<>(); + mAudioDescriptors = new ArrayList<>(); } /** @@ -165,6 +218,16 @@ public final class AudioDeviceAttributes implements Parcelable { /** * @hide + * Returns the name of the audio device, or an empty string for devices without one + * @return the device name + */ + @SystemApi + public @NonNull String getName() { + return mName; + } + + /** + * @hide * Returns the internal device type of a device * @return the internal device type */ @@ -172,9 +235,29 @@ public final class AudioDeviceAttributes implements Parcelable { return mNativeType; } + /** + * @hide + * Returns the list of AudioProfiles supported by the device + * @return the list of AudioProfiles + */ + @SystemApi + public @NonNull List<AudioProfile> getAudioProfiles() { + return mAudioProfiles; + } + + /** + * @hide + * Returns the list of AudioDescriptors supported by the device + * @return the list of AudioDescriptors + */ + @SystemApi + public @NonNull List<AudioDescriptor> getAudioDescriptors() { + return mAudioDescriptors; + } + @Override public int hashCode() { - return Objects.hash(mRole, mType, mAddress); + return Objects.hash(mRole, mType, mAddress, mName, mAudioProfiles, mAudioDescriptors); } @Override @@ -185,6 +268,25 @@ public final class AudioDeviceAttributes implements Parcelable { AudioDeviceAttributes that = (AudioDeviceAttributes) o; return ((mRole == that.mRole) && (mType == that.mType) + && mAddress.equals(that.mAddress) + && mName.equals(that.mName) + && mAudioProfiles.equals(that.mAudioProfiles) + && mAudioDescriptors.equals(that.mAudioDescriptors)); + } + + /** + * Returns true if the role, type and address are equal. Called to compare with an + * AudioDeviceAttributes that was created from a native AudioDeviceTypeAddr instance. + * @param o object to compare with + * @return whether role, type and address are equal + */ + public boolean equalTypeAddress(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioDeviceAttributes that = (AudioDeviceAttributes) o; + return ((mRole == that.mRole) + && (mType == that.mType) && mAddress.equals(that.mAddress)); } @@ -199,7 +301,10 @@ public final class AudioDeviceAttributes implements Parcelable { + " role:" + roleToString(mRole) + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType) : AudioSystem.getInputDeviceName(mNativeType)) - + " addr:" + mAddress); + + " addr:" + mAddress + + " name:" + mName + + " profiles:" + mAudioProfiles.toString() + + " descriptors:" + mAudioDescriptors.toString()); } @Override @@ -212,14 +317,26 @@ public final class AudioDeviceAttributes implements Parcelable { dest.writeInt(mRole); dest.writeInt(mType); dest.writeString(mAddress); + dest.writeString(mName); dest.writeInt(mNativeType); + dest.writeParcelableArray( + mAudioProfiles.toArray(new AudioProfile[mAudioProfiles.size()]), flags); + dest.writeParcelableArray( + mAudioDescriptors.toArray(new AudioDescriptor[mAudioDescriptors.size()]), flags); } private AudioDeviceAttributes(@NonNull Parcel in) { mRole = in.readInt(); mType = in.readInt(); mAddress = in.readString(); + mName = in.readString(); mNativeType = in.readInt(); + AudioProfile[] audioProfilesArray = + in.readParcelableArray(AudioProfile.class.getClassLoader(), AudioProfile.class); + mAudioProfiles = new ArrayList<AudioProfile>(Arrays.asList(audioProfilesArray)); + AudioDescriptor[] audioDescriptorsArray = in.readParcelableArray( + AudioDescriptor.class.getClassLoader(), AudioDescriptor.class); + mAudioDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(audioDescriptorsArray)); } public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR = diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index dd17dc649bc7..3d08959901b1 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -421,7 +421,7 @@ public final class AudioDeviceInfo { */ public CharSequence getProductName() { String portName = mPort.name(); - return portName.length() != 0 ? portName : android.os.Build.MODEL; + return (portName != null && portName.length() != 0) ? portName : android.os.Build.MODEL; } /** diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 15a398de9021..cdc31631637e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5874,7 +5874,7 @@ public class AudioManager { return false; } - /** + /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) * @param state new connection state: 1 connected, 0 disconnected @@ -5883,10 +5883,29 @@ public class AudioManager { */ @UnsupportedAppUsage @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public void setWiredDeviceConnectionState(int type, int state, String address, String name) { + public void setWiredDeviceConnectionState(int device, int state, String address, + String name) { + final IAudioService service = getService(); + int role = isOutputDevice(device) + ? AudioDeviceAttributes.ROLE_OUTPUT : AudioDeviceAttributes.ROLE_INPUT; + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + role, AudioDeviceInfo.convertInternalDeviceToDeviceType(device), address, + name, new ArrayList<>()/*mAudioProfiles*/, new ArrayList<>()/*mAudioDescriptors*/); + setWiredDeviceConnectionState(attributes, state); + } + + /** + * Indicate wired accessory connection state change and attributes. + * @param state new connection state: 1 connected, 0 disconnected + * @param attributes attributes of the connected device + * {@hide} + */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) { final IAudioService service = getService(); try { - service.setWiredDeviceConnectionState(type, state, address, name, + service.setWiredDeviceConnectionState(attributes, state, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -5919,13 +5938,14 @@ public class AudioManager { * @param newDevice Bluetooth device connected or null if there is no new devices * @param previousDevice Bluetooth device disconnected or null if there is no disconnected * devices - * @param info contain all info related to the device. {@link BtProfileConnectionInfo} + * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo} * {@hide} */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice, - @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + @Nullable BluetoothDevice previousDevice, + @NonNull BluetoothProfileConnectionInfo info) { final IAudioService service = getService(); try { service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info); diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java index ae8d0a5ad4ab..5c5f837dd07a 100644 --- a/media/java/android/media/AudioProfile.java +++ b/media/java/android/media/AudioProfile.java @@ -18,10 +18,14 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -33,7 +37,7 @@ import java.util.stream.Collectors; * be reported in different audio profiles. The application can choose any of the encapsulation * types. */ -public class AudioProfile { +public class AudioProfile implements Parcelable { /** * No encapsulation type is specified. */ @@ -57,9 +61,19 @@ public class AudioProfile { private final int[] mChannelIndexMasks; private final int mEncapsulationType; - AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, - @NonNull int[] channelIndexMasks, - int encapsulationType) { + /** + * @hide + * Constructor from format, sampling rates, channel masks, channel index masks and + * encapsulation type. + * @param format the audio format + * @param samplingRates the supported sampling rates + * @param channelMasks the supported channel masks + * @param channelIndexMasks the supported channel index masks + * @param encapsulationType the encapsulation type of the encoding format + */ + @SystemApi + public AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, + @NonNull int[] channelIndexMasks, int encapsulationType) { mFormat = format; mSamplingRates = samplingRates; mChannelMasks = channelMasks; @@ -114,6 +128,26 @@ public class AudioProfile { } @Override + public int hashCode() { + return Objects.hash(mFormat, Arrays.hashCode(mSamplingRates), + Arrays.hashCode(mChannelMasks), Arrays.hashCode(mChannelIndexMasks), + mEncapsulationType); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioProfile that = (AudioProfile) o; + return ((mFormat == that.mFormat) + && (hasIdenticalElements(mSamplingRates, that.mSamplingRates)) + && (hasIdenticalElements(mChannelMasks, that.mChannelMasks)) + && (hasIdenticalElements(mChannelIndexMasks, that.mChannelIndexMasks)) + && (mEncapsulationType == that.mEncapsulationType)); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder("{"); sb.append(AudioFormat.toLogFriendlyEncoding(mFormat)); @@ -126,6 +160,7 @@ public class AudioProfile { if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) { sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks)); } + sb.append(", encapsulation type=" + mEncapsulationType); sb.append("}"); return sb.toString(); } @@ -137,4 +172,50 @@ public class AudioProfile { return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X", anInt)) .collect(Collectors.joining(", ")); } + + private static boolean hasIdenticalElements(int[] array1, int[] array2) { + int[] sortedArray1 = Arrays.copyOf(array1, array1.length); + Arrays.sort(sortedArray1); + int[] sortedArray2 = Arrays.copyOf(array2, array2.length); + Arrays.sort(sortedArray2); + return Arrays.equals(sortedArray1, sortedArray2); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mFormat); + dest.writeIntArray(mSamplingRates); + dest.writeIntArray(mChannelMasks); + dest.writeIntArray(mChannelIndexMasks); + dest.writeInt(mEncapsulationType); + } + + private AudioProfile(@NonNull Parcel in) { + mFormat = in.readInt(); + mSamplingRates = in.createIntArray(); + mChannelMasks = in.createIntArray(); + mChannelIndexMasks = in.createIntArray(); + mEncapsulationType = in.readInt(); + } + + public static final @NonNull Parcelable.Creator<AudioProfile> CREATOR = + new Parcelable.Creator<AudioProfile>() { + /** + * Rebuilds an AudioProfile previously stored with writeToParcel(). + * @param p Parcel object to read the AudioProfile from + * @return a new AudioProfile created from the data in the parcel + */ + public AudioProfile createFromParcel(Parcel p) { + return new AudioProfile(p); + } + + public AudioProfile[] newArray(int size) { + return new AudioProfile[size]; + } + }; } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 1b46a50d7886..536b4ad71285 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -26,10 +26,12 @@ import android.bluetooth.BluetoothLeAudioCodecConfig; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; +import android.media.audio.common.AidlConversion; import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.os.Build; import android.os.IBinder; +import android.os.Parcel; import android.os.Vibrator; import android.telephony.TelephonyManager; import android.util.Log; @@ -1555,9 +1557,24 @@ public class AudioSystem * {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED} */ @UnsupportedAppUsage - public static native int setDeviceConnectionState(int device, int state, - String device_address, String device_name, - int codecFormat); + public static int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + android.media.audio.common.AudioPort port = + AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + Parcel parcel = Parcel.obtain(); + port.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + try { + return setDeviceConnectionState(state, parcel, codecFormat); + } finally { + parcel.recycle(); + } + } + /** + * @hide + */ + @UnsupportedAppUsage + public static native int setDeviceConnectionState(int state, Parcel parcel, int codecFormat); /** @hide */ @UnsupportedAppUsage public static native int getDeviceConnectionState(int device, String device_address); diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BluetoothProfileConnectionInfo.aidl index 047f06be0964..0617084fd826 100644 --- a/media/java/android/media/BtProfileConnectionInfo.aidl +++ b/media/java/android/media/BluetoothProfileConnectionInfo.aidl @@ -16,5 +16,5 @@ package android.media; -parcelable BtProfileConnectionInfo; +parcelable BluetoothProfileConnectionInfo; diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BluetoothProfileConnectionInfo.java index 88b9777e911d..c14884657ddd 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BluetoothProfileConnectionInfo.java @@ -26,15 +26,14 @@ import android.os.Parcelable; * {@hide} */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) -public final class BtProfileConnectionInfo implements Parcelable { - +public final class BluetoothProfileConnectionInfo implements Parcelable { private final int mProfile; private final boolean mSupprNoisy; private final int mVolume; private final boolean mIsLeOutput; - private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume, - boolean isLeOutput) { + private BluetoothProfileConnectionInfo(int profile, boolean suppressNoisyIntent, + int volume, boolean isLeOutput) { mProfile = profile; mSupprNoisy = suppressNoisyIntent; mVolume = volume; @@ -45,21 +44,21 @@ public final class BtProfileConnectionInfo implements Parcelable { * Constructor used by BtHelper when a profile is connected * {@hide} */ - public BtProfileConnectionInfo(int profile) { + public BluetoothProfileConnectionInfo(int profile) { this(profile, false, -1, false); } - public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR = - new Parcelable.Creator<BtProfileConnectionInfo>() { + public static final @NonNull Parcelable.Creator<BluetoothProfileConnectionInfo> CREATOR = + new Parcelable.Creator<BluetoothProfileConnectionInfo>() { @Override - public BtProfileConnectionInfo createFromParcel(Parcel source) { - return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(), - source.readInt(), source.readBoolean()); + public BluetoothProfileConnectionInfo createFromParcel(Parcel source) { + return new BluetoothProfileConnectionInfo(source.readInt(), + source.readBoolean(), source.readInt(), source.readBoolean()); } @Override - public BtProfileConnectionInfo[] newArray(int size) { - return new BtProfileConnectionInfo[size]; + public BluetoothProfileConnectionInfo[] newArray(int size) { + return new BluetoothProfileConnectionInfo[size]; } }; @@ -84,10 +83,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param volume of device -1 to ignore value */ - public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent, - int volume) { - return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume, - false); + public static @NonNull BluetoothProfileConnectionInfo createA2dpInfo( + boolean suppressNoisyIntent, int volume) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, + volume, false); } /** @@ -96,8 +95,8 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param volume of device -1 to ignore value */ - public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) { - return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false); + public static @NonNull BluetoothProfileConnectionInfo createA2dpSinkInfo(int volume) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false); } /** @@ -106,9 +105,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} * intent will not be sent. */ - public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) { - return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1, - false); + public static @NonNull BluetoothProfileConnectionInfo createHearingAidInfo( + boolean suppressNoisyIntent) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, + -1, false); } /** @@ -119,10 +119,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param isLeOutput if true mean the device is an output device, if false it's an input device */ - public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent, - boolean isLeOutput) { - return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1, - isLeOutput); + public static @NonNull BluetoothProfileConnectionInfo createLeAudioInfo( + boolean suppressNoisyIntent, boolean isLeOutput) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, + -1, isLeOutput); } /** @@ -136,7 +136,7 @@ public final class BtProfileConnectionInfo implements Parcelable { * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be * sent */ - public boolean getSuppressNoisyIntent() { + public boolean isSuppressNoisyIntent() { return mSupprNoisy; } @@ -153,7 +153,7 @@ public final class BtProfileConnectionInfo implements Parcelable { * @return {@code true} is the LE device is an output device, {@code false} if it's an input * device */ - public boolean getIsLeOutput() { + public boolean isLeOutput() { return mIsLeOutput; } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 96199a988704..fec14def618c 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -25,7 +25,7 @@ import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -215,8 +215,7 @@ interface IAudioService { IRingtonePlayer getRingtonePlayer(); int getUiSoundsStreamType(); - void setWiredDeviceConnectionState(int type, int state, String address, String name, - String caller); + void setWiredDeviceConnectionState(in AudioDeviceAttributes aa, int state, String caller); @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); @@ -276,7 +275,7 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice, - in BluetoothDevice previousDevice, in BtProfileConnectionInfo info); + in BluetoothDevice previousDevice, in BluetoothProfileConnectionInfo info); oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult, in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index e2e48d35a672..5f02a430f384 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -43,6 +43,7 @@ import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.StampedLock; /** * <p>The ImageReader class allows direct application access to image data @@ -675,7 +676,8 @@ public class ImageReader implements AutoCloseable { * If no handler specified and the calling thread has no looper. */ public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) { - synchronized (mListenerLock) { + long writeStamp = mListenerLock.writeLock(); + try { if (listener != null) { Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); if (looper == null) { @@ -691,6 +693,8 @@ public class ImageReader implements AutoCloseable { mListenerExecutor = null; } mListener = listener; + } finally { + mListenerLock.unlockWrite(writeStamp); } } @@ -713,9 +717,12 @@ public class ImageReader implements AutoCloseable { throw new IllegalArgumentException("executor must not be null"); } - synchronized (mListenerLock) { + long writeStamp = mListenerLock.writeLock(); + try { mListenerExecutor = executor; mListener = listener; + } finally { + mListenerLock.unlockWrite(writeStamp); } } @@ -731,6 +738,8 @@ public class ImageReader implements AutoCloseable { /** * Callback that is called when a new image is available from ImageReader. * + * This callback must not modify or close the passed {@code reader}. + * * @param reader the ImageReader the callback is associated with. * @see ImageReader * @see Image @@ -889,28 +898,41 @@ public class ImageReader implements AutoCloseable { return; } + synchronized (ir.mCloseLock) { + if (!ir.mIsReaderValid) { + // It's dangerous to fire onImageAvailable() callback when the ImageReader + // is being closed, as application could acquire next image in the + // onImageAvailable() callback. + return; + } + } + final Executor executor; - final OnImageAvailableListener listener; - synchronized (ir.mListenerLock) { + final long readStamp = ir.mListenerLock.readLock(); + try { executor = ir.mListenerExecutor; - listener = ir.mListener; - } - final boolean isReaderValid; - synchronized (ir.mCloseLock) { - isReaderValid = ir.mIsReaderValid; + if (executor == null) { + return; + } + } finally { + ir.mListenerLock.unlockRead(readStamp); } - // It's dangerous to fire onImageAvailable() callback when the ImageReader - // is being closed, as application could acquire next image in the - // onImageAvailable() callback. - if (executor != null && listener != null && isReaderValid) { - executor.execute(new Runnable() { - @Override - public void run() { - listener.onImageAvailable(ir); + executor.execute(() -> { + // Acquire readlock to ensure that the ImageReader does not change its + // state while a listener is actively processing. + final long rStamp = ir.mListenerLock.readLock(); + try { + // Fire onImageAvailable of the latest non-null listener + // This ensures that if the listener changes while messages are in queue, the + // in-flight messages will call onImageAvailable of the new listener instead + if (ir.mListener != null) { + ir.mListener.onImageAvailable(ir); } - }); - } + } finally { + ir.mListenerLock.unlockRead(rStamp); + } + }); } /** @@ -1070,7 +1092,7 @@ public class ImageReader implements AutoCloseable { private Surface mSurface; private int mEstimatedNativeAllocBytes; - private final Object mListenerLock = new Object(); + private final StampedLock mListenerLock = new StampedLock(); private final Object mCloseLock = new Object(); private boolean mIsReaderValid = false; private OnImageAvailableListener mListener; diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java index 1053fb717fda..f17189dedcba 100644 --- a/media/java/android/media/audio/common/AidlConversion.java +++ b/media/java/android/media/audio/common/AidlConversion.java @@ -17,12 +17,17 @@ package android.media.audio.common; import android.annotation.NonNull; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; import android.media.AudioFormat; +import android.media.AudioSystem; import android.media.MediaFormat; import android.os.Parcel; import com.android.internal.annotations.VisibleForTesting; +import java.util.stream.Collectors; + /** * This class provides utility functions for converting between * the AIDL types defined in 'android.media.audio.common' and: @@ -525,6 +530,351 @@ public class AidlConversion { } } + /** + * Convert from SDK AudioDeviceAttributes to AIDL AudioPort. + */ + public static AudioPort api2aidl_AudioDeviceAttributes_AudioPort( + @NonNull AudioDeviceAttributes attributes) { + AudioPort port = new AudioPort(); + port.name = attributes.getName(); + // TO DO: b/211611504 Convert attributes.getAudioProfiles() to AIDL as well. + port.profiles = new AudioProfile[]{}; + port.extraAudioDescriptors = attributes.getAudioDescriptors().stream() + .map(descriptor -> api2aidl_AudioDescriptor_ExtraAudioDescriptor(descriptor)) + .collect(Collectors.toList()).toArray(ExtraAudioDescriptor[]::new); + port.flags = new AudioIoFlags(); + port.gains = new AudioGain[]{}; + AudioPortDeviceExt deviceExt = new AudioPortDeviceExt(); + deviceExt.device = new AudioDevice(); + deviceExt.encodedFormats = new AudioFormatDescription[]{}; + deviceExt.device.type = + api2aidl_NativeType_AudioDeviceDescription(attributes.getInternalType()); + deviceExt.device.address = AudioDeviceAddress.id(attributes.getAddress()); + port.ext = AudioPortExt.device(deviceExt); + return port; + } + + /** + * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor. + */ + public static ExtraAudioDescriptor api2aidl_AudioDescriptor_ExtraAudioDescriptor( + @NonNull AudioDescriptor descriptor) { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = + api2aidl_AudioDescriptorStandard_AudioStandard(descriptor.getStandard()); + extraDescriptor.audioDescriptor = descriptor.getDescriptor(); + extraDescriptor.encapsulationType = + api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType( + descriptor.getEncapsulationType()); + return extraDescriptor; + } + + /** + * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor. + */ + public static @NonNull AudioDescriptor aidl2api_ExtraAudioDescriptor_AudioDescriptor( + @NonNull ExtraAudioDescriptor extraDescriptor) { + AudioDescriptor descriptor = new AudioDescriptor( + aidl2api_AudioStandard_AudioDescriptorStandard(extraDescriptor.standard), + aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType( + extraDescriptor.encapsulationType), + extraDescriptor.audioDescriptor); + return descriptor; + } + + /** + * Convert from SDK AudioDescriptor#mStandard to AIDL AudioStandard + */ + @AudioStandard + public static int api2aidl_AudioDescriptorStandard_AudioStandard( + @AudioDescriptor.AudioDescriptorStandard int standard) { + switch (standard) { + case AudioDescriptor.STANDARD_EDID: + return AudioStandard.EDID; + case AudioDescriptor.STANDARD_NONE: + default: + return AudioStandard.NONE; + } + } + + /** + * Convert from AIDL AudioStandard to SDK AudioDescriptor#mStandard + */ + @AudioDescriptor.AudioDescriptorStandard + public static int aidl2api_AudioStandard_AudioDescriptorStandard(@AudioStandard int standard) { + switch (standard) { + case AudioStandard.EDID: + return AudioDescriptor.STANDARD_EDID; + case AudioStandard.NONE: + default: + return AudioDescriptor.STANDARD_NONE; + } + } + + /** + * Convert from SDK AudioProfile.EncapsulationType to AIDL AudioEncapsulationType + */ + @AudioEncapsulationType + public static int api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType( + @android.media.AudioProfile.EncapsulationType int type) { + switch (type) { + case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937: + return AudioEncapsulationType.IEC61937; + case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE: + default: + return AudioEncapsulationType.NONE; + } + } + + /** + * Convert from AIDL AudioEncapsulationType to SDK AudioProfile.EncapsulationType + */ + @android.media.AudioProfile.EncapsulationType + public static int aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType( + @AudioEncapsulationType int type) { + switch (type) { + case AudioEncapsulationType.IEC61937: + return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937; + case AudioEncapsulationType.NONE: + default: + return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE; + } + } + + /** + * Convert from SDK native type to AIDL AudioDeviceDescription + */ + public static AudioDeviceDescription api2aidl_NativeType_AudioDeviceDescription( + int nativeType) { + AudioDeviceDescription aidl = new AudioDeviceDescription(); + aidl.connection = ""; + switch (nativeType) { + case AudioSystem.DEVICE_OUT_EARPIECE: + aidl.type = AudioDeviceType.OUT_SPEAKER_EARPIECE; + break; + case AudioSystem.DEVICE_OUT_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + break; + case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE: + aidl.type = AudioDeviceType.OUT_HEADPHONE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + aidl.type = AudioDeviceType.OUT_CARKIT; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + aidl.type = AudioDeviceType.OUT_HEADPHONE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_TELEPHONY_TX: + aidl.type = AudioDeviceType.OUT_TELEPHONY_TX; + break; + case AudioSystem.DEVICE_OUT_AUX_LINE: + aidl.type = AudioDeviceType.OUT_LINE_AUX; + break; + case AudioSystem.DEVICE_OUT_SPEAKER_SAFE: + aidl.type = AudioDeviceType.OUT_SPEAKER_SAFE; + break; + case AudioSystem.DEVICE_OUT_HEARING_AID: + aidl.type = AudioDeviceType.OUT_HEARING_AID; + aidl.connection = AudioDeviceDescription.CONNECTION_WIRELESS; + break; + case AudioSystem.DEVICE_OUT_ECHO_CANCELLER: + aidl.type = AudioDeviceType.OUT_ECHO_CANCELLER; + break; + case AudioSystem.DEVICE_OUT_BLE_SPEAKER: + aidl.type = AudioDeviceType.OUT_SPEAKER; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_BUILTIN_MIC: + aidl.type = AudioDeviceType.IN_MICROPHONE; + break; + case AudioSystem.DEVICE_IN_BACK_MIC: + aidl.type = AudioDeviceType.IN_MICROPHONE_BACK; + break; + case AudioSystem.DEVICE_IN_TELEPHONY_RX: + aidl.type = AudioDeviceType.IN_TELEPHONY_RX; + break; + case AudioSystem.DEVICE_IN_TV_TUNER: + aidl.type = AudioDeviceType.IN_TV_TUNER; + break; + case AudioSystem.DEVICE_IN_LOOPBACK: + aidl.type = AudioDeviceType.IN_LOOPBACK; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_BLE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_ECHO_REFERENCE: + aidl.type = AudioDeviceType.IN_ECHO_REFERENCE; + break; + case AudioSystem.DEVICE_IN_WIRED_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_WIRED_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO; + break; + case AudioSystem.DEVICE_IN_HDMI: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI; + break; + case AudioSystem.DEVICE_OUT_HDMI: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI; + break; + case AudioSystem.DEVICE_IN_REMOTE_SUBMIX: + aidl.type = AudioDeviceType.IN_SUBMIX; + break; + case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX: + aidl.type = AudioDeviceType.OUT_SUBMIX; + break; + case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET: + aidl.type = AudioDeviceType.IN_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET: + aidl.type = AudioDeviceType.OUT_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET: + aidl.type = AudioDeviceType.IN_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET: + aidl.type = AudioDeviceType.OUT_DOCK; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_USB_ACCESSORY: + aidl.type = AudioDeviceType.IN_ACCESSORY; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_ACCESSORY: + aidl.type = AudioDeviceType.OUT_ACCESSORY; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_USB_DEVICE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_DEVICE: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_FM_TUNER: + aidl.type = AudioDeviceType.IN_FM_TUNER; + break; + case AudioSystem.DEVICE_OUT_FM: + aidl.type = AudioDeviceType.OUT_FM; + break; + case AudioSystem.DEVICE_IN_LINE: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_OUT_LINE: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG; + break; + case AudioSystem.DEVICE_IN_SPDIF: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF; + break; + case AudioSystem.DEVICE_OUT_SPDIF: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF; + break; + case AudioSystem.DEVICE_IN_BLUETOOTH_A2DP: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP; + break; + case AudioSystem.DEVICE_IN_IP: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4; + break; + case AudioSystem.DEVICE_OUT_IP: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4; + break; + case AudioSystem.DEVICE_IN_BUS: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BUS; + break; + case AudioSystem.DEVICE_OUT_BUS: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_BUS; + break; + case AudioSystem.DEVICE_IN_PROXY: + aidl.type = AudioDeviceType.IN_AFE_PROXY; + break; + case AudioSystem.DEVICE_OUT_PROXY: + aidl.type = AudioDeviceType.OUT_AFE_PROXY; + break; + case AudioSystem.DEVICE_IN_USB_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_OUT_USB_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_USB; + break; + case AudioSystem.DEVICE_IN_HDMI_ARC: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC; + break; + case AudioSystem.DEVICE_OUT_HDMI_ARC: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC; + break; + case AudioSystem.DEVICE_IN_HDMI_EARC: + aidl.type = AudioDeviceType.IN_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC; + break; + case AudioSystem.DEVICE_OUT_HDMI_EARC: + aidl.type = AudioDeviceType.OUT_DEVICE; + aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC; + break; + case AudioSystem.DEVICE_IN_BLE_HEADSET: + aidl.type = AudioDeviceType.IN_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_OUT_BLE_HEADSET: + aidl.type = AudioDeviceType.OUT_HEADSET; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; + case AudioSystem.DEVICE_IN_DEFAULT: + aidl.type = AudioDeviceType.IN_DEFAULT; + break; + case AudioSystem.DEVICE_OUT_DEFAULT: + aidl.type = AudioDeviceType.OUT_DEFAULT; + break; + default: + aidl.type = AudioDeviceType.NONE; + } + return aidl; + } + private static native int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t( Parcel aidl, boolean isInput); private static native Parcel legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel( diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java index 0f5bf082d44f..14a9144c9cdc 100644 --- a/media/java/android/media/tv/tuner/DemuxCapabilities.java +++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java @@ -36,13 +36,8 @@ import java.lang.annotation.RetentionPolicy; public class DemuxCapabilities { /** @hide */ - @IntDef(flag = true, value = { - Filter.TYPE_TS, - Filter.TYPE_MMTP, - Filter.TYPE_IP, - Filter.TYPE_TLV, - Filter.TYPE_ALP - }) + @IntDef(value = {Filter.TYPE_TS, Filter.TYPE_MMTP, Filter.TYPE_IP, Filter.TYPE_TLV, + Filter.TYPE_ALP}) @Retention(RetentionPolicy.SOURCE) public @interface FilterCapabilities {} diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 9f4423644877..14accaafe189 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -153,8 +153,8 @@ public class Filter implements AutoCloseable { /** @hide */ - @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_DATA_READY, STATUS_LOW_WATER, - STATUS_HIGH_WATER, STATUS_OVERFLOW}) + @IntDef(prefix = "STATUS_", + value = {STATUS_DATA_READY, STATUS_LOW_WATER, STATUS_HIGH_WATER, STATUS_OVERFLOW}) @Retention(RetentionPolicy.SOURCE) public @interface Status {} @@ -185,8 +185,7 @@ public class Filter implements AutoCloseable { public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW; /** @hide */ - @IntDef(flag = true, - prefix = "SCRAMBLING_STATUS_", + @IntDef(prefix = "SCRAMBLING_STATUS_", value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED, SCRAMBLING_STATUS_SCRAMBLED}) @Retention(RetentionPolicy.SOURCE) @@ -209,8 +208,7 @@ public class Filter implements AutoCloseable { android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED; /** @hide */ - @IntDef(flag = true, - prefix = "MONITOR_EVENT_", + @IntDef(prefix = "MONITOR_EVENT_", value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) @Retention(RetentionPolicy.SOURCE) public @interface MonitorEventMask {} diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java index d34581da29cb..b16d9fb247b7 100644 --- a/media/java/android/media/tv/tuner/filter/RecordSettings.java +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -40,8 +40,7 @@ public class RecordSettings extends Settings { * * @hide */ - @IntDef(flag = true, - value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR, + @IntDef(value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR, TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED, TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR, TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR, @@ -165,7 +164,6 @@ public class RecordSettings extends Settings { * @hide */ @IntDef(prefix = "SC_INDEX_", - flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME, SC_INDEX_SEQUENCE, SC_INDEX_I_SLICE, SC_INDEX_P_SLICE, SC_INDEX_B_SLICE, SC_INDEX_SI_SLICE, SC_INDEX_SP_SLICE}) @@ -214,8 +212,7 @@ public class RecordSettings extends Settings { * * @hide */ - @IntDef(flag = true, - value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, + @IntDef(value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP, SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP, SC_HEVC_INDEX_SLICE_TRAIL_CRA}) @@ -258,8 +255,7 @@ public class RecordSettings extends Settings { /** * @hide */ - @IntDef(flag = true, - prefix = "SC_", + @IntDef(prefix = "SC_", value = { SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, diff --git a/media/java/android/media/tv/tuner/filter/SharedFilter.java b/media/java/android/media/tv/tuner/filter/SharedFilter.java index 740ab9c45c83..21964ee5a32a 100644 --- a/media/java/android/media/tv/tuner/filter/SharedFilter.java +++ b/media/java/android/media/tv/tuner/filter/SharedFilter.java @@ -38,7 +38,7 @@ import java.util.concurrent.Executor; @SystemApi public final class SharedFilter implements AutoCloseable { /** @hide */ - @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_INACCESSIBLE}) + @IntDef(prefix = "STATUS_", value = {STATUS_INACCESSIBLE}) @Retention(RetentionPolicy.SOURCE) public @interface Status {} diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index e0405ef57f2e..6c1134ae8fac 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -36,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class AnalogFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "SIGNAL_TYPE_", + @IntDef(prefix = "SIGNAL_TYPE_", value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_AUTO, SIGNAL_TYPE_PAL, SIGNAL_TYPE_PAL_M, SIGNAL_TYPE_PAL_N, SIGNAL_TYPE_PAL_60, SIGNAL_TYPE_NTSC, SIGNAL_TYPE_NTSC_443, SIGNAL_TYPE_SECAM}) @@ -82,8 +81,7 @@ public class AnalogFrontendSettings extends FrontendSettings { public static final int SIGNAL_TYPE_SECAM = FrontendAnalogType.SECAM; /** @hide */ - @IntDef(flag = true, - prefix = "SIF_", + @IntDef(prefix = "SIF_", value = {SIF_UNDEFINED, SIF_AUTO, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK, SIF_DK1_A2, SIF_DK2_A2, SIF_DK3_A2, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2, SIF_M_EIAJ, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME}) diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java index a7157e20c5e6..c99f911c4236 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java @@ -39,8 +39,7 @@ import java.lang.annotation.RetentionPolicy; public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_BANDWIDTH_6MHZ, BANDWIDTH_BANDWIDTH_7MHZ, BANDWIDTH_BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -69,8 +68,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM, MODULATION_MOD_256QAM, @@ -113,8 +111,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_CTI, TIME_INTERLEAVE_MODE_HTI}) @Retention(RetentionPolicy.SOURCE) @@ -140,8 +137,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_15, CODERATE_3_15, CODERATE_4_15, CODERATE_5_15, CODERATE_6_15, CODERATE_7_15, CODERATE_8_15, CODERATE_9_15, CODERATE_10_15, CODERATE_11_15, CODERATE_12_15, CODERATE_13_15}) @@ -207,8 +203,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "FEC_", + @IntDef(prefix = "FEC_", value = {FEC_UNDEFINED, FEC_AUTO, FEC_BCH_LDPC_16K, FEC_BCH_LDPC_64K, FEC_CRC_LDPC_16K, FEC_CRC_LDPC_64K, FEC_LDPC_16K, FEC_LDPC_64K}) @Retention(RetentionPolicy.SOURCE) @@ -249,8 +244,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "DEMOD_OUTPUT_FORMAT_", + @IntDef(prefix = "DEMOD_OUTPUT_FORMAT_", value = {DEMOD_OUTPUT_FORMAT_UNDEFINED, DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET, DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java index 3071ce861e0d..64c6ce629740 100644 --- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java @@ -34,8 +34,7 @@ import java.lang.annotation.RetentionPolicy; public class AtscFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_8VSB, MODULATION_MOD_16VSB}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java index 6b5d6ca9eb84..07c1fbffd91e 100644 --- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java @@ -43,8 +43,7 @@ import java.lang.annotation.RetentionPolicy; public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) public @interface Bandwidth {} @@ -68,8 +67,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720}) @Retention(RetentionPolicy.SOURCE) @@ -97,8 +95,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "GUARD_INTERVAL_", + @IntDef(prefix = "GUARD_INTERVAL_", value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST, GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST, @@ -143,8 +140,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO, MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR, MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM, @@ -187,8 +183,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { FrontendDtmbModulation.CONSTELLATION_64QAM; /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5}) @Retention(RetentionPolicy.SOURCE) public @interface CodeRate {} @@ -215,8 +210,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { public static final int CODERATE_4_5 = FrontendDtmbCodeRate.CODERATE_4_5; /** @hide */ - @IntDef(flag = true, - prefix = "TRANSMISSION_MODE_", + @IntDef(prefix = "TRANSMISSION_MODE_", value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index afe953de5389..45bfc09b8ae7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -40,8 +40,7 @@ import java.lang.annotation.RetentionPolicy; public class DvbcFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_16QAM, MODULATION_MOD_32QAM, MODULATION_MOD_64QAM, MODULATION_MOD_128QAM, MODULATION_MOD_256QAM}) @@ -98,8 +97,7 @@ public class DvbcFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "ANNEX_", + @IntDef(prefix = "ANNEX_", value = {ANNEX_UNDEFINED, ANNEX_A, ANNEX_B, ANNEX_C}) @Retention(RetentionPolicy.SOURCE) public @interface Annex {} @@ -159,8 +157,7 @@ public class DvbcFrontendSettings extends FrontendSettings { android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED; /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1, TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4, @@ -226,8 +223,7 @@ public class DvbcFrontendSettings extends FrontendSettings { FrontendCableTimeInterleaveMode.INTERLEAVING_128_4; /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ, BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index e16f19285100..56dbb480880b 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -42,8 +42,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "SCAN_TYPE_", + @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC, SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS}) @Retention(RetentionPolicy.SOURCE) @@ -75,8 +74,7 @@ public class DvbsFrontendSettings extends FrontendSettings { public static final int SCAN_TYPE_JESS = FrontendDvbsScanType.JESS; /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK, MODULATION_MOD_32PSK, MODULATION_MOD_ACM, MODULATION_MOD_8APSK, @@ -207,8 +205,7 @@ public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "STANDARD_", + @IntDef(prefix = "STANDARD_", value = {STANDARD_AUTO, STANDARD_S, STANDARD_S2, STANDARD_S2X}) @Retention(RetentionPolicy.SOURCE) public @interface Standard {} diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index d86e9a8af5f6..06547e1fc0e7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -42,8 +42,7 @@ import java.lang.annotation.RetentionPolicy; public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TRANSMISSION_MODE_", + @IntDef(prefix = "TRANSMISSION_MODE_", value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_1K, TRANSMISSION_MODE_16K, TRANSMISSION_MODE_32K}) @@ -98,8 +97,7 @@ public class DvbtFrontendSettings extends FrontendSettings { FrontendDvbtTransmissionMode.MODE_32K_E; /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ, BANDWIDTH_6MHZ, BANDWIDTH_5MHZ, BANDWIDTH_1_7MHZ, BANDWIDTH_10MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -140,8 +138,7 @@ public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CONSTELLATION_", + @IntDef(prefix = "CONSTELLATION_", value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK, CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM, CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R, @@ -192,8 +189,7 @@ public class DvbtFrontendSettings extends FrontendSettings { FrontendDvbtConstellation.CONSTELLATION_256QAM_R; /** @hide */ - @IntDef(flag = true, - prefix = "HIERARCHY_", + @IntDef(prefix = "HIERARCHY_", value = {HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE, HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH, HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH}) @@ -243,8 +239,7 @@ public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, CODERATE_5_6, CODERATE_7_8, CODERATE_3_5, CODERATE_4_5, CODERATE_6_7, CODERATE_8_9}) @Retention(RetentionPolicy.SOURCE) @@ -296,8 +291,7 @@ public class DvbtFrontendSettings extends FrontendSettings { public static final int CODERATE_8_9 = FrontendDvbtCoderate.CODERATE_8_9; /** @hide */ - @IntDef(flag = true, - prefix = "GUARD_INTERVAL_", + @IntDef(prefix = "GUARD_INTERVAL_", value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4, @@ -346,8 +340,7 @@ public class DvbtFrontendSettings extends FrontendSettings { public static final int GUARD_INTERVAL_19_256 = FrontendDvbtGuardInterval.INTERVAL_19_256; /** @hide */ - @IntDef(flag = true, - prefix = "STANDARD", + @IntDef(prefix = "STANDARD_", value = {STANDARD_AUTO, STANDARD_T, STANDARD_T2} ) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java index 38bffec3f77f..2f45a7072017 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -89,8 +89,7 @@ public abstract class FrontendSettings { /** @hide */ - @LongDef(flag = true, - prefix = "FEC_", + @LongDef(prefix = "FEC_", value = {FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5, FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8, FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20, 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/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java index 726fe15b8edb..7e83d1545032 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java @@ -36,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class Isdbs3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK, MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16APSK, MODULATION_MOD_32APSK}) @@ -75,8 +74,7 @@ public class Isdbs3FrontendSettings extends FrontendSettings { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_3, CODERATE_2_5, CODERATE_1_2, CODERATE_3_5, CODERATE_2_3, CODERATE_3_4, CODERATE_7_9, CODERATE_4_5, CODERATE_5_6, CODERATE_7_8, CODERATE_9_10}) diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java index 51ec5aeeb2eb..50294539e8f8 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java @@ -54,8 +54,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK, MODULATION_MOD_QPSK, MODULATION_MOD_TC8PSK}) @Retention(RetentionPolicy.SOURCE) @@ -84,8 +83,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, CODERATE_5_6, CODERATE_7_8}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java index 89512a05674b..f08a51497f75 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java @@ -40,8 +40,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_DQPSK, MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM}) @Retention(RetentionPolicy.SOURCE) @@ -74,8 +73,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODE_", + @IntDef(prefix = "MODE_", value = {MODE_UNDEFINED, MODE_AUTO, MODE_1, MODE_2, MODE_3}) @Retention(RetentionPolicy.SOURCE) public @interface Mode {} @@ -103,8 +101,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ, BANDWIDTH_6MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -132,7 +129,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ; /** @hide */ - @IntDef(flag = true, prefix = "PARTIAL_RECEPTION_FLAG_", + @IntDef(prefix = "PARTIAL_RECEPTION_FLAG_", value = {PARTIAL_RECEPTION_FLAG_UNDEFINED, PARTIAL_RECEPTION_FLAG_FALSE, PARTIAL_RECEPTION_FLAG_TRUE}) @Retention(RetentionPolicy.SOURCE) @@ -153,7 +150,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { public static final int PARTIAL_RECEPTION_FLAG_TRUE = FrontendIsdbtPartialReceptionFlag.TRUE; /** @hide */ - @IntDef(flag = true, prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_1_0, TIME_INTERLEAVE_MODE_1_4, TIME_INTERLEAVE_MODE_1_8, TIME_INTERLEAVE_MODE_1_16, TIME_INTERLEAVE_MODE_2_0, TIME_INTERLEAVE_MODE_2_2, diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index d8f48c2cf0c6..20d711cf4c54 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -103,6 +103,7 @@ public class MtpDatabase implements AutoCloseable { private int mDeviceType; private String mHostType; private boolean mSkipThumbForHost = false; + private volatile boolean mHostIsWindows = false; private MtpServer mServer; private MtpStorageManager mManager; @@ -358,7 +359,7 @@ public class MtpDatabase implements AutoCloseable { } public void addStorage(StorageVolume storage) { - MtpStorage mtpStorage = mManager.addMtpStorage(storage); + MtpStorage mtpStorage = mManager.addMtpStorage(storage, () -> mHostIsWindows); mStorageMap.put(storage.getPath(), mtpStorage); if (mServer != null) { mServer.addStorage(mtpStorage); @@ -413,6 +414,7 @@ public class MtpDatabase implements AutoCloseable { } mHostType = ""; mSkipThumbForHost = false; + mHostIsWindows = false; } @VisibleForNative @@ -736,10 +738,12 @@ public class MtpDatabase implements AutoCloseable { : MtpConstants.RESPONSE_GENERAL_ERROR); case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO: mHostType = stringValue; + Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property) + + "=" + stringValue); if (stringValue.startsWith("Android/")) { - Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property) - + "=" + stringValue); mSkipThumbForHost = true; + } else if (stringValue.startsWith("Windows/")) { + mHostIsWindows = true; } return MtpConstants.RESPONSE_OK; } diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 88c32a3ea72b..a3754e90a875 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -19,6 +19,8 @@ package android.mtp; import android.compat.annotation.UnsupportedAppUsage; import android.os.storage.StorageVolume; +import java.util.function.Supplier; + /** * This class represents a storage unit on an MTP device. * Used only for MTP support in USB responder mode. @@ -33,14 +35,16 @@ public class MtpStorage { private final boolean mRemovable; private final long mMaxFileSize; private final String mVolumeName; + private final Supplier<Boolean> mIsHostWindows; - public MtpStorage(StorageVolume volume, int storageId) { + public MtpStorage(StorageVolume volume, int storageId, Supplier<Boolean> isHostWindows) { mStorageId = storageId; mPath = volume.getPath(); mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); mVolumeName = volume.getMediaStoreVolumeName(); + mIsHostWindows = isHostWindows; } /** @@ -93,4 +97,13 @@ public class MtpStorage { public String getVolumeName() { return mVolumeName; } + + /** + * Returns true if the mtp host of this storage is Windows. + * + * @return is host Windows + */ + public boolean isHostWindows() { + return mIsHostWindows.get(); + } } diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java index 0bede0dccbed..e9426cf2ce31 100644 --- a/media/java/android/mtp/MtpStorageManager.java +++ b/media/java/android/mtp/MtpStorageManager.java @@ -18,7 +18,11 @@ package android.mtp; import android.media.MediaFile; import android.os.FileObserver; +import android.os.SystemProperties; import android.os.storage.StorageVolume; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.util.Log; import com.android.internal.util.Preconditions; @@ -35,6 +39,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Supplier; /** * MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of @@ -199,7 +204,38 @@ public class MtpStorageManager { } public long getSize() { - return mIsDir ? 0 : getPath().toFile().length(); + return mIsDir ? 0 : maybeApplyTranscodeLengthWorkaround(getPath().toFile().length()); + } + + private long maybeApplyTranscodeLengthWorkaround(long length) { + // Windows truncates transferred files to the size advertised in the object property. + if (mStorage.isHostWindows() && isTranscodeMtpEnabled() && isFileTranscodeSupported()) { + // If the file supports transcoding, we double the returned size to accommodate + // the increase in size from transcoding to AVC. This is the same heuristic + // applied in the FUSE daemon (MediaProvider). + return length * 2; + } + return length; + } + + private boolean isTranscodeMtpEnabled() { + return SystemProperties.getBoolean("sys.fuse.transcode_mtp", false); + } + + private boolean isFileTranscodeSupported() { + // Check if the file supports transcoding by reading the |st_nlinks| struct stat + // field. This will be > 1 if the file supports transcoding. The FUSE daemon + // sets the field accordingly to enable the MTP stack workaround some Windows OS + // MTP client bug where they ignore the size returned as part of getting the MTP + // object, see MtpServer#doGetObject. + final Path path = getPath(); + try { + StructStat stat = Os.stat(path.toString()); + return stat.st_nlink > 1; + } catch (ErrnoException e) { + Log.w(TAG, "Failed to stat path: " + getPath() + ". Ignoring transcoding."); + return false; + } } public Path getPath() { @@ -420,10 +456,12 @@ public class MtpStorageManager { * @param volume Storage to add. * @return the associated MtpStorage */ - public synchronized MtpStorage addMtpStorage(StorageVolume volume) { + public synchronized MtpStorage addMtpStorage(StorageVolume volume, + Supplier<Boolean> isHostWindows) { int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1; - MtpStorage storage = new MtpStorage(volume, storageId); - MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true); + MtpStorage storage = new MtpStorage(volume, storageId, isHostWindows); + MtpObject root = new MtpObject(storage.getPath(), storageId, storage, /* parent= */ null, + /* isDir= */ true); mRoots.put(storageId, root); return storage; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java index fd66d3b9904e..f23794b50543 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java @@ -19,7 +19,7 @@ package com.android.mediaframeworktest.unit; import static org.junit.Assert.assertEquals; import android.bluetooth.BluetoothProfile; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import androidx.test.runner.AndroidJUnit4; @@ -27,22 +27,24 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) -public class BtProfileConnectionInfoTest { +public class BluetoothProfileConnectionInfoTest { @Test public void testCoverageA2dp() { final boolean supprNoisy = false; final int volume = 42; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createA2dpInfo(supprNoisy, volume); assertEquals(info.getProfile(), BluetoothProfile.A2DP); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); assertEquals(info.getVolume(), volume); } @Test public void testCoverageA2dpSink() { final int volume = 42; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createA2dpSinkInfo(volume); assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK); assertEquals(info.getVolume(), volume); } @@ -50,20 +52,21 @@ public class BtProfileConnectionInfoTest { @Test public void testCoveragehearingAid() { final boolean supprNoisy = true; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createHearingAidInfo(supprNoisy); assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); } @Test public void testCoverageLeAudio() { final boolean supprNoisy = false; final boolean isLeOutput = true; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy, - isLeOutput); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createLeAudioInfo(supprNoisy, isLeOutput); assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); - assertEquals(info.getIsLeOutput(), isLeOutput); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isLeOutput(), isLeOutput); } } diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java index abdc7e559fb3..a6a5568ac8f4 100644 --- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java +++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java @@ -160,10 +160,11 @@ public class MtpStorageManagerTest { Log.d(TAG, "sendObjectInfoChanged: " + id); objectsInfoChanged.add(id); } - }, null); + }, /* subdirectories= */ null); - mainMtpStorage = manager.addMtpStorage(mainStorage); - secondaryMtpStorage = manager.addMtpStorage(secondaryStorage); + mainMtpStorage = manager.addMtpStorage(mainStorage, /* isHostWindows= */ () -> false); + secondaryMtpStorage = manager.addMtpStorage(secondaryStorage, + /* isHostWindows= */ () -> false); } @After diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java index 414de89f8218..09573909c288 100644 --- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java +++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java @@ -16,8 +16,18 @@ package android.media.audio.common; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + import android.media.AudioAttributes; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; +import android.media.AudioProfile; import android.media.AudioSystem; import android.media.AudioTrack; import android.media.MediaFormat; @@ -25,11 +35,12 @@ import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; -import static org.junit.Assert.*; - import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; + /** * Unit tests for AidlConversion utilities. * @@ -417,10 +428,102 @@ public final class AidlConversionUnitTests { () -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue)); } + @Test + public void testAudioDescriptorConversion_Default() { + ExtraAudioDescriptor aidl = createDefaultDescriptor(); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_NONE, audioDescriptor.getStandard()); + assertEquals( + AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDescriptorConversion() { + ExtraAudioDescriptor aidl = createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A}); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_EDID, audioDescriptor.getStandard()); + assertEquals(AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937, + audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDeviceAttributesConversion_Default() { + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_DEFAULT, "myAddress"); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + assertEquals("", port.name); + assertEquals(0, port.extraAudioDescriptors.length); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals("", port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEFAULT, port.ext.getDevice().device.type.type); + } + + @Test + public void testAudioDeviceAttributesConversion() { + AudioDescriptor audioDescriptor1 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A})); + + AudioDescriptor audioDescriptor2 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createDefaultDescriptor()); + + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HDMI_ARC, "myAddress", "myName", new ArrayList<>(), + new ArrayList<>(Arrays.asList(audioDescriptor1, audioDescriptor2))); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort( + attributes); + assertEquals("myName", port.name); + assertEquals(2, port.extraAudioDescriptors.length); + assertEquals(AudioStandard.EDID, port.extraAudioDescriptors[0].standard); + assertEquals(AudioEncapsulationType.IEC61937, + port.extraAudioDescriptors[0].encapsulationType); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, + port.extraAudioDescriptors[0].audioDescriptor)); + assertEquals(AudioStandard.NONE, port.extraAudioDescriptors[1].standard); + assertEquals(AudioEncapsulationType.NONE, + port.extraAudioDescriptors[1].encapsulationType); + assertTrue(Arrays.equals(new byte[]{}, + port.extraAudioDescriptors[1].audioDescriptor)); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals(AudioDeviceDescription.CONNECTION_HDMI_ARC, + port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type); + } + private static AudioFormatDescription createPcm16FormatAidl() { final AudioFormatDescription aidl = new AudioFormatDescription(); aidl.type = AudioFormatType.PCM; aidl.pcm = PcmType.INT_16_BIT; return aidl; } + + private static ExtraAudioDescriptor createDefaultDescriptor() { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.NONE; + extraDescriptor.encapsulationType = AudioEncapsulationType.NONE; + extraDescriptor.audioDescriptor = new byte[]{}; + return extraDescriptor; + } + + private static ExtraAudioDescriptor createEncapsulationDescriptor(byte[] audioDescriptor) { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.EDID; + extraDescriptor.encapsulationType = AudioEncapsulationType.IEC61937; + extraDescriptor.audioDescriptor = audioDescriptor; + return extraDescriptor; + } } 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/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java index f472d563c477..e0ce081c0276 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java @@ -358,6 +358,7 @@ public class EthernetManager { return proxy; } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void updateConfiguration( @NonNull String iface, @NonNull EthernetNetworkUpdateRequest request, @@ -372,6 +373,7 @@ public class EthernetManager { } } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void connectNetwork( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, @@ -385,6 +387,7 @@ public class EthernetManager { } } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void disconnectNetwork( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java index 0f21e55b9f27..512fbcee9330 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java +++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java @@ -16,6 +16,9 @@ package android.net.nsd; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; @@ -23,15 +26,22 @@ import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.Network; +import android.net.NetworkRequest; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; @@ -278,9 +288,180 @@ public final class NsdManager { private final SparseArray mListenerMap = new SparseArray(); private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); + // Map of listener key sent by client -> per-network discovery tracker + @GuardedBy("mPerNetworkDiscoveryMap") + private final ArrayMap<Integer, PerNetworkDiscoveryTracker> + mPerNetworkDiscoveryMap = new ArrayMap<>(); private final ServiceHandler mHandler; + private class PerNetworkDiscoveryTracker { + final String mServiceType; + final int mProtocolType; + final DiscoveryListener mBaseListener; + final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners = + new ArrayMap<>(); + + final NetworkCallback mNetworkCb = new NetworkCallback() { + @Override + public void onAvailable(@NonNull Network network) { + final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener( + network, mBaseListener); + mPerNetworkListeners.put(network, wrappedListener); + discoverServices(mServiceType, mProtocolType, network, wrappedListener); + } + + @Override + public void onLost(@NonNull Network network) { + final DelegatingDiscoveryListener listener = mPerNetworkListeners.get(network); + if (listener == null) return; + listener.notifyAllServicesLost(); + // Listener will be removed from map in discovery stopped callback + stopServiceDiscovery(listener); + } + }; + + // Accessed from mHandler + private boolean mStopRequested; + + public void start(@NonNull NetworkRequest request) { + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + cm.registerNetworkCallback(request, mNetworkCb, mHandler); + mHandler.post(() -> mBaseListener.onDiscoveryStarted(mServiceType)); + } + + /** + * Stop discovery on all networks tracked by this class. + * + * This will request all underlying listeners to stop, and the last one to stop will call + * onDiscoveryStopped or onStopDiscoveryFailed. + * + * Must be called on the handler thread. + */ + public void requestStop() { + mHandler.post(() -> { + mStopRequested = true; + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + cm.unregisterNetworkCallback(mNetworkCb); + if (mPerNetworkListeners.size() == 0) { + mBaseListener.onDiscoveryStopped(mServiceType); + return; + } + for (int i = 0; i < mPerNetworkListeners.size(); i++) { + final DelegatingDiscoveryListener listener = mPerNetworkListeners.valueAt(i); + stopServiceDiscovery(listener); + } + }); + } + + private PerNetworkDiscoveryTracker(String serviceType, int protocolType, + DiscoveryListener baseListener) { + mServiceType = serviceType; + mProtocolType = protocolType; + mBaseListener = baseListener; + } + + /** + * Subset of NsdServiceInfo that is tracked to generate service lost notifications when a + * network is lost. + * + * Service lost notifications only contain service name, type and network, so only track + * that information (Network is known from the listener). This also implements + * equals/hashCode for usage in maps. + */ + private class TrackedNsdInfo { + private final String mServiceName; + private final String mServiceType; + TrackedNsdInfo(NsdServiceInfo info) { + mServiceName = info.getServiceName(); + mServiceType = info.getServiceType(); + } + + @Override + public int hashCode() { + return Objects.hash(mServiceName, mServiceType); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TrackedNsdInfo)) return false; + final TrackedNsdInfo other = (TrackedNsdInfo) obj; + return Objects.equals(mServiceName, other.mServiceName) + && Objects.equals(mServiceType, other.mServiceType); + } + } + + private class DelegatingDiscoveryListener implements DiscoveryListener { + private final Network mNetwork; + private final DiscoveryListener mWrapped; + private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>(); + + private DelegatingDiscoveryListener(Network network, DiscoveryListener listener) { + mNetwork = network; + mWrapped = listener; + } + + void notifyAllServicesLost() { + for (int i = 0; i < mFoundInfo.size(); i++) { + final TrackedNsdInfo trackedInfo = mFoundInfo.valueAt(i); + final NsdServiceInfo serviceInfo = new NsdServiceInfo( + trackedInfo.mServiceName, trackedInfo.mServiceType); + serviceInfo.setNetwork(mNetwork); + mWrapped.onServiceLost(serviceInfo); + } + } + + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + // The delegated listener is used when NsdManager takes care of starting/stopping + // discovery on multiple networks. Failure to start on one network is not a global + // failure to be reported up, as other networks may succeed: just log. + Log.e(TAG, "Failed to start discovery for " + serviceType + " on " + mNetwork + + " with code " + errorCode); + mPerNetworkListeners.remove(mNetwork); + } + + @Override + public void onDiscoveryStarted(String serviceType) { + // Wrapped listener was called upon registration, it is not called for discovery + // on each network + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Failed to stop discovery for " + serviceType + " on " + mNetwork + + " with code " + errorCode); + mPerNetworkListeners.remove(mNetwork); + if (mStopRequested && mPerNetworkListeners.size() == 0) { + // Do not report onStopDiscoveryFailed when some underlying listeners failed: + // this does not mean that all listeners did, and onStopDiscoveryFailed is not + // actionable anyway. Just report that discovery stopped. + mWrapped.onDiscoveryStopped(serviceType); + } + } + + @Override + public void onDiscoveryStopped(String serviceType) { + mPerNetworkListeners.remove(mNetwork); + if (mStopRequested && mPerNetworkListeners.size() == 0) { + mWrapped.onDiscoveryStopped(serviceType); + } + } + + @Override + public void onServiceFound(NsdServiceInfo serviceInfo) { + mFoundInfo.add(new TrackedNsdInfo(serviceInfo)); + mWrapped.onServiceFound(serviceInfo); + } + + @Override + public void onServiceLost(NsdServiceInfo serviceInfo) { + mFoundInfo.remove(new TrackedNsdInfo(serviceInfo)); + mWrapped.onServiceLost(serviceInfo); + } + } + } + /** * Create a new Nsd instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve @@ -634,6 +815,14 @@ public final class NsdManager { } /** + * Same as {@link #discoverServices(String, int, Network, DiscoveryListener)} with a null + * {@link Network}. + */ + public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { + discoverServices(serviceType, protocolType, (Network) null, listener); + } + + /** * Initiate service discovery to browse for instances of a service type. Service discovery * consumes network bandwidth and will continue until the application calls * {@link #stopServiceDiscovery}. @@ -657,11 +846,13 @@ public final class NsdManager { * @param serviceType The service type being discovered. Examples include "_http._tcp" for * http services or "_ipp._tcp" for printers * @param protocolType The service discovery protocol + * @param network Network to discover services on, or null to discover on all available networks * @param listener The listener notifies of a successful discovery and is used * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. * Cannot be null. Cannot be in use for an active service discovery. */ - public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) { + public void discoverServices(@NonNull String serviceType, int protocolType, + @Nullable Network network, @NonNull DiscoveryListener listener) { if (TextUtils.isEmpty(serviceType)) { throw new IllegalArgumentException("Service type cannot be empty"); } @@ -669,6 +860,7 @@ public final class NsdManager { NsdServiceInfo s = new NsdServiceInfo(); s.setServiceType(serviceType); + s.setNetwork(network); int key = putListener(listener, s); try { @@ -679,6 +871,67 @@ public final class NsdManager { } /** + * Initiate service discovery to browse for instances of a service type. Service discovery + * consumes network bandwidth and will continue until the application calls + * {@link #stopServiceDiscovery}. + * + * <p> The function call immediately returns after sending a request to start service + * discovery to the framework. The application is notified of a success to initiate + * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure + * through {@link DiscoveryListener#onStartDiscoveryFailed}. + * + * <p> Upon successful start, application is notified when a service is found with + * {@link DiscoveryListener#onServiceFound} or when a service is lost with + * {@link DiscoveryListener#onServiceLost}. + * + * <p> Upon failure to start, service discovery is not active and application does + * not need to invoke {@link #stopServiceDiscovery} + * + * <p> The application should call {@link #stopServiceDiscovery} when discovery of this + * service type is no longer required, and/or whenever the application is paused or + * stopped. + * + * <p> During discovery, new networks may connect or existing networks may disconnect - for + * example if wifi is reconnected. When a service was found on a network that disconnects, + * {@link DiscoveryListener#onServiceLost} will be called. If a new network connects that + * matches the {@link NetworkRequest}, {@link DiscoveryListener#onServiceFound} will be called + * for services found on that network. Applications that do not want to track networks + * themselves are encouraged to use this method instead of other overloads of + * {@code discoverServices}, as they will receive proper notifications when a service becomes + * available or unavailable due to network changes. + * + * @param serviceType The service type being discovered. Examples include "_http._tcp" for + * http services or "_ipp._tcp" for printers + * @param protocolType The service discovery protocol + * @param networkRequest Request specifying networks that should be considered when discovering + * @param listener The listener notifies of a successful discovery and is used + * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}. + * Cannot be null. Cannot be in use for an active service discovery. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + public void discoverServices(@NonNull String serviceType, int protocolType, + @NonNull NetworkRequest networkRequest, @NonNull DiscoveryListener listener) { + if (TextUtils.isEmpty(serviceType)) { + throw new IllegalArgumentException("Service type cannot be empty"); + } + Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null"); + checkProtocol(protocolType); + + NsdServiceInfo s = new NsdServiceInfo(); + s.setServiceType(serviceType); + + final int baseListenerKey = putListener(listener, s); + + final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker( + serviceType, protocolType, listener); + + synchronized (mPerNetworkDiscoveryMap) { + mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo); + discoveryInfo.start(networkRequest); + } + } + + /** * Stop service discovery initiated with {@link #discoverServices}. An active service * discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted} * and it stays active until the application invokes a stop service discovery. A successful @@ -696,6 +949,14 @@ public final class NsdManager { */ public void stopServiceDiscovery(DiscoveryListener listener) { int id = getListenerKey(listener); + // If this is a PerNetworkDiscovery request, handle it as such + synchronized (mPerNetworkDiscoveryMap) { + final PerNetworkDiscoveryTracker info = mPerNetworkDiscoveryMap.get(id); + if (info != null) { + info.requestStop(); + return; + } + } try { mService.stopDiscovery(id); } catch (RemoteException e) { diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java index 0946499f164f..8506db1fbe01 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java +++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java @@ -17,7 +17,9 @@ package android.net.nsd; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.net.Network; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -49,6 +51,9 @@ public final class NsdServiceInfo implements Parcelable { private int mPort; + @Nullable + private Network mNetwork; + public NsdServiceInfo() { } @@ -307,18 +312,37 @@ public final class NsdServiceInfo implements Parcelable { return txtRecord; } - public String toString() { - StringBuffer sb = new StringBuffer(); + /** + * Get the network where the service can be found. + * + * This is never null if this {@link NsdServiceInfo} was obtained from + * {@link NsdManager#discoverServices} or {@link NsdManager#resolveService}. + */ + @Nullable + public Network getNetwork() { + return mNetwork; + } + /** + * Set the network where the service can be found. + * @param network The network, or null to search for, or to announce, the service on all + * connected networks. + */ + public void setNetwork(@Nullable Network network) { + mNetwork = network; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); sb.append("name: ").append(mServiceName) .append(", type: ").append(mServiceType) .append(", host: ").append(mHost) - .append(", port: ").append(mPort); + .append(", port: ").append(mPort) + .append(", network: ").append(mNetwork); byte[] txtRecord = getTxtRecord(); - if (txtRecord != null) { - sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8)); - } + sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8)); return sb.toString(); } @@ -352,6 +376,8 @@ public final class NsdServiceInfo implements Parcelable { } dest.writeString(key); } + + dest.writeParcelable(mNetwork, 0); } /** Implement the Parcelable interface */ @@ -381,6 +407,7 @@ public final class NsdServiceInfo implements Parcelable { } info.mTxtRecord.put(in.readString(), valueArray); } + info.mNetwork = in.readParcelable(null, Network.class); return info; } 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/ConnectivityT/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java index 497107dbf989..ddf6d2c4ab15 100644 --- a/packages/ConnectivityT/service/src/com/android/server/NsdService.java +++ b/packages/ConnectivityT/service/src/com/android/server/NsdService.java @@ -19,6 +19,9 @@ package com.android.server; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; import android.net.nsd.INsdManager; import android.net.nsd.INsdManagerCallback; import android.net.nsd.INsdServiceConnector; @@ -44,6 +47,9 @@ import com.android.net.module.util.DnsSdTxtRecord; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CountDownLatch; @@ -60,6 +66,7 @@ public class NsdService extends INsdManager.Stub { private static final boolean DBG = true; private static final long CLEANUP_DELAY_MS = 10000; + private static final int IFACE_IDX_ANY = 0; private final Context mContext; private final NsdStateMachine mNsdStateMachine; @@ -297,7 +304,7 @@ public class NsdService extends INsdManager.Stub { maybeStartDaemon(); id = getUniqueId(); - if (discoverServices(id, args.serviceInfo.getServiceType())) { + if (discoverServices(id, args.serviceInfo)) { if (DBG) { Log.d(TAG, "Discover " + msg.arg2 + " " + id + args.serviceInfo.getServiceType()); @@ -430,13 +437,38 @@ public class NsdService extends INsdManager.Stub { } switch (code) { case NativeResponseCode.SERVICE_FOUND: - /* NNN uniqueId serviceName regType domain */ + /* NNN uniqueId serviceName regType domain interfaceIdx netId */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); + final int foundNetId; + try { + foundNetId = Integer.parseInt(cooked[6]); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]); + break; + } + if (foundNetId == 0L) { + // Ignore services that do not have a Network: they are not usable + // by apps, as they would need privileged permissions to use + // interfaces that do not have an associated Network. + break; + } + servInfo.setNetwork(new Network(foundNetId)); clientInfo.onServiceFound(clientId, servInfo); break; case NativeResponseCode.SERVICE_LOST: - /* NNN uniqueId serviceName regType domain */ + /* NNN uniqueId serviceName regType domain interfaceIdx netId */ + final int lostNetId; + try { + lostNetId = Integer.parseInt(cooked[6]); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]); + break; + } servInfo = new NsdServiceInfo(cooked[2], cooked[3]); + // The network could be null if it was torn down when the service is lost + // TODO: avoid returning null in that case, possibly by remembering found + // services on the same interface index and their network at the time + servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId)); clientInfo.onServiceLost(clientId, servInfo); break; case NativeResponseCode.SERVICE_DISCOVERY_FAILED: @@ -461,7 +493,7 @@ public class NsdService extends INsdManager.Stub { /* NNN regId errorCode */ break; case NativeResponseCode.SERVICE_RESOLVED: - /* NNN resolveId fullName hostName port txtlen txtdata */ + /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */ int index = 0; while (index < cooked[2].length() && cooked[2].charAt(index) != '.') { if (cooked[2].charAt(index) == '\\') { @@ -473,6 +505,7 @@ public class NsdService extends INsdManager.Stub { Log.e(TAG, "Invalid service found " + raw); break; } + String name = cooked[2].substring(0, index); String rest = cooked[2].substring(index); String type = rest.replace(".local.", ""); @@ -483,12 +516,13 @@ public class NsdService extends INsdManager.Stub { clientInfo.mResolvedService.setServiceType(type); clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); clientInfo.mResolvedService.setTxtRecords(cooked[6]); + // Network will be added after SERVICE_GET_ADDR_SUCCESS stopResolveService(id); removeRequestMap(clientId, id, clientInfo); int id2 = getUniqueId(); - if (getAddrInfo(id2, cooked[3])) { + if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) { storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); } else { clientInfo.onResolveServiceFailed( @@ -513,12 +547,31 @@ public class NsdService extends INsdManager.Stub { clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: - /* NNN resolveId hostname ttl addr */ + /* NNN resolveId hostname ttl addr interfaceIdx netId */ + Network network = null; + try { + final int netId = Integer.parseInt(cooked[6]); + network = netId == 0L ? null : new Network(netId); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e); + } + + InetAddress serviceHost = null; try { - clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); + serviceHost = InetAddress.getByName(cooked[4]); + } catch (UnknownHostException e) { + Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); + } + + // If the resolved service is on an interface without a network, consider it + // as a failure: it would not be usable by apps as they would need + // privileged permissions. + if (network != null && serviceHost != null) { + clientInfo.mResolvedService.setHost(serviceHost); + clientInfo.mResolvedService.setNetwork(network); clientInfo.onResolveServiceSucceeded( clientId, clientInfo.mResolvedService); - } catch (java.net.UnknownHostException e) { + } else { clientInfo.onResolveServiceFailed( clientId, NsdManager.FAILURE_INTERNAL_ERROR); } @@ -815,8 +868,15 @@ public class NsdService extends INsdManager.Stub { return mDaemon.execute("update", regId, t.size(), t.getRawData()); } - private boolean discoverServices(int discoveryId, String serviceType) { - return mDaemon.execute("discover", discoveryId, serviceType); + private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) { + final Network network = serviceInfo.getNetwork(); + final int discoverInterface = getNetworkInterfaceIndex(network); + if (network != null && discoverInterface == IFACE_IDX_ANY) { + Log.e(TAG, "Interface to discover service on not found"); + return false; + } + return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(), + discoverInterface); } private boolean stopServiceDiscovery(int discoveryId) { @@ -824,17 +884,61 @@ public class NsdService extends INsdManager.Stub { } private boolean resolveService(int resolveId, NsdServiceInfo service) { - String name = service.getServiceName(); - String type = service.getServiceType(); - return mDaemon.execute("resolve", resolveId, name, type, "local."); + final String name = service.getServiceName(); + final String type = service.getServiceType(); + final Network network = service.getNetwork(); + final int resolveInterface = getNetworkInterfaceIndex(network); + if (network != null && resolveInterface == IFACE_IDX_ANY) { + Log.e(TAG, "Interface to resolve service on not found"); + return false; + } + return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface); + } + + /** + * Guess the interface to use to resolve or discover a service on a specific network. + * + * This is an imperfect guess, as for example the network may be gone or not yet fully + * registered. This is fine as failing is correct if the network is gone, and a client + * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also + * this is to support the legacy mdnsresponder implementation, which historically resolved + * services on an unspecified network. + */ + private int getNetworkInterfaceIndex(Network network) { + if (network == null) return IFACE_IDX_ANY; + + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + if (cm == null) { + Log.wtf(TAG, "No ConnectivityManager for resolveService"); + return IFACE_IDX_ANY; + } + final LinkProperties lp = cm.getLinkProperties(network); + if (lp == null) return IFACE_IDX_ANY; + + // Only resolve on non-stacked interfaces + final NetworkInterface iface; + try { + iface = NetworkInterface.getByName(lp.getInterfaceName()); + } catch (SocketException e) { + Log.e(TAG, "Error querying interface", e); + return IFACE_IDX_ANY; + } + + if (iface == null) { + Log.e(TAG, "Interface not found: " + lp.getInterfaceName()); + return IFACE_IDX_ANY; + } + + return iface.getIndex(); } private boolean stopResolveService(int resolveId) { return mDaemon.execute("stop-resolve", resolveId); } - private boolean getAddrInfo(int resolveId, String hostname) { - return mDaemon.execute("getaddrinfo", resolveId, hostname); + private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) { + // interfaceIdx is always obtained (as string) from the service resolved callback + return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx); } private boolean stopGetAddrInfo(int resolveId) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 99e3160fcbe3..df19c67f00e5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -173,8 +173,10 @@ public class A2dpProfile implements LocalBluetoothProfile { } public BluetoothDevice getActiveDevice() { - if (mService == null) return null; - return mService.getActiveDevice(); + if (mBluetoothAdapter == null) return null; + final List<BluetoothDevice> activeDevices = mBluetoothAdapter + .getActiveDevices(BluetoothProfile.A2DP); + return (activeDevices.size() > 0) ? activeDevices.get(0) : null; } @Override @@ -221,7 +223,7 @@ public class A2dpProfile implements LocalBluetoothProfile { } public boolean supportsHighQualityAudio(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return false; } @@ -234,7 +236,7 @@ public class A2dpProfile implements LocalBluetoothProfile { */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public boolean isHighQualityAudioEnabled(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return false; } @@ -260,7 +262,7 @@ public class A2dpProfile implements LocalBluetoothProfile { } public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return; } @@ -286,7 +288,7 @@ public class A2dpProfile implements LocalBluetoothProfile { */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public String getHighQualityAudioOptionLabel(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; if (bluetoothDevice == null || !supportsHighQualityAudio(device) || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index b11bbdec191f..7e5c1240cc63 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -132,10 +132,12 @@ public class HeadsetProfile implements LocalBluetoothProfile { } public BluetoothDevice getActiveDevice() { - if (mService == null) { + if (mBluetoothAdapter == null) { return null; } - return mService.getActiveDevice(); + final List<BluetoothDevice> activeDevices = mBluetoothAdapter + .getActiveDevices(BluetoothProfile.HEADSET); + return (activeDevices.size() > 0) ? activeDevices.get(0) : null; } public int getAudioState(BluetoothDevice device) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index dc109cac37b2..6f2d4decf033 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -173,8 +173,10 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getActiveDevices() { - if (mService == null) return new ArrayList<>(); - return mService.getActiveDevices(); + if (mBluetoothAdapter == null) { + return new ArrayList<>(); + } + return mBluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java index 209507ac7088..e203cbaa3da5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java @@ -21,12 +21,12 @@ import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -144,20 +144,20 @@ public class LeAudioProfile implements LocalBluetoothProfile { * @hide */ public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } /* * @hide */ public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.disconnect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -177,10 +177,10 @@ public class LeAudioProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getActiveDevices() { - if (mService == null) { + if (mBluetoothAdapter == null) { return new ArrayList<>(); } - return mService.getActiveDevices(); + return mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index a000c099347d..d179b828d4ab 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -528,7 +528,18 @@ public class DreamBackend { if (flattenedString.indexOf('/') < 0) { flattenedString = serviceInfo.packageName + "/" + flattenedString; } - return ComponentName.unflattenFromString(flattenedString); + + ComponentName cn = ComponentName.unflattenFromString(flattenedString); + + if (cn == null) return null; + if (!cn.getPackageName().equals(serviceInfo.packageName)) { + Log.w(TAG, + "Inconsistent package name in component: " + cn.getPackageName() + + ", should be: " + serviceInfo.packageName); + return null; + } + + return cn; } private static DreamMetadata getDreamMetadata(PackageManager pm, ResolveInfo resolveInfo) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index f167721f94bf..d7b366e60a1d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -60,6 +60,8 @@ public class A2dpProfileTest { private BluetoothDevice mDevice; @Mock private BluetoothA2dp mBluetoothA2dp; + @Mock + private BluetoothAdapter mBluetoothAdapter; private BluetoothProfile.ServiceListener mServiceListener; private A2dpProfile mProfile; @@ -72,7 +74,8 @@ public class A2dpProfileTest { mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager); mServiceListener = mShadowBluetoothAdapter.getServiceListener(); mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp); - when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP))) + .thenReturn(Arrays.asList(mDevice)); } @Test 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/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 08d217d15a5a..4540b77d0a40 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -301,6 +301,7 @@ class ActivityLaunchAnimator( * The intent was started. If [willAnimate] is false, nothing else will happen and the * animation will not be started. */ + @JvmDefault fun onIntentStarted(willAnimate: Boolean) {} /** @@ -308,6 +309,7 @@ class ActivityLaunchAnimator( * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called * before the cancellation. */ + @JvmDefault fun onLaunchAnimationCancelled() {} } 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/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index ebe96ebf2988..77386cf2ff10 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -100,9 +100,11 @@ class LaunchAnimator( * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding * fully above the [launchContainer]. */ + @JvmDefault fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {} /** The animation made progress and the expandable view [state] should be updated. */ + @JvmDefault fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {} /** @@ -110,6 +112,7 @@ class LaunchAnimator( * called previously. This is typically used to clean up the resources initialized when the * animation was started. */ + @JvmDefault fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {} } diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 29221aa04699..208825ccc8cf 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -103,6 +103,20 @@ enum class Style(internal val coreSpec: CoreSpec) { n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0)) )), + RAINBOW(CoreSpec( + a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), + a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0)) + )), + FRUIT_SALAD(CoreSpec( + a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.EQ, 36.0)), + a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 36.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 10.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)) + )), } class ColorScheme( 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/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index cc10b02a8bb6..d7a8a7aa2c10 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -680,6 +680,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Whether the secure camera is currently showing over the keyguard. + */ + public boolean isSecureCameraLaunchedOverKeyguard() { + return mSecureCameraLaunched; + } + + /** * @return a cached version of DreamManager.isDreaming() */ public boolean isDreaming() { 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/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 2160744c6803..77997e437e33 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -31,8 +31,10 @@ import androidx.lifecycle.ViewModelStore; import com.android.internal.policy.PhoneWindow; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.Complication; +import com.android.systemui.dreams.complication.ComplicationUtils; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; @@ -57,6 +59,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final DreamBackend mDreamBackend; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -109,6 +112,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ setCurrentState(Lifecycle.State.CREATED); mLifecycleRegistry = component.getLifecycleRegistry(); mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); + mDreamBackend = component.getDreamBackend(); mDreamOverlayTouchMonitor.init(); } @@ -130,6 +134,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { setCurrentState(Lifecycle.State.STARTED); mExecutor.execute(() -> { + mStateController.setAvailableComplicationTypes( + ComplicationUtils.convertComplicationTypes( + mDreamBackend.getEnabledComplications())); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index ac7457d90e49..bc5a52a40a35 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -131,9 +131,7 @@ public class DreamOverlayStateController implements .filter(complication -> { @Complication.ComplicationType final int requiredTypes = complication.getRequiredTypeAvailability(); - - return requiredTypes == Complication.COMPLICATION_TYPE_NONE - || (requiredTypes & getAvailableComplicationTypes()) == requiredTypes; + return (requiredTypes & getAvailableComplicationTypes()) == requiredTypes; }) .collect(Collectors.toCollection(HashSet::new)) : mComplications); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java index 3a2a6ef60f03..a4a0075e742a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java @@ -25,6 +25,8 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION import com.android.settingslib.dream.DreamBackend; +import java.util.Set; + /** * A collection of utility methods for working with {@link Complication}. */ @@ -50,4 +52,14 @@ public class ComplicationUtils { return COMPLICATION_TYPE_NONE; } } + + /** + * Converts a set of {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to + * a combined complications types state. + */ + @Complication.ComplicationType + public static int convertComplicationTypes(@DreamBackend.ComplicationType Set<Integer> types) { + return types.stream().mapToInt(ComplicationUtils::convertComplicationType).reduce( + COMPLICATION_TYPE_NONE, (a, b) -> a | b); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java index 59c52b9e402b..6861c7479161 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java @@ -44,6 +44,11 @@ public class DreamClockDateComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_DATE; + } + /** * Create {@link DreamClockDateViewHolder}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java index b0c3a424cc92..936767a60233 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java @@ -44,6 +44,11 @@ public class DreamClockTimeComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_TIME; + } + /** * Create {@link DreamClockTimeViewHolder}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java index cbdbef3ae57e..f5c5a434a077 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java @@ -53,6 +53,11 @@ public class DreamWeatherComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_WEATHER; + } + /** * Create {@link DreamWeatherViewHolder}. */ 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/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java index 05ab9015fecc..60278a9da7eb 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java @@ -22,6 +22,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.ViewModelStore; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dreams.DreamOverlayContainerViewController; import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.dagger.ComplicationModule; @@ -68,4 +69,7 @@ public interface DreamOverlayComponent { /** Builds a {@link DreamOverlayTouchMonitor} */ DreamOverlayTouchMonitor getDreamOverlayTouchMonitor(); + + /** Builds a ${@link DreamBackend} */ + DreamBackend getDreamBackend(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 4eb5cb97607a..efa063f4dfc8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams.dagger; import android.content.ContentResolver; +import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.view.LayoutInflater; @@ -27,6 +28,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import com.android.internal.util.Preconditions; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.battery.BatteryMeterViewController; @@ -147,4 +149,10 @@ public abstract class DreamOverlayModule { static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) { return lifecycleOwner.getLifecycle(); } + + @Provides + @DreamOverlayComponent.DreamOverlayScope + static DreamBackend providesDreamBackend(Context context) { + return DreamBackend.getInstance(context); + } } 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/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java index d1a103e3a8fa..de67ba8a6ded 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java @@ -40,6 +40,7 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { private boolean mIsDropDownMode; private int mMenuVerticalPadding = 0; private int mGlobalActionsSidePadding = 0; + private int mMaximumWidthThresholdDp = 800; private ListAdapter mAdapter; private AdapterView.OnItemLongClickListener mOnItemLongClickListener; @@ -92,6 +93,8 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { // width should be between [.5, .9] of screen int parentWidth = res.getSystem().getDisplayMetrics().widthPixels; + float parentDensity = res.getSystem().getDisplayMetrics().density; + float parentWidthDp = parentWidth / parentDensity; int widthSpec = MeasureSpec.makeMeasureSpec( (int) (parentWidth * 0.9), MeasureSpec.AT_MOST); int maxWidth = 0; @@ -101,9 +104,12 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { int w = child.getMeasuredWidth(); maxWidth = Math.max(w, maxWidth); } - int width = Math.max(maxWidth, (int) (parentWidth * 0.5)); - listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding); + int width = maxWidth; + if (parentWidthDp < mMaximumWidthThresholdDp) { + width = Math.max(maxWidth, (int) (parentWidth * 0.5)); + } + listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding); setWidth(width); if (getAnchorView().getLayoutDirection() == LayoutDirection.LTR) { setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index e88011e0d3fe..88555edd1e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -233,10 +233,13 @@ public class KeyguardService extends Service { mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; mShellTransitions = shellTransitions; + } - if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) { - // Nothing here. Initialization for this happens in onCreate. - } else { + @Override + public void onCreate() { + ((SystemUIApplication) getApplication()).startServicesIfNeeded(); + + if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); if (sEnableRemoteKeyguardGoingAwayAnimation) { final RemoteAnimationAdapter exitAnimationAdapter = @@ -248,22 +251,19 @@ public class KeyguardService extends Service { } if (sEnableRemoteKeyguardOccludeAnimation) { final RemoteAnimationAdapter occludeAnimationAdapter = - new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0); + new RemoteAnimationAdapter( + mKeyguardViewMediator.getOccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter); + + final RemoteAnimationAdapter unoccludeAnimationAdapter = + new RemoteAnimationAdapter( + mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, - occludeAnimationAdapter); + unoccludeAnimationAdapter); } ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay( DEFAULT_DISPLAY, definition); - } - } - - @Override - public void onCreate() { - ((SystemUIApplication) getApplication()).startServicesIfNeeded(); - - if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { return; } if (sEnableRemoteKeyguardGoingAwayAnimation) { @@ -354,33 +354,6 @@ public class KeyguardService extends Service { } }; - private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner = - new IRemoteAnimationRunner.Stub() { - @Override // Binder interface - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback) { - Slog.d(TAG, "mOccludeAnimationRunner.onAnimationStart: transit=" + transit); - try { - if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { - mBinder.setOccluded(true /* isOccluded */, true /* animate */); - } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) { - mBinder.setOccluded(false /* isOccluded */, false /* animate */); - } - // TODO(bc-unlock): Implement (un)occlude animation. - finishedCallback.onAnimationFinished(); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException"); - } - } - - @Override // Binder interface - public void onAnimationCancelled() { - } - }; - final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() { @Override public void startAnimation(IBinder transition, TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index fd2c6dd3ca36..0f08a1863e08 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -77,11 +77,13 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.jank.InteractionJankMonitor; @@ -89,6 +91,7 @@ import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardStateCallback; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardConstants; @@ -102,7 +105,10 @@ import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; +import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; +import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -375,6 +381,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private int mUnlockSoundId; private int mTrustedSoundId; private int mLockSoundStreamId; + private final float mPowerButtonY; + private final float mWindowCornerRadius; + /** * The animation used for hiding keyguard. This is used to fetch the animation timings if * WindowManager is not providing us with them. @@ -815,6 +824,109 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } }; + /** + * Animation launch controller for activities that occlude the keyguard. + */ + private final ActivityLaunchAnimator.Controller mOccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationStart(boolean isExpandingFullyAbove) { + setOccluded(true /* occluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(true /* occluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + final float initialHeight = height / 3f; + final float initialWidth = width / 3f; + + if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) { + // Start the animation near the power button, at one-third size, since the + // camera was launched from the power button. + return new LaunchAnimator.State( + (int) (mPowerButtonY - initialHeight / 2f) /* top */, + (int) (mPowerButtonY + initialHeight / 2f) /* bottom */, + (int) (width - initialWidth) /* left */, + width /* right */, + mWindowCornerRadius, mWindowCornerRadius); + } else { + // Start the animation in the center of the screen, scaled down. + return new LaunchAnimator.State( + height / 2, height / 2, width / 2, width / 2, + mWindowCornerRadius, mWindowCornerRadius); + } + } + }; + + /** + * Animation controller for activities that unocclude the keyguard. This will play the launch + * animation in reverse. + */ + private final ActivityLaunchAnimator.Controller mUnoccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + // TODO(b/207399883): Unocclude animation. This currently ends instantly. + return new LaunchAnimator.State( + 0, height, 0, width, mWindowCornerRadius, mWindowCornerRadius); + } + }; + + private IRemoteAnimationRunner mOccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); + private IRemoteAnimationRunner mUnoccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mUnoccludeAnimationController); + private DeviceConfigProxy mDeviceConfig; private DozeParameters mDozeParameters; @@ -824,6 +936,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private boolean mWallpaperSupportsAmbientMode; private ScreenOnCoordinator mScreenOnCoordinator; + private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator; + /** * Injected constructor. See {@link KeyguardModule}. */ @@ -850,7 +964,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) { + Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, + Lazy<ActivityLaunchAnimator> activityLaunchAnimator) { super(context); mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; @@ -890,6 +1005,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mScreenOffAnimationController = screenOffAnimationController; mInteractionJankMonitor = interactionJankMonitor; mDreamOverlayStateController = dreamOverlayStateController; + + mActivityLaunchAnimator = activityLaunchAnimator; + + mPowerButtonY = context.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y); + mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); } public void userActivity() { @@ -1440,6 +1561,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.endSection(); } + public IRemoteAnimationRunner getOccludeAnimationRunner() { + return mOccludeAnimationRunner; + } + + public IRemoteAnimationRunner getUnoccludeAnimationRunner() { + return mUnoccludeAnimationRunner; + } + public boolean isHiding() { return mHiding; } @@ -2868,4 +2997,36 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, return mMessage; } } + + /** + * Implementation of RemoteAnimationRunner that creates a new + * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the + * remote animation methods to that runner. + */ + private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub { + + private final ActivityLaunchAnimator.Controller mActivityLaunchController; + @Nullable private ActivityLaunchAnimator.Runner mRunner; + + ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) { + mActivityLaunchController = controller; + } + + @Override + public void onAnimationCancelled() throws RemoteException { + if (mRunner != null) { + mRunner.onAnimationCancelled(); + } + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) + throws RemoteException { + mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController); + mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index b49b49cbbb6d..195ef1a84c43 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -32,6 +32,7 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.mediator.ScreenOnCoordinator; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; @@ -102,7 +103,8 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy<NotificationShadeWindowController> notificationShadeWindowController) { + Lazy<NotificationShadeWindowController> notificationShadeWindowController, + Lazy<ActivityLaunchAnimator> activityLaunchAnimator) { return new KeyguardViewMediator( context, falsingCollector, @@ -128,8 +130,8 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, - notificationShadeWindowController - ); + notificationShadeWindowController, + activityLaunchAnimator); } @SysUISingleton 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/dream/MediaDreamComplication.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java index 2c35db337cda..7c0481049098 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java +++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java @@ -37,6 +37,11 @@ public class MediaDreamComplication implements Complication { } @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_CAST_INFO; + } + + @Override public ViewHolder createView(ComplicationViewModel model) { return mComponentFactory.create().getViewHolder(); } 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/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 391525e11866..092e86daddb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -179,6 +179,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) { return; } + Trace.beginSection("NotificationViewHierarchyManager.updateNotificationViews"); beginUpdate(); @@ -353,6 +354,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle mListContainer.onNotificationViewUpdateFinished(); endUpdate(); + Trace.endSection(); } /** @@ -362,6 +364,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle * {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator} */ private void updateNotifStats() { + Trace.beginSection("NotificationViewHierarchyManager.updateNotifStats"); boolean hasNonClearableAlertingNotifs = false; boolean hasClearableAlertingNotifs = false; boolean hasNonClearableSilentNotifs = false; @@ -403,6 +406,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */, hasClearableSilentNotifs /* hasClearableSilentNotifs */ )); + Trace.endSection(); } /** @@ -520,7 +524,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } private void updateRowStatesInternal() { - Trace.beginSection("NotificationViewHierarchyManager#updateRowStates"); + Trace.beginSection("NotificationViewHierarchyManager.updateRowStates"); final int N = mListContainer.getContainerChildCount(); int visibleNotifications = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 5302188ccb31..4a7606c316e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -22,6 +22,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.app.ActivityManager; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -33,7 +34,6 @@ import android.graphics.Color; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; @@ -57,6 +57,7 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.NotificationIconDozeHelper; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.util.drawable.DrawableSize; import java.text.NumberFormat; import java.util.Arrays; @@ -84,16 +85,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public static final int STATE_DOT = 1; public static final int STATE_HIDDEN = 2; - /** - * Maximum allowed byte count for an icon bitmap - * @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE - */ - private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB - /** - * Maximum allowed width or height for an icon drawable, if we can't get byte count - */ - private static final int MAX_IMAGE_SIZE = 5000; - private static final String TAG = "StatusBarIconView"; private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT = new FloatProperty<StatusBarIconView>("iconAppearAmount") { @@ -390,21 +381,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi return false; } - if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) { - // If it's a bitmap we can check the size directly - int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount(); - if (byteCount > MAX_BITMAP_SIZE) { - Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon); - return false; - } - } else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE - || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) { - // Otherwise, check dimensions - Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x" - + drawable.getIntrinsicHeight() + ") " + mIcon); - return false; - } - if (withClear) { setImageDrawable(null); } @@ -432,7 +408,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi * @return Drawable for this item, or null if the package or item could not * be found */ - public static Drawable getIcon(Context sysuiContext, + private Drawable getIcon(Context sysuiContext, Context context, StatusBarIcon statusBarIcon) { int userId = statusBarIcon.user.getIdentifier(); if (userId == UserHandle.USER_ALL) { @@ -446,6 +422,16 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi typedValue, true); float scaleFactor = typedValue.getFloat(); + // We downscale the loaded drawable to reasonable size to protect against applications + // using too much memory. The size can be tweaked in config.xml. Drawables + // that are already sized properly won't be touched. + boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); + Resources res = sysuiContext.getResources(); + int maxIconSize = res.getDimensionPixelSize(isLowRamDevice + ? com.android.internal.R.dimen.notification_small_icon_size_low_ram + : com.android.internal.R.dimen.notification_small_icon_size); + icon = DrawableSize.downscaleToSize(res, icon, maxIconSize, maxIconSize); + // No need to scale the icon, so return it as is. if (scaleFactor == 1.f) { return icon; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index c33160858d0f..ad9f12ec3bc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -24,6 +24,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; @@ -343,11 +344,14 @@ public class NotificationEntryManager implements private final InflationCallback mInflationCallback = new InflationCallback() { @Override public void handleInflationException(NotificationEntry entry, Exception e) { + Trace.beginSection("NotificationEntryManager.handleInflationException"); NotificationEntryManager.this.handleInflationException(entry.getSbn(), e); + Trace.endSection(); } @Override public void onAsyncInflationFinished(NotificationEntry entry) { + Trace.beginSection("NotificationEntryManager.onAsyncInflationFinished"); mPendingNotifications.remove(entry.getKey()); // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. @@ -369,6 +373,7 @@ public class NotificationEntryManager implements } } } + Trace.endSection(); } }; @@ -463,6 +468,7 @@ public class NotificationEntryManager implements boolean forceRemove, DismissedByUserStats dismissedByUserStats, int reason) { + Trace.beginSection("NotificationEntryManager.removeNotificationInternal"); final NotificationEntry entry = getActiveNotificationUnfiltered(key); @@ -470,6 +476,7 @@ public class NotificationEntryManager implements if (interceptor.onNotificationRemoveRequested(key, entry, reason)) { // Remove intercepted; log and skip mLogger.logRemovalIntercepted(key); + Trace.endSection(); return; } } @@ -557,6 +564,7 @@ public class NotificationEntryManager implements mLeakDetector.trackGarbage(entry); } } + Trace.endSection(); } private void sendNotificationRemovalToServer( @@ -620,6 +628,7 @@ public class NotificationEntryManager implements private void addNotificationInternal( StatusBarNotification notification, RankingMap rankingMap) throws InflationException { + Trace.beginSection("NotificationEntryManager.addNotificationInternal"); String key = notification.getKey(); if (DEBUG) { Log.d(TAG, "addNotification key=" + key); @@ -667,6 +676,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } public void addNotification(StatusBarNotification notification, RankingMap ranking) { @@ -679,12 +689,14 @@ public class NotificationEntryManager implements private void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) throws InflationException { + Trace.beginSection("NotificationEntryManager.updateNotificationInternal"); if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); abortExistingInflation(key, "updateNotification"); final NotificationEntry entry = getActiveNotificationUnfiltered(key); if (entry == null) { + Trace.endSection(); return; } @@ -721,6 +733,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } public void updateNotification(StatusBarNotification notification, RankingMap ranking) { @@ -740,14 +753,17 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("updateNotifications", reason); return; } + Trace.beginSection("NotificationEntryManager.updateNotifications"); reapplyFilterAndSort(reason); if (mPresenter != null) { mPresenter.updateNotificationViews(reason); } mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications()); + Trace.endSection(); } public void updateNotificationRanking(RankingMap rankingMap) { + Trace.beginSection("NotificationEntryManager.updateNotificationRanking"); List<NotificationEntry> entries = new ArrayList<>(); entries.addAll(getVisibleNotifications()); entries.addAll(mPendingNotifications.values()); @@ -788,6 +804,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } void notifyChannelModified( @@ -887,6 +904,7 @@ public class NotificationEntryManager implements /** @return list of active notifications filtered for the current user */ public List<NotificationEntry> getActiveNotificationsForCurrentUser() { + Trace.beginSection("NotificationEntryManager.getActiveNotificationsForCurrentUser"); Assert.isMainThread(); ArrayList<NotificationEntry> filtered = new ArrayList<>(); @@ -898,7 +916,7 @@ public class NotificationEntryManager implements } filtered.add(entry); } - + Trace.endSection(); return filtered; } @@ -908,10 +926,12 @@ public class NotificationEntryManager implements * @param reason the reason for calling this method, which will be logged */ public void updateRanking(RankingMap rankingMap, String reason) { + Trace.beginSection("NotificationEntryManager.updateRanking"); updateRankingAndSort(rankingMap, reason); for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } /** Resorts / filters the current notification set with the current RankingMap */ @@ -920,7 +940,9 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason); return; } + Trace.beginSection("NotificationEntryManager.reapplyFilterAndSort"); updateRankingAndSort(mRanker.getRankingMap(), reason); + Trace.endSection(); } /** Calls to NotificationRankingManager and updates mSortedAndFiltered */ @@ -929,9 +951,11 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason); return; } + Trace.beginSection("NotificationEntryManager.updateRankingAndSort"); mSortedAndFiltered.clear(); mSortedAndFiltered.addAll(mRanker.updateRanking( rankingMap, mActiveNotifications.values(), reason)); + Trace.endSection(); } /** dump the current active notification list. Called from StatusBar */ 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 74c97fdbddca..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())); @@ -426,14 +431,18 @@ public class ShadeListBuilder implements Dumpable { } Trace.endSection(); + Trace.beginSection("ShadeListBuilder.logEndBuildList"); // Step 9: We're done! mLogger.logEndBuildList( mIterationCount, mReadOnlyNotifList.size(), countChildren(mReadOnlyNotifList)); if (mAlwaysLogList || mIterationCount % 10 == 0) { + Trace.beginSection("ShadeListBuilder.logFinalList"); mLogger.logFinalList(mNotifList); + Trace.endSection(); } + Trace.endSection(); mPipelineState.setState(STATE_IDLE); mIterationCount++; Trace.endSection(); @@ -996,16 +1005,20 @@ public class ShadeListBuilder implements Dumpable { } private void freeEmptyGroups() { + Trace.beginSection("ShadeListBuilder.freeEmptyGroups"); mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty()); + Trace.endSection(); } private void logChanges() { + Trace.beginSection("ShadeListBuilder.logChanges"); for (NotificationEntry entry : mAllEntries) { logAttachStateChanges(entry); } for (GroupEntry group : mGroups.values()) { logAttachStateChanges(group); } + Trace.endSection(); } private void logAttachStateChanges(ListEntry entry) { @@ -1083,16 +1096,23 @@ public class ShadeListBuilder implements Dumpable { } private void cleanupPluggables() { + Trace.beginSection("ShadeListBuilder.cleanupPluggables"); callOnCleanup(mNotifPreGroupFilters); callOnCleanup(mNotifPromoters); callOnCleanup(mNotifFinalizeFilters); 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())); + Trace.endSection(); } private void callOnCleanup(List<? extends Pluggable<?>> pluggables) { @@ -1101,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(), @@ -1112,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/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index c6a8a69cfb0d..1c96e8ceb27f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -23,6 +23,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -38,10 +39,11 @@ class StackCoordinator @Inject internal constructor( pipeline.addOnAfterRenderListListener(::onAfterRenderList) } - fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) { - controller.setNotifStats(calculateNotifStats(entries)) - notificationIconAreaController.updateNotificationIcons(entries) - } + fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) = + traceSection("StackCoordinator.onAfterRenderList") { + controller.setNotifStats(calculateNotifStats(entries)) + notificationIconAreaController.updateNotificationIcons(entries) + } private fun calculateNotifStats(entries: List<ListEntry>): NotifStats { var hasNonClearableAlertingNotifs = false 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/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt index 4de8e7a5641c..b76169f111db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.render import android.view.View +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationListContainer @@ -41,6 +42,7 @@ class RootNodeController( override fun addChildAt(child: NodeController, index: Int) { listContainer.addContainerViewAt(child.view, index) listContainer.onNotificationViewUpdateFinished() + (child.view as? ExpandableNotificationRow)?.isChangingPosition = false } override fun moveChildTo(child: NodeController, index: Int) { @@ -50,6 +52,7 @@ class RootNodeController( override fun removeChild(child: NodeController, isTransfer: Boolean) { if (isTransfer) { listContainer.setChildTransferInProgress(true) + (child.view as? ExpandableNotificationRow)?.isChangingPosition = true } listContainer.removeContainerView(child.view) if (isTransfer) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 46efef66de43..2436ffdbeefa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -248,19 +248,25 @@ public class ExpandableNotificationRowController implements NotifViewController mView.addChildNotification((ExpandableNotificationRow) child.getView(), index); mListContainer.notifyGroupChildAdded(childView); + childView.setChangingPosition(false); } @Override public void moveChildTo(NodeController child, int index) { ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView(); + childView.setChangingPosition(true); mView.removeChildNotification(childView); mView.addChildNotification(childView, index); + childView.setChangingPosition(false); } @Override public void removeChild(NodeController child, boolean isTransfer) { ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView(); + if (isTransfer) { + childView.setChangingPosition(true); + } mView.removeChildNotification(childView); if (!isTransfer) { mListContainer.notifyGroupChildRemoved(childView, mView); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index b02dc0cffdb9..54e26c34522d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.children import com.android.systemui.util.foldToSparseArray import com.android.systemui.util.takeUntil +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -157,7 +158,9 @@ class NotificationSectionsManager @Inject internal constructor( } } } - private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild) + private fun logShadeContents() = traceSection("NotifSectionsManager.logShadeContents") { + parent.children.forEachIndexed(::logShadeChild) + } private val isUsingMultipleSections: Boolean get() = sectionsFeatureManager.getNumberOfBuckets() > 1 @@ -221,10 +224,10 @@ class NotificationSectionsManager @Inject internal constructor( * Should be called whenever notifs are added, removed, or updated. Updates section boundary * bookkeeping and adds/moves/removes section headers if appropriate. */ - fun updateSectionBoundaries(reason: String) { + fun updateSectionBoundaries(reason: String) = traceSection("NotifSectionsManager.update") { notifPipelineFlags.checkLegacyPipelineEnabled() if (!isUsingMultipleSections) { - return + return@traceSection } logger.logStartSectionUpdate(reason) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index dd72615768b0..25b8a6545638 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -43,7 +43,6 @@ import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; -import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -696,8 +695,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable && mQsExpansionFraction != 1 && !mScreenOffAnimationController.shouldHideNotificationsFooter() && !mIsRemoteInputActive; - boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + boolean showHistory = mController.isHistoryEnabled(); updateFooterView(showFooterView, showDismissView, showHistory); } @@ -5243,11 +5241,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable R.layout.status_bar_no_notifications, this, false); view.setText(R.string.empty_shade_text); view.setOnClickListener(v -> { - final boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; - Intent intent = showHistory ? new Intent( - Settings.ACTION_NOTIFICATION_HISTORY) : new Intent( - Settings.ACTION_NOTIFICATION_SETTINGS); + final boolean showHistory = mController.isHistoryEnabled(); + Intent intent = showHistory + ? new Intent(Settings.ACTION_NOTIFICATION_HISTORY) + : new Intent(Settings.ACTION_NOTIFICATION_SETTINGS); mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP); }); setEmptyShadeView(view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 334128a2b4ca..a2929f01f715 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -35,6 +35,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; +import android.os.Trace; +import android.os.UserHandle; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -180,7 +182,6 @@ public class NotificationStackScrollLayoutController { private final NotificationLockscreenUserManager mLockscreenUserManager; // TODO: StatusBar should be encapsulated behind a Controller private final StatusBar mStatusBar; - private final NotificationGroupManagerLegacy mLegacyGroupManager; private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; @@ -191,6 +192,7 @@ public class NotificationStackScrollLayoutController { private boolean mFadeNotificationsOnDismiss; private NotificationSwipeHelper mSwipeHelper; private boolean mShowEmptyShadeView; + @Nullable private Boolean mHistoryEnabled; private int mBarState; private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -340,6 +342,8 @@ public class NotificationStackScrollLayoutController { @Override public void onUserChanged(int userId) { mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode()); + mHistoryEnabled = null; + updateFooter(); } }; @@ -699,8 +703,6 @@ public class NotificationStackScrollLayoutController { } }); mNotifPipelineFlags = notifPipelineFlags; - mLegacyGroupManager = mNotifPipelineFlags.isNewPipelineEnabled() - ? null : legacyGroupManager; mSilentHeaderController = silentHeaderController; mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; @@ -802,6 +804,7 @@ public class NotificationStackScrollLayoutController { (key, newValue) -> { switch (key) { case Settings.Secure.NOTIFICATION_HISTORY_ENABLED: + mHistoryEnabled = null; // invalidate updateFooter(); break; case HIGH_PRIORITY: @@ -1009,6 +1012,20 @@ public class NotificationStackScrollLayoutController { return mNotifStats.getNumActiveNotifs(); } + public boolean isHistoryEnabled() { + Boolean historyEnabled = mHistoryEnabled; + if (historyEnabled == null) { + if (mView == null || mView.getContext() == null) { + Log.wtf(TAG, "isHistoryEnabled failed to initialize its value"); + return false; + } + mHistoryEnabled = historyEnabled = + Settings.Secure.getIntForUser(mView.getContext().getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + } + return historyEnabled; + } + public int getIntrinsicContentHeight() { return mView.getIntrinsicContentHeight(); } @@ -1199,6 +1216,7 @@ public class NotificationStackScrollLayoutController { * are true. */ public void updateShowEmptyShadeView() { + Trace.beginSection("NSSLC.updateShowEmptyShadeView"); mShowEmptyShadeView = mBarState != KEYGUARD && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade()) && getVisibleNotificationCount() == 0; @@ -1206,6 +1224,7 @@ public class NotificationStackScrollLayoutController { mView.updateEmptyShadeView( mShowEmptyShadeView, mZenModeController.areNotificationsHiddenInShade()); + Trace.endSection(); } public boolean areNotificationsHiddenInShade() { @@ -1323,11 +1342,15 @@ public class NotificationStackScrollLayoutController { if (mNotifPipelineFlags.isNewPipelineEnabled()) { return; } + Trace.beginSection("NSSLC.updateSectionBoundaries"); mView.updateSectionBoundaries(reason); + Trace.endSection(); } public void updateFooter() { + Trace.beginSection("NSSLC.updateFooter"); mView.updateFooter(); + Trace.endSection(); } public void onUpdateRowStates() { 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/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt new file mode 100644 index 000000000000..b5068087d905 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -0,0 +1,123 @@ +package com.android.systemui.util.drawable + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.Animatable +import android.graphics.drawable.Animatable2 +import android.graphics.drawable.AnimatedImageDrawable +import android.graphics.drawable.AnimatedRotateDrawable +import android.graphics.drawable.AnimatedStateListDrawable +import android.graphics.drawable.AnimatedVectorDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import android.util.Log +import androidx.annotation.Px +import com.android.systemui.util.traceSection + +class DrawableSize { + + companion object { + + const val TAG = "SysUiDrawableSize" + + /** + * Downscales passed Drawable to set maximum width and height. This will only + * be done for Drawables that can be downscaled non-destructively - e.g. animated + * and stateful drawables will no be downscaled. + * + * Downscaling will keep the aspect ratio. + * This method will not touch drawables that already fit into size specification. + * + * @param resources Resources on which to base the density of resized drawable. + * @param drawable Drawable to downscale. + * @param maxWidth Maximum width of the downscaled drawable. + * @param maxHeight Maximum height of the downscaled drawable. + * + * @return returns downscaled drawable if it's possible to downscale it or original if it's + * not. + */ + @JvmStatic + fun downscaleToSize( + res: Resources, + drawable: Drawable, + @Px maxWidth: Int, + @Px maxHeight: Int + ): Drawable { + traceSection("DrawableSize#downscaleToSize") { + // Bitmap drawables can contain big bitmaps as their content while sneaking it past + // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap + // size for those. + val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width + ?: drawable.intrinsicWidth + val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height + ?: drawable.intrinsicHeight + + // Don't touch drawable if we can't resolve sizes for whatever reason. + if (originalWidth <= 0 || originalHeight <= 0) { + return drawable + } + + // Do not touch drawables that are already within bounds. + if (originalWidth < maxWidth && originalHeight < maxHeight) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " + + "to $maxWidth x $maxHeight") + } + + return drawable + } + + if (!isSimpleBitmap(drawable)) { + return drawable + } + + val scaleWidth = maxWidth.toFloat() / originalWidth.toFloat() + val scaleHeight = maxHeight.toFloat() / originalHeight.toFloat() + val scale = minOf(scaleHeight, scaleWidth) + + val width = (originalWidth * scale).toInt() + val height = (originalHeight * scale).toInt() + + if (width <= 0 || height <= 0) { + Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " + + "from $originalWidth x $originalHeight to invalid $width x $height.") + return drawable + } + + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " + + "from $originalWidth x $originalHeight to $width x $height") + } + + // We want to keep existing config if it's more efficient than 32-bit RGB. + val config = (drawable as? BitmapDrawable)?.bitmap?.config + ?: Bitmap.Config.ARGB_8888 + val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config) + val canvas = Canvas(scaledDrawableBitmap) + + val originalBounds = drawable.bounds + drawable.setBounds(0, 0, width, height) + drawable.draw(canvas) + drawable.bounds = originalBounds + + return BitmapDrawable(res, scaledDrawableBitmap) + } + } + + private fun isSimpleBitmap(drawable: Drawable): Boolean { + return !(drawable.isStateful || isAnimated(drawable)) + } + + private fun isAnimated(drawable: Drawable): Boolean { + if (drawable is Animatable || drawable is Animatable2) { + return true + } + + return drawable is AnimatedImageDrawable || + drawable is AnimatedRotateDrawable || + drawable is AnimatedStateListDrawable || + drawable is AnimatedVectorDrawable + } + } +}
\ 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/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index d5bd67adcf09..8adb55b8d6e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -36,7 +36,9 @@ import androidx.lifecycle.LifecycleRegistry; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; import com.android.systemui.util.concurrency.FakeExecutor; @@ -50,6 +52,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayServiceTest extends SysuiTestCase { @@ -94,11 +100,13 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock DreamOverlayStateController mStateController; + @Mock + DreamBackend mDreamBackend; DreamOverlayService mService; @Before - public void setup() throws Exception { + public void setup() { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(WindowManager.class, mWindowManager); @@ -110,6 +118,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { .thenReturn(mLifecycleRegistry); when(mDreamOverlayComponent.getDreamOverlayTouchMonitor()) .thenReturn(mDreamOverlayTouchMonitor); + when(mDreamOverlayComponent.getDreamBackend()) + .thenReturn(mDreamBackend); when(mDreamOverlayComponentFactory .create(any(), any())) .thenReturn(mDreamOverlayComponent); @@ -120,28 +130,34 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor); + } + + @Test + public void testOverlayContainerViewAddedToWindow() throws Exception { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); mMainExecutor.runAllReady(); - } - @Test - public void testOverlayContainerViewAddedToWindow() { verify(mWindowManager).addView(any(), any()); } @Test - public void testDreamOverlayContainerViewControllerInitialized() { + public void testDreamOverlayContainerViewControllerInitialized() throws Exception { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback); + mMainExecutor.runAllReady(); + verify(mDreamOverlayContainerViewController).init(); } @Test public void testShouldShowComplicationsTrueByDefault() { - assertThat(mService.shouldShowComplications()).isTrue(); - mService.onBind(new Intent()); assertThat(mService.shouldShowComplications()).isTrue(); @@ -155,4 +171,24 @@ public class DreamOverlayServiceTest extends SysuiTestCase { assertThat(mService.shouldShowComplications()).isFalse(); } + + @Test + public void testSetAvailableComplicationTypes() throws Exception { + final Set<Integer> enabledComplications = new HashSet<>( + Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME, + DreamBackend.COMPLICATION_TYPE_DATE, + DreamBackend.COMPLICATION_TYPE_WEATHER)); + when(mDreamBackend.getEnabledComplications()).thenReturn(enabledComplications); + + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + overlay.startDream(mWindowParams, mDreamOverlayCallback); + mMainExecutor.runAllReady(); + + final int expectedTypes = + Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE + | Complication.COMPLICATION_TYPE_WEATHER; + verify(mStateController).setAvailableComplicationTypes(expectedTypes); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java index f1978b214594..365c5291b118 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java @@ -22,6 +22,7 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME; import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER; import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType; +import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationTypes; import static com.google.common.truth.Truth.assertThat; @@ -36,6 +37,11 @@ import com.android.systemui.SysuiTestCase; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + @SmallTest @RunWith(AndroidTestingRunner.class) public class ComplicationUtilsTest extends SysuiTestCase { @@ -52,4 +58,37 @@ public class ComplicationUtilsTest extends SysuiTestCase { assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO)) .isEqualTo(COMPLICATION_TYPE_CAST_INFO); } + + @Test + public void testConvertComplicationTypesEmpty() { + final Set<Integer> input = new HashSet<>(); + final int expected = Complication.COMPLICATION_TYPE_NONE; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } + + @Test + public void testConvertComplicationTypesSingleValue() { + final Set<Integer> input = new HashSet<>( + Collections.singleton(DreamBackend.COMPLICATION_TYPE_WEATHER)); + final int expected = Complication.COMPLICATION_TYPE_WEATHER; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } + + @Test + public void testConvertComplicationTypesSingleValueMultipleValues() { + final Set<Integer> input = new HashSet<>( + Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME, + DreamBackend.COMPLICATION_TYPE_DATE, + DreamBackend.COMPLICATION_TYPE_WEATHER, + DreamBackend.COMPLICATION_TYPE_AIR_QUALITY, + DreamBackend.COMPLICATION_TYPE_CAST_INFO)); + final int expected = + Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE + | Complication.COMPLICATION_TYPE_WEATHER | COMPLICATION_TYPE_AIR_QUALITY + | COMPLICATION_TYPE_CAST_INFO; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d94e2eee9ffa..210cb82e1606 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -46,6 +46,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dreams.DreamOverlayStateController; @@ -100,6 +101,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ScreenOnCoordinator mScreenOnCoordinator; private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy; private @Mock DreamOverlayStateController mDreamOverlayStateController; + private @Mock ActivityLaunchAnimator mActivityLaunchAnimator; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -205,7 +207,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, - mNotificationShadeWindowControllerLazy); + mNotificationShadeWindowControllerLazy, + () -> mActivityLaunchAnimator); mViewMediator.start(); } } 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/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java index 85ea52b6af6a..d13451dc4769 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; @@ -37,6 +39,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -130,7 +133,12 @@ public class StatusBarIconViewTest extends SysuiTestCase { Icon icon = Icon.createWithBitmap(largeBitmap); StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", icon, 0, 0, ""); - assertFalse(mIconView.set(largeIcon)); + assertTrue(mIconView.set(largeIcon)); + + // The view should downscale the bitmap. + BitmapDrawable drawable = (BitmapDrawable) mIconView.getDrawable(); + assertThat(drawable.getBitmap().getWidth()).isLessThan(1000); + assertThat(drawable.getBitmap().getHeight()).isLessThan(1000); } @Test 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/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index bdcbbbc99ea3..4f731ed5f72c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.stack; -import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static android.view.View.GONE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; @@ -41,8 +40,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.os.UserHandle; -import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.MathUtils; @@ -112,10 +109,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void setUp() throws Exception { allowTestableLooperAsMainThread(); - Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED, - 1, UserHandle.USER_CURRENT); - - // Interact with real instance of AmbientState. mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController); @@ -150,6 +143,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); mStackScroller.setEmptyShadeView(mEmptyShadeView); + when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true); when(mStackScrollLayoutController.getNoticationRoundessManager()) .thenReturn(mNotificationRoundnessManager); mStackScroller.setController(mStackScrollLayoutController); @@ -404,6 +398,22 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + public void testUpdateFooter_withoutHistory() { + setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); + + when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false); + when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); + when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) + .thenReturn(true); + + FooterView view = mock(FooterView.class); + mStackScroller.setFooterView(view); + mStackScroller.updateFooter(); + verify(mStackScroller).updateFooterView(true, true, false); + } + + @Test public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { setBarStateForTest(StatusBarState.SHADE); mStackScroller.setCurrentUserSetup(false); 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/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt new file mode 100644 index 000000000000..ac357ea34be0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -0,0 +1,73 @@ +package com.android.systemui.util.drawable + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ShapeDrawable +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class DrawableSizeTest : SysuiTestCase() { + + lateinit var resources: Resources + + @Before + fun setUp() { + resources = context.resources + } + + @Test + fun testDownscaleToSize_drawableZeroSize_unchanged() { + val drawable = ShapeDrawable() + val result = DrawableSize.downscaleToSize(resources, drawable, 100, 100) + assertThat(result).isSameInstanceAs(drawable) + } + + @Test + fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() { + val drawable = BitmapDrawable(resources, + Bitmap.createBitmap( + resources.displayMetrics, + 150, + 150, + Bitmap.Config.ARGB_8888 + ) + ) + val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) + assertThat(result).isSameInstanceAs(drawable) + } + + @Test + fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() { + // This bitmap would actually fail to resize if the method doesn't check for + // bitmap dimensions inside drawable. + val drawable = BitmapDrawable(resources, + Bitmap.createBitmap( + resources.displayMetrics, + 150, + 75, + Bitmap.Config.ARGB_8888 + ) + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) + assertThat(result).isNotSameInstanceAs(drawable) + assertThat(result.intrinsicWidth).isEqualTo(75) + assertThat(result.intrinsicHeight).isEqualTo(37) + } + + @Test + fun testDownscaleToSize_drawableAnimated_unchanged() { + val drawable = resources.getDrawable(android.R.drawable.stat_sys_download, + resources.newTheme()) + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + assertThat(result).isSameInstanceAs(drawable) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 9c49e98b9e36..ca37a40474e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -1339,6 +1339,22 @@ public class BubblesTest extends SysuiTestCase { assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); } + @Test + public void testStackViewOnBackPressed_updatesBubbleDataExpandState() { + mBubbleController.updateBubble(mBubbleEntry); + + // Expand the stack + mBubbleData.setExpanded(true); + assertStackExpanded(); + + // Hit back + BubbleStackView stackView = mBubbleController.getStackView(); + stackView.onBackPressed(); + + // Make sure we're collapsed + assertStackCollapsed(); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index e12a82a1c62b..d82671d6ecb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -1158,6 +1158,22 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); } + @Test + public void testStackViewOnBackPressed_updatesBubbleDataExpandState() { + mBubbleController.updateBubble(mBubbleEntry); + + // Expand the stack + mBubbleData.setExpanded(true); + assertStackExpanded(); + + // Hit back + BubbleStackView stackView = mBubbleController.getStackView(); + stackView.onBackPressed(); + + // Make sure we're collapsed + assertStackCollapsed(); + } + /** * Sets the bubble metadata flags for this entry. These flags are normally set by * NotificationManagerService when the notification is sent, however, these tests do not diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index e93ac47b1940..8b62a64f57d4 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -66,6 +66,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -269,6 +270,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ void onDoubleTap(int displayId); void onDoubleTapAndHold(int displayId); + } public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, @@ -2164,4 +2166,23 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ public void onDoubleTapAndHold(int displayId) { mSystemSupport.onDoubleTapAndHold(displayId); } -} + + /** + * Sets the scaling factor for animations. + */ + public void setAnimationScale(float scale) { + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Global.putFloat( + mContext.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, scale); + Settings.Global.putFloat( + mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, + scale); + Settings.Global.putFloat( + mContext.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, scale); + } finally { + Binder.restoreCallingIdentity(identity); + } + } +}
\ No newline at end of file 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/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index bb49ba059d23..75acf81a4a3c 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -39,7 +39,6 @@ import android.window.DisplayWindowPolicyController; import java.util.List; import java.util.Set; -import java.util.function.Consumer; /** @@ -62,7 +61,6 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { private final ArraySet<ComponentName> mAllowedActivities; @Nullable private final ArraySet<ComponentName> mBlockedActivities; - private Consumer<ActivityInfo> mActivityBlockedCallback; @NonNull final ArraySet<Integer> mRunningUids = new ArraySet<>(); @@ -83,12 +81,10 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { @NonNull ArraySet<UserHandle> allowedUsers, @Nullable Set<ComponentName> allowedActivities, @Nullable Set<ComponentName> blockedActivities, - @NonNull ActivityListener activityListener, - @NonNull Consumer<ActivityInfo> activityBlockedCallback) { + @NonNull ActivityListener activityListener) { mAllowedUsers = allowedUsers; mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities); mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities); - mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; } @@ -100,7 +96,6 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { for (int i = 0; i < activityCount; i++) { final ActivityInfo aInfo = activities.get(i); if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) { - mActivityBlockedCallback.accept(aInfo); return false; } } @@ -110,11 +105,7 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController { @Override public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags, int systemWindowFlags) { - if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) { - mActivityBlockedCallback.accept(activityInfo); - return false; - } - return true; + return canContainActivity(activityInfo, windowFlags, systemWindowFlags); } @Override diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 98a5ec1c3681..95b9e58a9dfd 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -34,8 +34,6 @@ import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; import android.graphics.Point; import android.graphics.PointF; import android.hardware.display.DisplayManager; @@ -59,7 +57,6 @@ import android.util.SparseArray; import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.BlockedAppActivity; import com.android.server.LocalServices; import java.io.FileDescriptor; @@ -421,8 +418,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub getAllowedUserHandles(), mParams.getAllowedActivities(), mParams.getBlockedActivities(), - createListenerAdapter(displayId), - activityInfo -> onActivityBlocked(displayId, activityInfo)); + createListenerAdapter(displayId)); mWindowPolicyControllers.put(displayId, dwpc); return dwpc; } @@ -445,16 +441,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } - private void onActivityBlocked(int displayId, ActivityInfo activityInfo) { - Intent intent = BlockedAppActivity.createStreamingBlockedIntent( - UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo, - mAssociationInfo.getDisplayName()); - mContext.startActivityAsUser( - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(), - mContext.getUser()); - } - private ArraySet<UserHandle> getAllowedUserHandles() { ArraySet<UserHandle> result = new ArraySet<>(); DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index ce30f0348f11..d719d77ea1be 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -509,8 +509,7 @@ public final class SystemServiceManager implements Dumpable { throw new IllegalArgumentException(onWhat + " what?"); } } catch (Exception ex) { - Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser - + " to service " + serviceName, ex); + logFailure(onWhat, curUser, serviceName, ex); } if (!submitToThreadPool) { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, @@ -584,8 +583,7 @@ public final class SystemServiceManager implements Dumpable { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + USER_STARTING + "User-" + curUserId); } catch (Exception e) { - Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser - + " to service " + serviceName, e); + logFailure(USER_STARTING, curUser, serviceName, e); Slog.e(TAG, "Disabling thread pool - please capture a bug report."); sUseLifecycleThreadPool = false; } finally { @@ -608,8 +606,7 @@ public final class SystemServiceManager implements Dumpable { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + USER_COMPLETED_EVENT + "User-" + curUserId); } catch (Exception e) { - Slog.wtf(TAG, "Failure reporting " + USER_COMPLETED_EVENT + " of user " + curUser - + " to service " + serviceName, e); + logFailure(USER_COMPLETED_EVENT, curUser, serviceName, e); throw e; } finally { t.traceEnd(); @@ -617,6 +614,12 @@ public final class SystemServiceManager implements Dumpable { }; } + /** Logs the failure. That's all. Tests may rely on parsing it, so only modify carefully. */ + private void logFailure(String onWhat, TargetUser curUser, String serviceName, Exception ex) { + Slog.wtf(TAG, "SystemService failure: Failure reporting " + onWhat + " of user " + + curUser + " to service " + serviceName, ex); + } + /** Sets the safe mode flag for services to query. */ void setSafeMode(boolean safeMode) { mSafeMode = safeMode; 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..e2204e2efaca 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. @@ -17888,6 +17905,11 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public boolean isAppFreezerEnabled() { + return mOomAdjuster.mCachedAppOptimizer.useFreezer(); + } + /** * Resets the state of the {@link com.android.server.am.AppErrors} instance. * This is intended for testing within the CTS only and is protected by diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 8561b61c2172..1131fa8a32b8 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -451,7 +451,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mUidsToRemove.clear(); mCurrentFuture = null; mUseLatestStates = true; - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { cancelSyncDueToBatteryLevelChangeLocked(); } if ((updateFlags & UPDATE_CPU) != 0) { @@ -496,7 +496,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.wtf(TAG, "Error updating external stats: ", e); } - if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { + if ((updateFlags & RESET) != 0) { + synchronized (BatteryExternalStatsWorker.this) { + mLastCollectionTimeStamp = 0; + } + } else if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { synchronized (BatteryExternalStatsWorker.this) { mLastCollectionTimeStamp = SystemClock.elapsedRealtime(); } @@ -658,7 +662,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC); } - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { mStats.updateKernelWakelocksLocked(elapsedRealtimeUs); mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs); } @@ -731,7 +735,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { uptime, networkStatsManager); } - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { // This helps mStats deal with ignoring data from prior to resets. mStats.informThatAllExternalStatsAreFlushed(); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5da461d8e392..2f7249ea5d84 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -797,12 +797,19 @@ public final class BatteryStatsService extends IBatteryStats.Stub final BatteryUsageStats bus; switch (atomTag) { case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET: - bus = getBatteryUsageStats(List.of(BatteryUsageStatsQuery.DEFAULT)).get(0); + final BatteryUsageStatsQuery querySinceReset = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .build(); + bus = getBatteryUsageStats(List.of(querySinceReset)).get(0); break; case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL: - final BatteryUsageStatsQuery powerProfileQuery = - new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(); - bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0); + final BatteryUsageStatsQuery queryPowerProfile = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .powerProfileModeledOnly() + .build(); + bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0); break; case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: if (!BATTERY_USAGE_STORE_ENABLED) { @@ -812,10 +819,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long sessionStart = mBatteryUsageStatsStore .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp(); final long sessionEnd = mStats.getStartClockTime(); - final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() - .aggregateSnapshots(sessionStart, sessionEnd) - .build(); - bus = getBatteryUsageStats(List.of(query)).get(0); + final BatteryUsageStatsQuery queryBeforeReset = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .aggregateSnapshots(sessionStart, sessionEnd) + .build(); + bus = getBatteryUsageStats(List.of(queryBeforeReset)).get(0); mBatteryUsageStatsStore .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd); break; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 8ad62605098c..a83fdd0e74cd 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -56,6 +56,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; import android.os.Process; @@ -1857,22 +1858,36 @@ public final class BroadcastQueue { } private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) { - final String targetPackage = getTargetPackage(r); - // Ignore non-explicit broadcasts - if (targetPackage == null) { - return; - } // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set // getIdForResponseEvent. + // TODO (217251579): Temporarily use temp-allowlist reason to identify + // push messages and record response events. + useTemporaryAllowlistReasonAsSignal(r); if (r.options == null || r.options.getIdForResponseEvent() <= 0) { return; } + final String targetPackage = getTargetPackage(r); + // Ignore non-explicit broadcasts + if (targetPackage == null) { + return; + } getUsageStatsManagerInternal().reportBroadcastDispatched( r.callingUid, targetPackage, UserHandle.of(r.userId), r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(), mService.getUidStateLocked(targetUid)); } + private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) { + if (r.options == null || r.options.getIdForResponseEvent() > 0) { + return; + } + final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode(); + if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING + || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) { + r.options.recordResponseEventWhileInBackground(reasonCode); + } + } + @NonNull private UsageStatsManagerInternal getUsageStatsManagerInternal() { final UsageStatsManagerInternal usageStatsManagerInternal = diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 6f22c61d2b2a..7af73ebda0c0 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -977,7 +977,7 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " received sync transactions while frozen, killing"); app.killLocked("Sync transaction while in frozen state", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true); processKilled = true; } @@ -990,7 +990,7 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " " + app.processName + ". Killing it. Exception: " + e); app.killLocked("Unable to query binder frozen stats", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); processKilled = true; } @@ -1007,7 +1007,7 @@ public final class CachedAppOptimizer { Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName + ". Killing it"); app.killLocked("Unable to unfreeze", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); return; } @@ -1423,7 +1423,7 @@ public final class CachedAppOptimizer { mFreezeHandler.post(() -> { synchronized (mAm) { proc.killLocked("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); } }); @@ -1477,7 +1477,7 @@ public final class CachedAppOptimizer { mFreezeHandler.post(() -> { synchronized (mAm) { proc.killLocked("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); } }); 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/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 65be5f06d08f..daf3561d75ce 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -29,7 +29,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioRoutesObserver; import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; @@ -500,12 +500,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void setWiredDeviceConnectionState(int type, - @AudioService.ConnectionState int state, String address, String name, - String caller) { + /*package*/ void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { //TODO move logging here just like in setBluetooth* methods synchronized (mDeviceStateLock) { - mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceInventory.setWiredDeviceConnectionState(attributes, state, caller); } } @@ -531,12 +530,12 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ static final class BtDeviceChangedData { final @Nullable BluetoothDevice mNewDevice; final @Nullable BluetoothDevice mPreviousDevice; - final @NonNull BtProfileConnectionInfo mInfo; + final @NonNull BluetoothProfileConnectionInfo mInfo; final @NonNull String mEventSource; BtDeviceChangedData(@Nullable BluetoothDevice newDevice, @Nullable BluetoothDevice previousDevice, - @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) { + @NonNull BluetoothProfileConnectionInfo info, @NonNull String eventSource) { mNewDevice = newDevice; mPreviousDevice = previousDevice; mInfo = info; @@ -568,9 +567,9 @@ import java.util.concurrent.atomic.AtomicBoolean; mDevice = device; mState = state; mProfile = d.mInfo.getProfile(); - mSupprNoisy = d.mInfo.getSuppressNoisyIntent(); + mSupprNoisy = d.mInfo.isSuppressNoisyIntent(); mVolume = d.mInfo.getVolume(); - mIsLeOutput = d.mInfo.getIsLeOutput(); + mIsLeOutput = d.mInfo.isLeOutput(); mEventSource = d.mEventSource; mAudioSystemDevice = audioDevice; mMusicDevice = AudioSystem.DEVICE_NONE; @@ -641,7 +640,7 @@ import java.util.concurrent.atomic.AtomicBoolean; audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID; break; case BluetoothProfile.LE_AUDIO: - if (d.mInfo.getIsLeOutput()) { + if (d.mInfo.isLeOutput()) { audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET; } else { audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET; @@ -1013,11 +1012,9 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) { synchronized (mDeviceStateLock) { - return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName, - false /*for test*/); + return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 4ae1bd371690..0e290410d288 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -230,19 +230,15 @@ public class AudioDeviceInventory { * A class just for packaging up a set of connection parameters. */ /*package*/ class WiredDeviceConnectionState { - public final int mType; + public final AudioDeviceAttributes mAttributes; public final @AudioService.ConnectionState int mState; - public final String mAddress; - public final String mName; public final String mCaller; public boolean mForTest = false; - /*package*/ WiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { - mType = type; + /*package*/ WiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { + mAttributes = attributes; mState = state; - mAddress = address; - mName = name; mCaller = caller; } } @@ -280,11 +276,10 @@ public class AudioDeviceInventory { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { - mAudioSystem.setDeviceConnectionState( - di.mDeviceType, - AudioSystem.DEVICE_STATE_AVAILABLE, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, - di.mDeviceName, + di.mDeviceName), + AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } } @@ -519,41 +514,45 @@ public class AudioDeviceInventory { /*package*/ void onSetWiredDeviceConnectionState( AudioDeviceInventory.WiredDeviceConnectionState wdcs) { + int type = wdcs.mAttributes.getInternalType(); + AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onSetWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType)) + .set(MediaMetrics.Property.ADDRESS, wdcs.mAttributes.getAddress()) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(type)) .set(MediaMetrics.Property.STATE, wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) - && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state DISCONNECTED"); } - if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, - wdcs.mType, wdcs.mAddress, wdcs.mName, wdcs.mForTest)) { + if (!handleDeviceConnection(wdcs.mAttributes, + wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) { // change of connection state failed, bailout mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") .record(); return; } if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { - if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state not DISCONNECTED"); } - mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller); + mDeviceBroker.checkMusicActive(type, wdcs.mCaller); } - if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) { + if (type == AudioSystem.DEVICE_OUT_HDMI) { mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller); } - sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName); - updateAudioRoutes(wdcs.mType, wdcs.mState); + sendDeviceConnectionIntent(type, wdcs.mState, + wdcs.mAttributes.getAddress(), wdcs.mAttributes.getName()); + updateAudioRoutes(type, wdcs.mState); } mmi.record(); } @@ -572,12 +571,12 @@ public class AudioDeviceInventory { return; } // Toggle HDMI to retrigger broadcast with proper formats. - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "", - "android"); // disconnect - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_AVAILABLE, "", "", - "android"); // reconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect } mmi.record(); } @@ -707,16 +706,17 @@ public class AudioDeviceInventory { /** * Implements the communication with AudioSystem to (dis)connect a device in the native layers + * @param attributes the attributes of the device * @param connect true if connection - * @param device the device type - * @param address the address of the device - * @param deviceName human-readable name of device * @param isForTesting if true, not calling AudioSystem for the connection as this is * just for testing * @return false if an error was reported by AudioSystem */ - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName, boolean isForTesting) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect, + boolean isForTesting) { + int device = attributes.getInternalType(); + String address = attributes.getAddress(); + String deviceName = attributes.getName(); if (AudioService.DEBUG_DEVICES) { Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device) + " address:" + address @@ -743,9 +743,8 @@ public class AudioDeviceInventory { if (isForTesting) { res = AudioSystem.AUDIO_STATUS_OK; } else { - res = mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + res = mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); } if (res != AudioSystem.AUDIO_STATUS_OK) { final String reason = "not connecting device 0x" + Integer.toHexString(device) @@ -762,9 +761,8 @@ public class AudioDeviceInventory { mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } else if (!connect && isConnected) { - mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); @@ -941,13 +939,13 @@ public class AudioDeviceInventory { return delay; } - /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { + /*package*/ int setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { synchronized (mDevicesLock) { - int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE); + int delay = checkSendBecomingNoisyIntentInt( + attributes.getInternalType(), state, AudioSystem.DEVICE_NONE); mDeviceBroker.postSetWiredDeviceConnectionState( - new WiredDeviceConnectionState(type, state, address, name, caller), - delay); + new WiredDeviceConnectionState(attributes, state, caller), delay); return delay; } } @@ -955,8 +953,7 @@ public class AudioDeviceInventory { /*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device, @AudioService.ConnectionState int state) { final WiredDeviceConnectionState connection = new WiredDeviceConnectionState( - device.getInternalType(), state, device.getAddress(), - "test device", "com.android.server.audio"); + device, state, "com.android.server.audio"); connection.mForTest = true; onSetWiredDeviceConnectionState(connection); } @@ -972,8 +969,9 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec); // TODO: log in MediaMetrics once distinction between connection failure and // double connection is made. @@ -1035,8 +1033,9 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.clearAvrcpAbsoluteVolumeSupported(); - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( @@ -1074,8 +1073,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), @@ -1085,8 +1085,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); @@ -1099,8 +1100,9 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), @@ -1122,8 +1124,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); @@ -1140,14 +1143,14 @@ public class AudioDeviceInventory { private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device, String eventSource) { if (device != AudioSystem.DEVICE_NONE) { - /* Audio Policy sees Le Audio similar to A2DP. Let's make sure * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set */ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, - address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); @@ -1168,8 +1171,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeLeAudioDeviceUnavailable(String address, int device) { if (device != AudioSystem.DEVICE_NONE) { - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, - address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 407d42ea510d..05955c3cab44 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -85,7 +85,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -6371,23 +6371,23 @@ public class AudioService extends IAudioService.Stub /** * see AudioManager.setWiredDeviceConnectionState() */ - public void setWiredDeviceConnectionState(int type, - @ConnectionState int state, String address, String name, - String caller) { + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @ConnectionState int state, String caller) { enforceModifyAudioRoutingPermission(); if (state != CONNECTION_STATE_CONNECTED && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); } new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ADDRESS, attributes.getAddress()) .set(MediaMetrics.Property.CLIENT_NAME, caller) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type)) - .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(attributes.getInternalType())) + .set(MediaMetrics.Property.NAME, attributes.getName()) .set(MediaMetrics.Property.STATE, state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected") .record(); - mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceBroker.setWiredDeviceConnectionState(attributes, state, caller); } /** @see AudioManager#setTestDeviceConnectionState(AudioDeviceAttributes, boolean) */ @@ -6434,14 +6434,14 @@ public class AudioService extends IAudioService.Stub * See AudioManager.handleBluetoothActiveDeviceChanged(...) */ public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice, - BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + BluetoothDevice previousDevice, @NonNull BluetoothProfileConnectionInfo info) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Bluetooth is the only caller allowed"); } if (info == null) { - throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device " - + previousDevice + " -> " + newDevice); + throw new IllegalArgumentException("Illegal null BluetoothProfileConnectionInfo for" + + " device " + previousDevice + " -> " + newDevice); } final int profile = info.getProfile(); if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 3137fa5784d2..3225274a8a9b 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -116,10 +116,11 @@ public class AudioServiceEvents { @Override public String eventToString() { return new StringBuilder("setWiredDeviceConnectionState(") - .append(" type:").append(Integer.toHexString(mState.mType)) + .append(" type:").append( + Integer.toHexString(mState.mAttributes.getInternalType())) .append(" state:").append(AudioSystem.deviceStateToString(mState.mState)) - .append(" addr:").append(mState.mAddress) - .append(" name:").append(mState.mName) + .append(" addr:").append(mState.mAttributes.getAddress()) + .append(" name:").append(mState.mAttributes.getName()) .append(") from ").append(mState.mCaller).toString(); } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index a2ba76b6fd6a..f572261c09e5 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -258,19 +258,16 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { } /** - * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} - * @param device + * Same as {@link AudioSystem#setDeviceConnectionState(AudioDeviceAttributes, int, int)} + * @param attributes * @param state - * @param deviceAddress - * @param deviceName * @param codecFormat * @return */ - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { invalidateRoutingCache(); - return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, - codecFormat); + return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat); } /** diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 47f31d505867..3491cd59ebb7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -31,7 +31,7 @@ import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; import android.os.UserHandle; import android.provider.Settings; @@ -40,6 +40,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -489,11 +490,13 @@ public class BtHelper { if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) { mDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, - new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + new BluetoothProfileConnectionInfo(profile), + "mBluetoothProfileServiceListener")); } else { mDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(null, btDevice, - new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + new BluetoothProfileConnectionInfo(profile), + "mBluetoothProfileServiceListener")); } } @@ -503,7 +506,12 @@ public class BtHelper { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); mBluetoothHeadset = headset; - setBtScoActiveDevice(headset != null ? headset.getActiveDevice() : null); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + List<BluetoothDevice> activeDevices = Collections.emptyList(); + if (adapter != null) { + activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET); + } + setBtScoActiveDevice((activeDevices.size() > 0) ? activeDevices.get(0) : null); // Refresh SCO audio state checkScoAudioState(); if (mScoAudioState != SCO_STATE_ACTIVATE_REQ @@ -587,8 +595,9 @@ public class BtHelper { String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(), - audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName), + isActive); } else { int[] outDeviceTypes = { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, @@ -596,13 +605,15 @@ public class BtHelper { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; for (int outDeviceType : outDeviceTypes) { - result |= mDeviceBroker.handleDeviceConnection( - isActive, outDeviceType, audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + outDeviceType, audioDevice.getAddress(), btDeviceName), + isActive); } } // handleDeviceConnection() && result to make sure the method get executed - result = mDeviceBroker.handleDeviceConnection( - isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result; + result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + inDevice, audioDevice.getAddress(), btDeviceName), + isActive) && result; return result; } 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/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index afcd3dde0633..1ce36b181eb0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -41,7 +41,10 @@ import android.hardware.hdmi.HdmiRecordSources; import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; -import android.media.AudioSystem; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; +import android.media.AudioProfile; import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager.TvInputCallback; import android.util.Slog; @@ -58,6 +61,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; /** * Represent a logical device of type TV residing in Android system. @@ -816,12 +820,23 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled); boolean oldStatus = mArcEstablished; - // 1. Enable/disable ARC circuit. - enableAudioReturnChannel(enabled); - // 2. Notify arc status to audio service. - notifyArcStatusToAudioService(enabled); - // 3. Update arc status; - mArcEstablished = enabled; + if (enabled) { + RequestSadAction action = new RequestSadAction( + this, Constants.ADDR_AUDIO_SYSTEM, + new RequestSadAction.RequestSadCallback() { + @Override + public void onRequestSadDone(List<byte[]> supportedSads) { + enableAudioReturnChannel(enabled); + notifyArcStatusToAudioService(enabled, supportedSads); + mArcEstablished = enabled; + } + }); + addAndStartAction(action); + } else { + enableAudioReturnChannel(enabled); + notifyArcStatusToAudioService(enabled, new ArrayList<>()); + mArcEstablished = enabled; + } return oldStatus; } @@ -843,11 +858,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return mService.isConnected(portId); } - private void notifyArcStatusToAudioService(boolean enabled) { + private void notifyArcStatusToAudioService(boolean enabled, List<byte[]> supportedSads) { // Note that we don't set any name to ARC. - mService.getAudioManager().setWiredDeviceConnectionState( - AudioSystem.DEVICE_OUT_HDMI_ARC, - enabled ? 1 : 0, "", ""); + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_ARC, "", "", + new ArrayList<AudioProfile>(), supportedSads.stream() + .map(sad -> new AudioDescriptor(AudioDescriptor.STANDARD_EDID, + AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, sad)) + .collect(Collectors.toList())); + mService.getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0); } /** diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index bfaa7b37fa33..354d1838709d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -150,8 +150,6 @@ public class InputManagerService extends IInputManager.Stub static final String TAG = "InputManager"; static final boolean DEBUG = false; - private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = true; - private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml"; @@ -303,7 +301,7 @@ public class InputManagerService extends IInputManager.Stub int locationKeyCode); private static native InputChannel nativeCreateInputChannel(long ptr, String name); private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId, - boolean isGestureMonitor, String name, int pid); + String name, int pid); private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken); private static native void nativePilferPointers(long ptr, IBinder token); private static native void nativeSetInputFilterEnabled(long ptr, boolean enable); @@ -720,8 +718,7 @@ public class InputManagerService extends IInputManager.Stub throw new IllegalArgumentException("displayId must >= 0."); } - return nativeCreateInputMonitor(mPtr, displayId, false /* isGestureMonitor */, - inputChannelName, Binder.getCallingPid()); + return nativeCreateInputMonitor(mPtr, displayId, inputChannelName, Binder.getCallingPid()); } @NonNull @@ -790,10 +787,7 @@ public class InputManagerService extends IInputManager.Stub final long ident = Binder.clearCallingIdentity(); try { final InputChannel inputChannel = - USE_SPY_WINDOW_GESTURE_MONITORS - ? createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid) - : nativeCreateInputMonitor(mPtr, displayId, true /*isGestureMonitor*/, - requestedName, pid); + createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid); return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken())); } finally { Binder.restoreCallingIdentity(ident); @@ -3375,11 +3369,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void dispose() { - if (USE_SPY_WINDOW_GESTURE_MONITORS) { - removeSpyWindowGestureMonitor(mInputChannelToken); - return; - } - nativeRemoveInputChannel(mPtr, mInputChannelToken); + removeSpyWindowGestureMonitor(mInputChannelToken); } } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index caec391be418..9846a2ba48a4 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -56,7 +56,8 @@ final class HandwritingEventReceiverSurface { mWindowHandle.name = name; mWindowHandle.token = mClientChannel.getToken(); mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; - mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mWindowHandle.visible = true; mWindowHandle.focusable = false; 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/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 91de9e559e13..728782ccee0b 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -45,7 +45,6 @@ import android.util.SparseIntArray; import com.android.internal.R; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -448,15 +447,16 @@ class BluetoothRouteProvider { case BluetoothProfile.A2DP: mA2dpProfile = (BluetoothA2dp) proxy; // It may contain null. - activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice()); + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP); break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = (BluetoothHearingAid) proxy; - activeDevices = mHearingAidProfile.getActiveDevices(); + activeDevices = mBluetoothAdapter.getActiveDevices( + BluetoothProfile.HEARING_AID); break; case BluetoothProfile.LE_AUDIO: mLeAudioProfile = (BluetoothLeAudio) proxy; - activeDevices = mLeAudioProfile.getActiveDevices(); + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO); break; default: return; diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index 9f086e6e4a61..d745a2325b22 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -48,8 +48,8 @@ import com.android.server.SystemServerInitThreadPool; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.pkg.SELinuxUtil; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SELinuxUtil; import dalvik.system.VMRuntime; @@ -549,6 +549,10 @@ final class AppDataHelper { } public void migrateKeyStoreData(int previousAppId, int appId) { + // If previous UID is system UID, declaring inheritKeyStoreKeys is not supported. + // Silently ignore the request to migrate keys. + if (previousAppId == Process.SYSTEM_UID) return; + for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) { int srcUid = UserHandle.getUid(userId, previousAppId); int destUid = UserHandle.getUid(userId, appId); diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index ba89916e6dfa..53eb9cf7d9fe 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -293,7 +293,8 @@ final class DexOptHelper { public ArraySet<String> getOptimizablePackages() { ArraySet<String> pkgs = new ArraySet<>(); mPm.forEachPackageState(packageState -> { - if (mPm.mPackageDexOptimizer.canOptimizePackage(packageState.getPkg())) { + final AndroidPackage pkg = packageState.getPkg(); + if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { pkgs.add(packageState.getPackageName()); } }); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index a5b42f03b6df..69d498794e64 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -182,7 +182,7 @@ public class PackageDexOptimizer { mInjector = injector; } - boolean canOptimizePackage(AndroidPackage pkg) { + boolean canOptimizePackage(@NonNull AndroidPackage pkg) { // We do not dexopt a package with no code. // Note that the system package is marked as having no code, however we can // still optimize it via dexoptSystemServerPath. diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java index d455be7e4a69..46fde4b59d8b 100644 --- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java +++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java @@ -108,14 +108,18 @@ public class OneTimePermissionUserManager { * </p> * @param packageName The package to start a one-time permission session for * @param timeoutMillis Number of milliseconds for an app to be in an inactive state + * @param revokeAfterKilledDelayMillis Number of milliseconds to wait after the process dies + * before ending the session. Set to -1 to use default value + * for the device. * @param importanceToResetTimer The least important level to uid must be to reset the timer * @param importanceToKeepSessionAlive The least important level the uid must be to keep the - * session alive + * session alive * * @hide */ void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis, - int importanceToResetTimer, int importanceToKeepSessionAlive) { + long revokeAfterKilledDelayMillis, int importanceToResetTimer, + int importanceToKeepSessionAlive) { int uid; try { uid = mContext.getPackageManager().getPackageUid(packageName, 0); @@ -126,11 +130,15 @@ public class OneTimePermissionUserManager { synchronized (mLock) { PackageInactivityListener listener = mListeners.get(uid); - if (listener == null) { - listener = new PackageInactivityListener(uid, packageName, timeoutMillis, + if (listener != null) { + listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer, importanceToKeepSessionAlive); - mListeners.put(uid, listener); + return; } + listener = new PackageInactivityListener(uid, packageName, timeoutMillis, + revokeAfterKilledDelayMillis, importanceToResetTimer, + importanceToKeepSessionAlive); + mListeners.put(uid, listener); } } @@ -159,18 +167,6 @@ public class OneTimePermissionUserManager { } /** - * The delay to wait before revoking on the event an app is terminated. Recommended to be long - * enough so that apps don't lose permission on an immediate restart - */ - private long getKilledDelayMillis(boolean isSelfRevokedPermissionSession) { - if (isSelfRevokedPermissionSession) { - return 0; - } - return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, - PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS); - } - - /** * Register to listen for Uids being uninstalled. This must be done outside of the * PermissionManagerService lock. */ @@ -178,18 +174,6 @@ public class OneTimePermissionUserManager { mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED)); } - void setSelfRevokedPermissionSession(int uid) { - synchronized (mLock) { - PackageInactivityListener listener = mListeners.get(uid); - if (listener == null) { - Log.e(LOG_TAG, "Could not set session for uid " + uid - + " as self-revoke session: session not found"); - return; - } - listener.setSelfRevokedPermissionSession(); - } - } - /** * A class which watches a package for inactivity and notifies the permission controller when * the package becomes inactive @@ -200,11 +184,11 @@ public class OneTimePermissionUserManager { private final int mUid; private final @NonNull String mPackageName; - private final long mTimeout; - private final int mImportanceToResetTimer; - private final int mImportanceToKeepSessionAlive; + private long mTimeout; + private long mRevokeAfterKilledDelay; + private int mImportanceToResetTimer; + private int mImportanceToKeepSessionAlive; - private boolean mIsSelfRevokedPermissionSession; private boolean mIsAlarmSet; private boolean mIsFinished; @@ -218,16 +202,23 @@ public class OneTimePermissionUserManager { private final Object mToken = new Object(); private PackageInactivityListener(int uid, @NonNull String packageName, long timeout, - int importanceToResetTimer, int importanceToKeepSessionAlive) { + long revokeAfterkilledDelay, int importanceToResetTimer, + int importanceToKeepSessionAlive) { Log.i(LOG_TAG, "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout + + " killedDelay=" + revokeAfterkilledDelay + " importanceToResetTimer=" + importanceToResetTimer + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive); mUid = uid; mPackageName = packageName; mTimeout = timeout; + mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1 + ? DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY, + DEFAULT_KILLED_DELAY_MILLIS) + : revokeAfterkilledDelay; mImportanceToResetTimer = importanceToResetTimer; mImportanceToKeepSessionAlive = importanceToKeepSessionAlive; @@ -247,6 +238,28 @@ public class OneTimePermissionUserManager { onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName)); } + public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis, + int importanceToResetTimer, int importanceToKeepSessionAlive) { + synchronized (mInnerLock) { + mTimeout = Math.min(mTimeout, timeoutMillis); + mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay, + revokeAfterKilledDelayMillis == -1 + ? DeviceConfig.getLong( + DeviceConfig.NAMESPACE_PERMISSIONS, + PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS) + : revokeAfterKilledDelayMillis); + mImportanceToResetTimer = Math.min(importanceToResetTimer, mImportanceToResetTimer); + mImportanceToKeepSessionAlive = Math.min(importanceToKeepSessionAlive, + mImportanceToKeepSessionAlive); + Log.v(LOG_TAG, + "Updated params for " + mPackageName + ". timeout=" + mTimeout + + " killedDelay=" + mRevokeAfterKilledDelay + + " importanceToResetTimer=" + mImportanceToResetTimer + + " importanceToKeepSessionAlive=" + mImportanceToKeepSessionAlive); + onImportanceChanged(mUid, mActivityManager.getPackageImportance(mPackageName)); + } + } + private void onImportanceChanged(int uid, int importance) { if (uid != mUid) { return; @@ -271,7 +284,7 @@ public class OneTimePermissionUserManager { } onImportanceChanged(mUid, imp); } - }, mToken, getKilledDelayMillis(mIsSelfRevokedPermissionSession)); + }, mToken, mRevokeAfterKilledDelay); return; } if (importance > mImportanceToResetTimer) { @@ -307,14 +320,6 @@ public class OneTimePermissionUserManager { } /** - * Marks the session as a self-revoke session, which does not delay the revocation when - * the app is restarting. - */ - public void setSelfRevokedPermissionSession() { - mIsSelfRevokedPermissionSession = true; - } - - /** * Set the alarm which will callback when the package is inactive */ @GuardedBy("mInnerLock") diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index edc0e3d64c42..695d6dd0bc76 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -68,7 +68,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.internal.util.function.TriFunction; import com.android.server.LocalServices; @@ -388,7 +387,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void startOneTimePermissionSession(String packageName, @UserIdInt int userId, - long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) { + long timeoutMillis, long revokeAfterKilledDelayMillis, int importanceToResetTimer, + int importanceToKeepSessionAlive) { mContext.enforceCallingOrSelfPermission( Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS, "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS @@ -398,7 +398,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long token = Binder.clearCallingIdentity(); try { getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName, - timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive); + timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer, + importanceToKeepSessionAlive); } finally { Binder.restoreCallingIdentity(token); } @@ -563,16 +564,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void revokeOwnPermissionsOnKill(@NonNull String packageName, @NonNull List<String> permissions) { - final int callingUid = Binder.getCallingUid(); - final int callingUserId = UserHandle.getUserId(callingUid); - AndroidFuture<Void> future = new AndroidFuture<>(); - future.whenComplete((result, err) -> { - if (err == null) { - getOneTimePermissionUserManager(callingUserId) - .setSelfRevokedPermissionSession(callingUid); - } - }); - mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions, future); + mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index db9c1b56e4d8..ed351fd4aef9 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -108,7 +108,6 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; -import com.android.internal.infra.AndroidFuture; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; @@ -1592,8 +1591,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } @Override - public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions, - AndroidFuture<Void> callback) { + public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions) { final int callingUid = Binder.getCallingUid(); int callingUserId = UserHandle.getUserId(callingUid); int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId); @@ -1608,8 +1606,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt + permName + " because it does not hold that permission"); } } - mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions, - callback); + mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions); } private boolean mayManageRolePermission(int uid) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java index 91c558b2f35e..3e28320a2130 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -27,7 +27,6 @@ import android.content.pm.permission.SplitPermissionInfoParcelable; import android.permission.IOnPermissionsChangeListener; import android.permission.PermissionManagerInternal; -import com.android.internal.infra.AndroidFuture; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.FileDescriptor; @@ -338,16 +337,16 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte * <li>Each permission in {@code permissions} must be a runtime permission. * </ul> * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. This revocation happens asynchronously and kills all processes running in the - * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * This revocation happens asynchronously and kills all processes running in the same UID as + * {@code packageName}. It will be triggered once it is safe to do so. * * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. - * @param callback Callback called when the revocation request has been completed. */ - void revokeOwnPermissionsOnKill(String packageName, List<String> permissions, - AndroidFuture<Void> callback); + void revokeOwnPermissionsOnKill(String packageName, List<String> permissions); /** * Get whether you should show UI with rationale for requesting a permission. You should do this diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java index f519ceda1e50..243efb5e58ce 100644 --- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java +++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java @@ -16,6 +16,8 @@ package com.android.server.security; +import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE; import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN; import android.content.Context; @@ -76,10 +78,24 @@ public class AttestationVerificationManagerService extends SystemService { private void verifyAttestationForAllVerifiers( AttestationProfile profile, int localBindingType, Bundle requirements, byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) { - // TODO(b/201696614): Implement IVerificationResult result = new IVerificationResult(); - result.resultCode = RESULT_UNKNOWN; + // TODO(b/201696614): Implement result.token = null; + switch (profile.getAttestationProfileId()) { + case PROFILE_SELF_TRUSTED: + Slog.d(TAG, "Verifying Self trusted profile."); + try { + result.resultCode = + AttestationVerificationSelfTrustedVerifierForTesting.getInstance() + .verifyAttestation(localBindingType, requirements, attestation); + } catch (Throwable t) { + result.resultCode = RESULT_FAILURE; + } + break; + default: + Slog.d(TAG, "No profile found, defaulting."); + result.resultCode = RESULT_UNKNOWN; + } resultCallback.complete(result); } diff --git a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java new file mode 100644 index 000000000000..58df2bd982dc --- /dev/null +++ b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java @@ -0,0 +1,224 @@ +/* + * 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.security; + +import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS; +import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; + +import android.annotation.NonNull; +import android.os.Build; +import android.os.Bundle; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.internal.org.bouncycastle.asn1.ASN1OctetString; +import com.android.internal.org.bouncycastle.asn1.ASN1Sequence; +import com.android.internal.org.bouncycastle.asn1.x509.Certificate; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Verifies {@code PROFILE_SELF_TRUSTED} attestations. + * + * Verifies that the attesting environment can create an attestation with the same root certificate + * as the verifying device with a matching attestation challenge. Skips CRL revocations checking + * so this verifier can work in a hermetic test environment. + * + * This verifier profile is intended to be used only for testing. + */ +class AttestationVerificationSelfTrustedVerifierForTesting { + private static final String TAG = "AVF"; + private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE); + + // The OID for the extension Android Keymint puts into device-generated certificates. + private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID = + "1.3.6.1.4.1.11129.2.1.17"; + + // ASN.1 sequence index values for the Android Keymint extension. + private static final int ATTESTATION_CHALLENGE_INDEX = 4; + + private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; + private static final String GOLDEN_ALIAS = + AttestationVerificationSelfTrustedVerifierForTesting.class.getCanonicalName() + + ".Golden"; + + private static volatile AttestationVerificationSelfTrustedVerifierForTesting + sAttestationVerificationSelfTrustedVerifier = null; + + private final CertificateFactory mCertificateFactory; + private final CertPathValidator mCertPathValidator; + private final KeyStore mAndroidKeyStore; + private X509Certificate mGoldenRootCert; + + static AttestationVerificationSelfTrustedVerifierForTesting getInstance() + throws Exception { + if (sAttestationVerificationSelfTrustedVerifier == null) { + synchronized (AttestationVerificationSelfTrustedVerifierForTesting.class) { + if (sAttestationVerificationSelfTrustedVerifier == null) { + sAttestationVerificationSelfTrustedVerifier = + new AttestationVerificationSelfTrustedVerifierForTesting(); + } + } + } + return sAttestationVerificationSelfTrustedVerifier; + } + + private static void debugVerboseLog(String str, Throwable t) { + if (DEBUG) { + Slog.v(TAG, str, t); + } + } + + private static void debugVerboseLog(String str) { + if (DEBUG) { + Slog.v(TAG, str); + } + } + + private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception { + mCertificateFactory = CertificateFactory.getInstance("X.509"); + mCertPathValidator = CertPathValidator.getInstance("PKIX"); + mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE); + mAndroidKeyStore.load(null); + if (!mAndroidKeyStore.containsAlias(GOLDEN_ALIAS)) { + KeyPairGenerator kpg = + KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE); + KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder( + GOLDEN_ALIAS, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) + .setAttestationChallenge(GOLDEN_ALIAS.getBytes()) + .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).build(); + kpg.initialize(parameterSpec); + kpg.generateKeyPair(); + } + + X509Certificate[] goldenCerts = (X509Certificate[]) + ((KeyStore.PrivateKeyEntry) mAndroidKeyStore.getEntry(GOLDEN_ALIAS, null)) + .getCertificateChain(); + mGoldenRootCert = goldenCerts[goldenCerts.length - 1]; + } + + int verifyAttestation( + int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) { + List<X509Certificate> certificates = new ArrayList<>(); + ByteArrayInputStream bis = new ByteArrayInputStream(attestation); + try { + while (bis.available() > 0) { + certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis)); + } + } catch (CertificateException e) { + debugVerboseLog("Unable to parse certificates from attestation", e); + return RESULT_FAILURE; + } + + if (localBindingType == TYPE_CHALLENGE + && validateRequirements(requirements) + && checkLeafChallenge(requirements, certificates) + && verifyCertificateChain(certificates)) { + return RESULT_SUCCESS; + } + + return RESULT_FAILURE; + } + + private boolean verifyCertificateChain(List<X509Certificate> certificates) { + if (certificates.size() < 2) { + debugVerboseLog("Certificate chain less than 2 in size."); + return false; + } + + try { + CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); + PKIXParameters validationParams = new PKIXParameters(getTrustAnchors()); + // Skipping revocation checking because we want this to work in a hermetic test + // environment. + validationParams.setRevocationEnabled(false); + mCertPathValidator.validate(certificatePath, validationParams); + } catch (Throwable t) { + debugVerboseLog("Invalid certificate chain", t); + return false; + } + + return true; + } + + private Set<TrustAnchor> getTrustAnchors() { + return Collections.singleton(new TrustAnchor(mGoldenRootCert, null)); + } + + private boolean validateRequirements(Bundle requirements) { + if (requirements.size() != 1) { + debugVerboseLog("Requirements does not contain exactly 1 key."); + return false; + } + if (!requirements.containsKey(PARAM_CHALLENGE)) { + debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE); + return false; + } + return true; + } + + private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) { + // Verify challenge + byte[] challenge; + try { + challenge = getChallengeFromCert(certificates.get(0)); + } catch (Throwable t) { + debugVerboseLog("Unable to parse challenge from certificate.", t); + return false; + } + + if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) { + return true; + } else { + debugVerboseLog("Self-Trusted validation failed; challenge mismatch."); + return false; + } + } + + private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate) + throws CertificateEncodingException, IOException { + Certificate certificate = Certificate.getInstance( + new ASN1InputStream(x509Certificate.getEncoded()).readObject()); + ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions() + .getExtensionParsedValue( + new ASN1ObjectIdentifier(ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID)); + return ((ASN1OctetString) keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX)) + .getOctets(); + } +} 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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8d4ae908e79f..ca4d7178fbcc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -24,6 +24,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; +import static android.Manifest.permission.MANAGE_GAME_ACTIVITY; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; @@ -1771,6 +1772,43 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public int startActivityFromGameSession(IApplicationThread caller, String callingPackage, + String callingFeatureId, int callingPid, int callingUid, Intent intent, int taskId, + int userId) { + if (checkCallingPermission(MANAGE_GAME_ACTIVITY) != PERMISSION_GRANTED) { + final String msg = "Permission Denial: startActivityFromGameSession() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + MANAGE_GAME_ACTIVITY; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + assertPackageMatchesCallingUid(callingPackage); + + final ActivityOptions activityOptions = ActivityOptions.makeBasic(); + activityOptions.setLaunchTaskId(taskId); + + userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityFromGameSession"); + + final long origId = Binder.clearCallingIdentity(); + try { + return getActivityStartController() + .obtainStarter(intent, "startActivityFromGameSession") + .setCaller(caller) + .setCallingUid(callingUid) + .setCallingPid(callingPid) + .setCallingPackage(intent.getPackage()) + .setCallingFeatureId(callingFeatureId) + .setUserId(userId) + .setActivityOptions(activityOptions.toBundle()) + .setRealCallingUid(Binder.getCallingUid()) + .execute(); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public BackNavigationInfo startBackNavigation() { mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "startBackNavigation()"); 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/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 8cb27e179c19..e5529f1997a3 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -265,7 +265,6 @@ public: base::Result<std::unique_ptr<InputChannel>> createInputChannel(JNIEnv* env, const std::string& name); base::Result<std::unique_ptr<InputChannel>> createInputMonitor(JNIEnv* env, int32_t displayId, - bool isGestureMonitor, const std::string& name, int32_t pid); status_t removeInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken); @@ -522,11 +521,9 @@ base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChann } base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor( - JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name, - int32_t pid) { + JNIEnv* /* env */, int32_t displayId, const std::string& name, int32_t pid) { ATRACE_CALL(); - return mInputManager->getDispatcher().createInputMonitor(displayId, isGestureMonitor, name, - pid); + return mInputManager->getDispatcher().createInputMonitor(displayId, name, pid); } status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */, @@ -1659,7 +1656,7 @@ static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong p } static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId, - jboolean isGestureMonitor, jstring nameObj, jint pid) { + jstring nameObj, jint pid) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); if (displayId == ADISPLAY_ID_NONE) { @@ -1672,7 +1669,7 @@ static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong p std::string name = nameChars.c_str(); base::Result<std::unique_ptr<InputChannel>> inputChannel = - im->createInputMonitor(env, displayId, isGestureMonitor, name, pid); + im->createInputMonitor(env, displayId, name, pid); if (!inputChannel.ok()) { std::string message = inputChannel.error().message(); @@ -2381,7 +2378,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation}, {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;", (void*)nativeCreateInputChannel}, - {"nativeCreateInputMonitor", "(JIZLjava/lang/String;I)Landroid/view/InputChannel;", + {"nativeCreateInputMonitor", "(JILjava/lang/String;I)Landroid/view/InputChannel;", (void*)nativeCreateInputMonitor}, {"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel}, {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers}, diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 166a0f5272cf..0584604ac7b9 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -509,7 +509,7 @@ uint32_t GnssCallback::getConstellationType( template <class T_list, class T_sv_info> Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) { // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework. - if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() == 1) { + if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() <= 1) { if (!isSvStatusRegistered) { return Void(); } @@ -695,7 +695,7 @@ Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) { Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) { // In AIDL v1, if no listener is registered, do not report nmea to the framework. - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() == 1) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() <= 1) { if (!isNmeaRegistered) { return Status::ok(); } 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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6f41d42b3d54..762d4c1b9a78 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -134,6 +134,7 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPR import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; +import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.provider.Telephony.Carriers.DPC_URI; import static android.provider.Telephony.Carriers.ENFORCE_KEY; @@ -222,6 +223,7 @@ import android.compat.annotation.EnabledSince; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.IIntentReceiver; @@ -10951,7 +10953,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mPendingUserCreatedCallbackTokens.contains(token)) { // Ignore because it was triggered by createAndManageUser() Slogf.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId - + " due to token" + token); + + " due to token " + token); mPendingUserCreatedCallbackTokens.remove(token); return; } @@ -11083,6 +11085,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { switched = mInjector.getIActivityManager().switchUser(userId); if (!switched) { Slogf.w(LOG_TAG, "Failed to switch to user %d", userId); + } else { + Slogf.d(LOG_TAG, "Switched"); } return switched; } catch (RemoteException e) { @@ -11106,18 +11110,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private @UserIdInt int getLogoutUserIdUnchecked() { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) { - // mLogoutUserId is USER_SYSTEM as well, but there's no need to acquire the lock - return UserHandle.USER_SYSTEM; - } synchronized (getLockObject()) { return mLogoutUserId; } } private void clearLogoutUser() { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore - synchronized (getLockObject()) { setLogoutUserIdLocked(UserHandle.USER_NULL); } @@ -11125,8 +11123,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @GuardedBy("getLockObject()") private void setLogoutUserIdLocked(@UserIdInt int userId) { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore - if (userId == UserHandle.USER_CURRENT) { userId = getCurrentForegroundUserId(); } @@ -11209,8 +11205,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } - // TODO(b/204585343): remove the headless system user check? - if (mInjector.userManagerIsHeadlessSystemUserMode() && callingUserId != mInjector + if (callingUserId != mInjector .binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId())) { Slogf.d(LOG_TAG, "logoutUser(): user %d is in background, just stopping, not switching", callingUserId); @@ -11226,8 +11221,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(canManageUsers(caller) || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); - int result = logoutUserUnchecked(getCurrentForegroundUserId()); - Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result); + int currentUserId = getCurrentForegroundUserId(); + if (VERBOSE_LOG) { + Slogf.v(LOG_TAG, "logout() called by uid %d; current user is %d", caller.getUid(), + currentUserId); + } + int result = logoutUserUnchecked(currentUserId); + if (VERBOSE_LOG) { + Slogf.v(LOG_TAG, "Result of logout(): %d", result); + } return result; } @@ -18669,4 +18671,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mContext.sendBroadcastAsUser(intent, user); } } + + public boolean isDpcDownloaded() { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + ContentResolver cr = mContext.getContentResolver(); + + return mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.getIntForUser( + cr, MANAGED_PROVISIONING_DPC_DOWNLOADED, + /* def= */ 0, /* userHandle= */ cr.getUserId()) + == 1); + } + + public void setDpcDownloaded(boolean downloaded) { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + int setTo = downloaded ? 1 : 0; + + mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.putInt( + mContext.getContentResolver(), MANAGED_PROVISIONING_DPC_DOWNLOADED, setTo)); + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f4fae2f81718..c2dec067c870 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1393,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; @@ -1898,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(); @@ -2793,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; @@ -2885,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) { 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/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index bdeb2b4fd839..f9bdad6c62ba 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -204,6 +204,49 @@ public class JobSchedulerServiceTest { jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag); } + @Test + public void testGetMinJobExecutionGuaranteeMs() { + JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(1).setExpedited(true)); + JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(3).setExpedited(true)); + JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(6)); + + spyOn(ejMax); + spyOn(ejHigh); + spyOn(ejMaxDowngraded); + spyOn(ejHighDowngraded); + spyOn(jobHigh); + spyOn(jobDef); + + when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true); + when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true); + when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); + when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); + when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false); + when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false); + + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejMax)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejHigh)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobHigh)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobDef)); + } + /** * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job * with the correct delay and deadline constraints if the periodic job is scheduled with the diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 9e1445cf589d..dad9fe8648b2 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -28,9 +28,10 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; +import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -100,7 +101,7 @@ public class AudioDeviceBrokerTest { mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( any(AudioDeviceBroker.BtDeviceInfo.class) @@ -186,8 +187,9 @@ public class AudioDeviceBrokerTest { doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup( any(Intent.class)); - mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioService.CONNECTION_STATE_CONNECTED, address, name, caller); + mSpyDevInventory.setWiredDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_WIRED_HEADSET, address, name), + AudioService.CONNECTION_STATE_CONNECTED, caller); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // Verify that the sticky intent is broadcasted @@ -208,13 +210,13 @@ public class AudioDeviceBrokerTest { // first connection: ensure the device is connected as a starting condition for the test mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource")); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice, - BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(false, -1), "testSource")); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } @@ -222,7 +224,7 @@ public class AudioDeviceBrokerTest { // reconnection mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 2), "testSource")); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts, @@ -246,11 +248,11 @@ public class AudioDeviceBrokerTest { */ private void checkSingleSystemConnection(BluetoothDevice btDevice) throws Exception { final String expectedName = btDevice.getName() == null ? "" : btDevice.getName(); + AudioDeviceAttributes expected = new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress(), expectedName); verify(mSpyAudioSystem, times(1)).setDeviceConnectionState( - ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), + ArgumentMatchers.argThat(x -> x.equalTypeAddress(expected)), ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE), - ArgumentMatchers.eq(btDevice.getAddress()), - ArgumentMatchers.eq(expectedName), anyInt() /*codec*/); } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 8d706cb960e9..1f355b096335 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -48,11 +48,10 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { //----------------------------------------------------------------- // Overrides of AudioSystemAdapter @Override - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { - Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, %s, %s, 0x%s", - Integer.toHexString(device), state, deviceAddress, deviceName, - Integer.toHexString(codecFormat))); + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s", + attributes.toString(), state, Integer.toHexString(codecFormat))); return AudioSystem.AUDIO_STATUS_OK; } 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/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index b6c4bc23f0e4..a9812ab9bb03 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -62,6 +62,23 @@ import java.util.concurrent.TimeUnit; public class HdmiCecLocalDeviceTvTest { private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; + private static final String[] SADS_NOT_TO_QUERY = new String[]{ + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO, + HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX}; + private static final HdmiCecMessage SAD_QUERY = + HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(ADDR_TV, ADDR_AUDIO_SYSTEM, + new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD, + Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2}); + private HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv; @@ -136,6 +153,10 @@ public class HdmiCecLocalDeviceTvTest { mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress); mTestLooper.dispatchAll(); mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(); + for (String sad : SADS_NOT_TO_QUERY) { + mHdmiControlService.getHdmiCecConfig().setIntValue( + sad, HdmiControlManager.QUERY_SAD_DISABLED); + } mNativeWrapper.clearResultMessages(); } @@ -442,6 +463,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY); } @Test @@ -463,6 +485,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY); } @Test @@ -485,6 +508,7 @@ public class HdmiCecLocalDeviceTvTest { ADDR_TV, ADDR_AUDIO_SYSTEM); assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated); + assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY); } @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/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 98a41bcf5adf..4a761a7a47be 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1814,6 +1814,7 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { mResponseStatsTracker.dump(idpw); } + return; } else if (arg != null && !arg.startsWith("-")) { // Anything else that doesn't start with '-' is a pkg to filter pkgs.add(arg); diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java index 85b1de5478e1..337e1f92050c 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.annotation.NonNull; +import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.media.IAudioService; import android.os.RemoteException; @@ -213,24 +214,25 @@ public final class UsbAlsaDevice { int outputState = (enable && connected) ? 1 : 0; if (outputState != mOutputState) { mOutputState = outputState; - mAudioService.setWiredDeviceConnectionState(device, outputState, - alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG); } } // Input Device if (mHasInput) { - int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET + int device = mIsInputHeadset + ? AudioSystem.DEVICE_IN_USB_HEADSET : AudioSystem.DEVICE_IN_USB_DEVICE; boolean connected = isInputJackConnected(); Slog.i(TAG, "INPUT JACK connected: " + connected); int inputState = (enable && connected) ? 1 : 0; if (inputState != mInputState) { mInputState = inputState; - mAudioService.setWiredDeviceConnectionState( - device, inputState, alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG); } } } catch (RemoteException e) { diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e560f34cfcd8..d7cdb507362a 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -356,13 +356,17 @@ public class TelecomManager { "android.telecom.extra.INCOMING_CALL_EXTRAS"; /** - * Optional extra for {@link #ACTION_INCOMING_CALL} containing a boolean to indicate that the - * call has an externally generated ringer. Used by the HfpClientConnectionService when In Band - * Ringtone is enabled to prevent two ringers from being generated. + * Optional extra for {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} used to indicate + * that a call has an in-band ringtone associated with it. This is used when the device is + * acting as an HFP headset and the Bluetooth stack has received an in-band ringtone from the + * the HFP host which must be played instead of any local ringtone the device would otherwise + * have generated. + * * @hide */ - public static final String EXTRA_CALL_EXTERNAL_RINGER = - "android.telecom.extra.CALL_EXTERNAL_RINGER"; + @SystemApi + public static final String EXTRA_CALL_HAS_IN_BAND_RINGTONE = + "android.telecom.extra.CALL_HAS_IN_BAND_RINGTONE"; /** * Optional extra for {@link android.content.Intent#ACTION_CALL} and 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/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 5a12865fb2a0..e0145e661660 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -79,7 +79,7 @@ public class SmsMessage { public static final int ENCODING_8BIT = 2; public static final int ENCODING_16BIT = 3; /** - * @hide This value is not defined in global standard. Only in Korea, this is used. + * This value is not defined in global standard. Only in Korea, this is used. */ public static final int ENCODING_KSC5601 = 4; 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/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index 1ff6ec1779cd..ec734716f6e4 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -261,9 +261,10 @@ public class DataServiceCallback { } /** - * Unthrottles the APN on the current transport. There is no matching "APN throttle" method. - * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within - * the time specified by {@link DataCallResponse#getRetryDurationMillis}. + * Unthrottles the APN on the current transport. + * The APN is throttled when {@link IDataService#setupDataCall} fails within + * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain + * throttled until this method is called. * <p/> * see: {@link DataCallResponse#getRetryDurationMillis} * @@ -284,9 +285,9 @@ public class DataServiceCallback { /** * Unthrottles the DataProfile on the current transport. - * There is no matching "DataProfile throttle" method. - * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within - * the time specified by {@link DataCallResponse#getRetryDurationMillis}. + * The DataProfile is throttled when {@link IDataService#setupDataCall} fails within + * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain + * throttled until this method is called. * <p/> * see: {@link DataCallResponse#getRetryDurationMillis} * 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/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt index 48bfd6f5d33c..62902929fcd5 100644 --- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt +++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt @@ -11,10 +11,20 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import com.google.common.truth.Truth.assertThat +import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE +import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED -import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY +import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE +import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN +import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY +import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties import java.lang.IllegalArgumentException +import java.io.ByteArrayOutputStream +import java.security.KeyPairGenerator +import java.security.KeyStore import java.time.Duration import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -23,25 +33,26 @@ import java.util.concurrent.TimeUnit @SmallTest @RunWith(AndroidJUnit4::class) class SystemAttestationVerificationTest { - @get:Rule val rule = ActivityScenarioRule(TestActivity::class.java) private lateinit var activity: Activity private lateinit var avm: AttestationVerificationManager + private lateinit var androidKeystore: KeyStore @Before fun setup() { rule.getScenario().onActivity { avm = it.getSystemService(AttestationVerificationManager::class.java) activity = it + androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } } } @Test fun verifyAttestation_returnsUnknown() { val future = CompletableFuture<Int>() - val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { result, _ -> future.complete(result) @@ -51,9 +62,82 @@ class SystemAttestationVerificationTest { } @Test - fun verifyToken_returnsUnknown() { + fun verifyAttestation_returnsFailureWithEmptyAttestation() { val future = CompletableFuture<Int>() val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0), + activity.mainExecutor) { result, _ -> + future.complete(result) + } + + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithEmptyRequirements() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongBindingType() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY, + selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongRequirements() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + val wrongKeyRequirements = Bundle() + wrongKeyRequirements.putByteArray( + "wrongBindingKey", "challengeStr".encodeToByteArray()) + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongChallenge() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + val wrongChallengeRequirements = Bundle() + wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray()) + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) { + result, _ -> future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED. + @Test + fun verifyAttestation_returnsSuccess() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS) + } + + @Test + fun verifyToken_returnsUnknown() { + val future = CompletableFuture<Int>() + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { _, token -> val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null) @@ -66,7 +150,7 @@ class SystemAttestationVerificationTest { @Test fun verifyToken_tooBigMaxAgeThrows() { val future = CompletableFuture<VerificationToken>() - val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { _, token -> future.complete(token) @@ -87,4 +171,52 @@ class SystemAttestationVerificationTest { super.onCreate(savedInstanceState) } } + + inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) { + val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val localBindingType = TYPE_CHALLENGE + val requirements: Bundle + val attestation: ByteArray + + init { + val challengeByteArray = challenge.encodeToByteArray() + generateAndStoreKey(alias, challengeByteArray) + attestation = generateCertificatesByteArray(alias) + requirements = Bundle() + requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray) + } + + private fun generateAndStoreKey(alias: String, challenge: ByteArray) { + val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_EC, + ANDROID_KEYSTORE + ) + val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( + alias, + KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY + ).run { + // a challenge results in a generated attestation + setAttestationChallenge(challenge) + setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + build() + } + kpg.initialize(parameterSpec) + kpg.generateKeyPair() + } + + private fun generateCertificatesByteArray(alias: String): ByteArray { + val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry + val certs = pkEntry.certificateChain + val bos = ByteArrayOutputStream() + certs.forEach { + bos.write(it.encoded) + } + return bos.toByteArray() + } + } + + companion object { + private const val TAG = "AVFTEST" + private const val ANDROID_KEYSTORE = "AndroidKeyStore" + } } 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) } diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 41f73cd9c706..228520e8545b 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -18,6 +18,7 @@ android_test { "java/**/*.kt", ], platform_apis: true, + defaults: ["framework-connectivity-test-defaults"], test_suites: ["device-tests"], certificate: "platform", static_libs: [ @@ -28,6 +29,7 @@ android_test { "net-tests-utils", "platform-test-annotations", "services.core", + "service-connectivity-tiramisu-pre-jarjar", ], libs: [ "android.test.runner", diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml index 2ad9aac67029..a8f657c89f76 100644 --- a/tests/vcn/AndroidManifest.xml +++ b/tests/vcn/AndroidManifest.xml @@ -16,7 +16,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.tests.vcn"> - + <uses-sdk android:minSdkVersion="33" + android:targetSdkVersion="33"/> <application> <uses-library android:name="android.test.runner" /> </application> |