diff options
439 files changed, 12621 insertions, 5353 deletions
diff --git a/Android.bp b/Android.bp index 25238e8c0881..7d02d7d50f84 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: [ @@ -418,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", @@ -447,7 +423,6 @@ filegroup { // TODO: remove these annotations as soon as we can use andoid.support.annotations.* ":framework-annotations", ":modules-utils-preconditions-srcs", - "core/java/android/net/DhcpResults.java", "core/java/android/util/IndentingPrintWriter.java", "core/java/android/util/LocalLog.java", "core/java/com/android/internal/util/HexDump.java", @@ -607,6 +582,7 @@ stubs_defaults { libs: [ "art.module.public.api", "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-tiramisu", "sdk_public_current_framework-bluetooth", // There are a few classes from modules used by the core that // need to be resolved by metalava. We use a prebuilt stub of the diff --git a/StubLibraries.bp b/StubLibraries.bp index a0a426e9b766..92e7dc98e244 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -258,6 +258,7 @@ java_library { srcs: [":module-lib-api-stubs-docs-non-updatable"], libs: [ "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-tiramisu", "sdk_public_current_framework-bluetooth", // NOTE: The below can be removed once the prebuilt stub contains bluetooth. "sdk_system_current_android", 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 50e0a1b45c9d..e346ebf827ae 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -9315,78 +9315,6 @@ Landroid/app/usage/IUsageStatsManager;->setAppStandbyBucket(Ljava/lang/String;II Landroid/app/usage/IUsageStatsManager;->setAppStandbyBuckets(Landroid/content/pm/ParceledListSlice;I)V Landroid/app/usage/IUsageStatsManager;->unregisterAppUsageObserver(ILjava/lang/String;)V Landroid/app/usage/IUsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JI)V -Landroid/app/usage/NetworkStats$Bucket;->convertDefaultNetworkStatus(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertMetered(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertRoaming(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertSet(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertState(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertTag(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertUid(I)I -Landroid/app/usage/NetworkStats$Bucket;->mBeginTimeStamp:J -Landroid/app/usage/NetworkStats$Bucket;->mDefaultNetworkStatus:I -Landroid/app/usage/NetworkStats$Bucket;->mEndTimeStamp:J -Landroid/app/usage/NetworkStats$Bucket;->mMetered:I -Landroid/app/usage/NetworkStats$Bucket;->mRoaming:I -Landroid/app/usage/NetworkStats$Bucket;->mRxBytes:J -Landroid/app/usage/NetworkStats$Bucket;->mRxPackets:J -Landroid/app/usage/NetworkStats$Bucket;->mState:I -Landroid/app/usage/NetworkStats$Bucket;->mTag:I -Landroid/app/usage/NetworkStats$Bucket;->mTxBytes:J -Landroid/app/usage/NetworkStats$Bucket;->mTxPackets:J -Landroid/app/usage/NetworkStats$Bucket;->mUid:I -Landroid/app/usage/NetworkStats;-><init>(Landroid/content/Context;Landroid/net/NetworkTemplate;IJJLandroid/net/INetworkStatsService;)V -Landroid/app/usage/NetworkStats;->fillBucketFromSummaryEntry(Landroid/app/usage/NetworkStats$Bucket;)V -Landroid/app/usage/NetworkStats;->getDeviceSummaryForNetwork()Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStats;->getNextHistoryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z -Landroid/app/usage/NetworkStats;->getNextSummaryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z -Landroid/app/usage/NetworkStats;->getSummaryAggregate()Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStats;->getUid()I -Landroid/app/usage/NetworkStats;->hasNextUid()Z -Landroid/app/usage/NetworkStats;->isUidEnumeration()Z -Landroid/app/usage/NetworkStats;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/app/usage/NetworkStats;->mEndTimeStamp:J -Landroid/app/usage/NetworkStats;->mEnumerationIndex:I -Landroid/app/usage/NetworkStats;->mHistory:Landroid/net/NetworkStatsHistory; -Landroid/app/usage/NetworkStats;->mRecycledHistoryEntry:Landroid/net/NetworkStatsHistory$Entry; -Landroid/app/usage/NetworkStats;->mRecycledSummaryEntry:Landroid/net/NetworkStats$Entry; -Landroid/app/usage/NetworkStats;->mSession:Landroid/net/INetworkStatsSession; -Landroid/app/usage/NetworkStats;->mStartTimeStamp:J -Landroid/app/usage/NetworkStats;->mState:I -Landroid/app/usage/NetworkStats;->mSummary:Landroid/net/NetworkStats; -Landroid/app/usage/NetworkStats;->mTag:I -Landroid/app/usage/NetworkStats;->mTemplate:Landroid/net/NetworkTemplate; -Landroid/app/usage/NetworkStats;->mUidOrUidIndex:I -Landroid/app/usage/NetworkStats;->mUids:[I -Landroid/app/usage/NetworkStats;->setSingleUidTagState(III)V -Landroid/app/usage/NetworkStats;->startHistoryEnumeration(III)V -Landroid/app/usage/NetworkStats;->startSummaryEnumeration()V -Landroid/app/usage/NetworkStats;->startUserUidEnumeration()V -Landroid/app/usage/NetworkStats;->stepHistory()V -Landroid/app/usage/NetworkStats;->stepUid()V -Landroid/app/usage/NetworkStats;->TAG:Ljava/lang/String; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;-><init>(Landroid/os/Looper;ILjava/lang/String;Landroid/app/usage/NetworkStatsManager$UsageCallback;)V -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->getObject(Landroid/os/Message;Ljava/lang/String;)Ljava/lang/Object; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mCallback:Landroid/app/usage/NetworkStatsManager$UsageCallback; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mNetworkType:I -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mSubscriberId:Ljava/lang/String; -Landroid/app/usage/NetworkStatsManager$UsageCallback;->request:Landroid/net/DataUsageRequest; -Landroid/app/usage/NetworkStatsManager;-><init>(Landroid/content/Context;Landroid/net/INetworkStatsService;)V -Landroid/app/usage/NetworkStatsManager;->CALLBACK_LIMIT_REACHED:I -Landroid/app/usage/NetworkStatsManager;->CALLBACK_RELEASED:I -Landroid/app/usage/NetworkStatsManager;->createTemplate(ILjava/lang/String;)Landroid/net/NetworkTemplate; -Landroid/app/usage/NetworkStatsManager;->DBG:Z -Landroid/app/usage/NetworkStatsManager;->FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN:I -Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_FORCE:I -Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_ON_OPEN:I -Landroid/app/usage/NetworkStatsManager;->mContext:Landroid/content/Context; -Landroid/app/usage/NetworkStatsManager;->mFlags:I -Landroid/app/usage/NetworkStatsManager;->MIN_THRESHOLD_BYTES:J -Landroid/app/usage/NetworkStatsManager;->mService:Landroid/net/INetworkStatsService; -Landroid/app/usage/NetworkStatsManager;->querySummaryForDevice(Landroid/net/NetworkTemplate;JJ)Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStatsManager;->registerUsageCallback(Landroid/net/NetworkTemplate;IJLandroid/app/usage/NetworkStatsManager$UsageCallback;Landroid/os/Handler;)V -Landroid/app/usage/NetworkStatsManager;->setAugmentWithSubscriptionPlan(Z)V -Landroid/app/usage/NetworkStatsManager;->setPollOnOpen(Z)V -Landroid/app/usage/NetworkStatsManager;->TAG:Ljava/lang/String; Landroid/app/usage/StorageStats;-><init>()V Landroid/app/usage/StorageStats;-><init>(Landroid/os/Parcel;)V Landroid/app/usage/StorageStats;->cacheBytes:J @@ -35338,13 +35266,6 @@ Landroid/net/ConnectivityMetricsEvent;->transports:J Landroid/net/Credentials;->gid:I Landroid/net/Credentials;->pid:I Landroid/net/Credentials;->uid:I -Landroid/net/DataUsageRequest;-><init>(ILandroid/net/NetworkTemplate;J)V -Landroid/net/DataUsageRequest;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/DataUsageRequest;->PARCELABLE_KEY:Ljava/lang/String; -Landroid/net/DataUsageRequest;->requestId:I -Landroid/net/DataUsageRequest;->REQUEST_ID_UNSET:I -Landroid/net/DataUsageRequest;->template:Landroid/net/NetworkTemplate; -Landroid/net/DataUsageRequest;->thresholdInBytes:J Landroid/net/DhcpResults;->addDns(Ljava/lang/String;)Z Landroid/net/DhcpResults;->clear()V Landroid/net/DhcpResults;->CREATOR:Landroid/os/Parcelable$Creator; @@ -35793,68 +35714,6 @@ Landroid/net/INetworkScoreService;->requestScores([Landroid/net/NetworkKey;)Z Landroid/net/INetworkScoreService;->setActiveScorer(Ljava/lang/String;)Z Landroid/net/INetworkScoreService;->unregisterNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V Landroid/net/INetworkScoreService;->updateScores([Landroid/net/ScoredNetwork;)Z -Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdate()V -Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdateIfaces([Landroid/net/Network;)V -Landroid/net/INetworkStatsService$Stub$Proxy;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService$Stub$Proxy;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService$Stub$Proxy;->getIfaceStats(Ljava/lang/String;I)J -Landroid/net/INetworkStatsService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub$Proxy;->getTotalStats(I)J -Landroid/net/INetworkStatsService$Stub$Proxy;->getUidStats(II)J -Landroid/net/INetworkStatsService$Stub$Proxy;->incrementOperationCount(III)V -Landroid/net/INetworkStatsService$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/INetworkStatsService$Stub$Proxy;->openSession()Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsService$Stub$Proxy;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsService$Stub$Proxy;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest; -Landroid/net/INetworkStatsService$Stub$Proxy;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V -Landroid/net/INetworkStatsService$Stub;-><init>()V -Landroid/net/INetworkStatsService$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdate:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdateIfaces:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDataLayerSnapshotForUid:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDetailedUidStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getIfaceStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getMobileIfaces:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getTotalStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getUidStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_incrementOperationCount:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSession:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSessionForUsageStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_registerUsageCallback:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_unregisterUsageRequest:I -Landroid/net/INetworkStatsService;->forceUpdateIfaces([Landroid/net/Network;)V -Landroid/net/INetworkStatsService;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService;->getIfaceStats(Ljava/lang/String;I)J -Landroid/net/INetworkStatsService;->getTotalStats(I)J -Landroid/net/INetworkStatsService;->getUidStats(II)J -Landroid/net/INetworkStatsService;->incrementOperationCount(III)V -Landroid/net/INetworkStatsService;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest; -Landroid/net/INetworkStatsService;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V -Landroid/net/INetworkStatsSession$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/INetworkStatsSession$Stub$Proxy;->close()V -Landroid/net/INetworkStatsSession$Stub$Proxy;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getRelevantUids()[I -Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/INetworkStatsSession$Stub;-><init>()V -Landroid/net/INetworkStatsSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsSession$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_close:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getDeviceSummaryForNetwork:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForNetwork:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryIntervalForUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getRelevantUids:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForAllUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForNetwork:I -Landroid/net/INetworkStatsSession;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession;->getRelevantUids()[I Landroid/net/InterfaceConfiguration;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/InterfaceConfiguration;->FLAG_DOWN:Ljava/lang/String; Landroid/net/InterfaceConfiguration;->FLAG_UP:Ljava/lang/String; @@ -36529,41 +36388,6 @@ Landroid/net/SSLSessionCache;->TAG:Ljava/lang/String; Landroid/net/StringNetworkSpecifier;-><init>(Ljava/lang/String;)V Landroid/net/StringNetworkSpecifier;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/StringNetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z -Landroid/net/TrafficStats;->addIfSupported(J)J -Landroid/net/TrafficStats;->closeQuietly(Landroid/net/INetworkStatsSession;)V -Landroid/net/TrafficStats;->GB_IN_BYTES:J -Landroid/net/TrafficStats;->getDataLayerSnapshotForUid(Landroid/content/Context;)Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->getRxPackets(Ljava/lang/String;)J -Landroid/net/TrafficStats;->getTxPackets(Ljava/lang/String;)J -Landroid/net/TrafficStats;->KB_IN_BYTES:J -Landroid/net/TrafficStats;->LOOPBACK_IFACE:Ljava/lang/String; -Landroid/net/TrafficStats;->MB_IN_BYTES:J -Landroid/net/TrafficStats;->PB_IN_BYTES:J -Landroid/net/TrafficStats;->sActiveProfilingStart:Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->sProfilingLock:Ljava/lang/Object; -Landroid/net/TrafficStats;->sStatsService:Landroid/net/INetworkStatsService; -Landroid/net/TrafficStats;->startDataProfiling(Landroid/content/Context;)V -Landroid/net/TrafficStats;->stopDataProfiling(Landroid/content/Context;)Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->TAG_SYSTEM_APP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_BACKUP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_DHCP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_DOWNLOAD:I -Landroid/net/TrafficStats;->TAG_SYSTEM_GPS:I -Landroid/net/TrafficStats;->TAG_SYSTEM_MEDIA:I -Landroid/net/TrafficStats;->TAG_SYSTEM_NEIGHBOR:I -Landroid/net/TrafficStats;->TAG_SYSTEM_NTP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_PAC:I -Landroid/net/TrafficStats;->TAG_SYSTEM_PROBE:I -Landroid/net/TrafficStats;->TAG_SYSTEM_RESTORE:I -Landroid/net/TrafficStats;->TB_IN_BYTES:J -Landroid/net/TrafficStats;->TYPE_RX_BYTES:I -Landroid/net/TrafficStats;->TYPE_RX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TCP_RX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TCP_TX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TX_BYTES:I -Landroid/net/TrafficStats;->TYPE_TX_PACKETS:I -Landroid/net/TrafficStats;->UID_REMOVED:I -Landroid/net/TrafficStats;->UID_TETHERING:I Landroid/net/Uri$AbstractHierarchicalUri;-><init>()V Landroid/net/Uri$AbstractHierarchicalUri;->getUserInfoPart()Landroid/net/Uri$Part; Landroid/net/Uri$AbstractHierarchicalUri;->host:Ljava/lang/String; diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt index 522e88f82e25..033afb676381 100644 --- a/boot/hiddenapi/hiddenapi-unsupported.txt +++ b/boot/hiddenapi/hiddenapi-unsupported.txt @@ -168,9 +168,6 @@ Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService; -Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService; Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/os/IDeviceIdentifiersPolicyService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdentifiersPolicyService; Landroid/os/IDeviceIdleController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdleController; diff --git a/core/api/current.txt b/core/api/current.txt index 8ca3551eace4..438d872d6a72 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -135,6 +135,9 @@ package android { field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA"; field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE"; field public static final String READ_LOGS = "android.permission.READ_LOGS"; + field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO"; + field public static final String READ_MEDIA_IMAGE = "android.permission.READ_MEDIA_IMAGE"; + field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO"; field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY"; field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; @@ -220,6 +223,8 @@ package android { field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES"; field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS"; field public static final String PHONE = "android.permission-group.PHONE"; + field public static final String READ_MEDIA_AURAL = "android.permission-group.READ_MEDIA_AURAL"; + field public static final String READ_MEDIA_VISUAL = "android.permission-group.READ_MEDIA_VISUAL"; field public static final String SENSORS = "android.permission-group.SENSORS"; field public static final String SMS = "android.permission-group.SMS"; field public static final String STORAGE = "android.permission-group.STORAGE"; @@ -3074,6 +3079,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); @@ -3591,17 +3597,17 @@ package android.animation { } public static interface Animator.AnimatorListener { - method public void onAnimationCancel(android.animation.Animator); - method public default void onAnimationEnd(android.animation.Animator, boolean); - method public void onAnimationEnd(android.animation.Animator); - method public void onAnimationRepeat(android.animation.Animator); - method public default void onAnimationStart(android.animation.Animator, boolean); - method public void onAnimationStart(android.animation.Animator); + method public void onAnimationCancel(@NonNull android.animation.Animator); + method public default void onAnimationEnd(@NonNull android.animation.Animator, boolean); + method public void onAnimationEnd(@NonNull android.animation.Animator); + method public void onAnimationRepeat(@NonNull android.animation.Animator); + method public default void onAnimationStart(@NonNull android.animation.Animator, boolean); + method public void onAnimationStart(@NonNull android.animation.Animator); } public static interface Animator.AnimatorPauseListener { - method public void onAnimationPause(android.animation.Animator); - method public void onAnimationResume(android.animation.Animator); + method public void onAnimationPause(@NonNull android.animation.Animator); + method public void onAnimationResume(@NonNull android.animation.Animator); } public class AnimatorInflater { @@ -3888,7 +3894,7 @@ package android.animation { } public static interface ValueAnimator.AnimatorUpdateListener { - method public void onAnimationUpdate(android.animation.ValueAnimator); + method public void onAnimationUpdate(@NonNull android.animation.ValueAnimator); } } @@ -4069,7 +4075,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 +4881,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 +4977,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 +6956,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); @@ -7326,6 +7334,8 @@ package android.app.admin { method public CharSequence getDeviceOwnerLockScreenInfo(); method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon); + method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon); method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); @@ -7930,11 +7940,10 @@ package android.app.admin { } public final class WifiSsidPolicy implements android.os.Parcelable { - method @NonNull public static android.app.admin.WifiSsidPolicy createAllowlistPolicy(@NonNull java.util.Set<java.lang.String>); - method @NonNull public static android.app.admin.WifiSsidPolicy createDenylistPolicy(@NonNull java.util.Set<java.lang.String>); + ctor public WifiSsidPolicy(int, @NonNull java.util.Set<android.net.wifi.WifiSsid>); method public int describeContents(); method public int getPolicyType(); - method @NonNull public java.util.Set<java.lang.String> getSsids(); + method @NonNull public java.util.Set<android.net.wifi.WifiSsid> getSsids(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.WifiSsidPolicy> CREATOR; field public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0; // 0x0 @@ -8591,62 +8600,6 @@ package android.app.usage { field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.ExternalStorageStats> CREATOR; } - public final class NetworkStats implements java.lang.AutoCloseable { - method public void close(); - method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket); - method public boolean hasNextBucket(); - } - - public static class NetworkStats.Bucket { - ctor public NetworkStats.Bucket(); - method public int getDefaultNetworkStatus(); - method public long getEndTimeStamp(); - method public int getMetered(); - method public int getRoaming(); - method public long getRxBytes(); - method public long getRxPackets(); - method public long getStartTimeStamp(); - method public int getState(); - method public int getTag(); - method public long getTxBytes(); - method public long getTxPackets(); - method public int getUid(); - field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff - field public static final int DEFAULT_NETWORK_NO = 1; // 0x1 - field public static final int DEFAULT_NETWORK_YES = 2; // 0x2 - field public static final int METERED_ALL = -1; // 0xffffffff - field public static final int METERED_NO = 1; // 0x1 - field public static final int METERED_YES = 2; // 0x2 - field public static final int ROAMING_ALL = -1; // 0xffffffff - field public static final int ROAMING_NO = 1; // 0x1 - field public static final int ROAMING_YES = 2; // 0x2 - field public static final int STATE_ALL = -1; // 0xffffffff - field public static final int STATE_DEFAULT = 1; // 0x1 - field public static final int STATE_FOREGROUND = 2; // 0x2 - field public static final int TAG_NONE = 0; // 0x0 - field public static final int UID_ALL = -1; // 0xffffffff - field public static final int UID_REMOVED = -4; // 0xfffffffc - field public static final int UID_TETHERING = -5; // 0xfffffffb - } - - public class NetworkStatsManager { - method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback); - method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler); - method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback); - } - - public abstract static class NetworkStatsManager.UsageCallback { - ctor public NetworkStatsManager.UsageCallback(); - method public abstract void onThresholdReached(int, String); - } - public final class StorageStats implements android.os.Parcelable { method public int describeContents(); method public long getAppBytes(); @@ -11928,7 +11881,7 @@ package android.content.pm { field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims"; field public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms"; field public static final String FEATURE_TELEPHONY_MESSAGING = "android.hardware.telephony.messaging"; - field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio"; + field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access"; field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription"; field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television"; field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen"; @@ -15466,8 +15419,6 @@ package android.graphics { public class RuntimeShader extends android.graphics.Shader { ctor public RuntimeShader(@NonNull String); - ctor public RuntimeShader(@NonNull String, boolean); - method public boolean isForceOpaque(); method public void setColorUniform(@NonNull String, @ColorInt int); method public void setColorUniform(@NonNull String, @ColorLong long); method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color); @@ -15765,9 +15716,9 @@ package android.graphics.drawable { method public final void copyBounds(@NonNull android.graphics.Rect); method @NonNull public final android.graphics.Rect copyBounds(); method @Nullable public static android.graphics.drawable.Drawable createFromPath(String); - method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, String); + method @Nullable public static android.graphics.drawable.Drawable createFromResourceStream(@Nullable android.content.res.Resources, @Nullable android.util.TypedValue, @Nullable java.io.InputStream, @Nullable String); method @Deprecated @Nullable public static android.graphics.drawable.Drawable createFromResourceStream(@Nullable android.content.res.Resources, @Nullable android.util.TypedValue, @Nullable java.io.InputStream, @Nullable String, @Nullable android.graphics.BitmapFactory.Options); - method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, String); + method @Nullable public static android.graphics.drawable.Drawable createFromStream(@Nullable java.io.InputStream, @Nullable String); method @NonNull public static android.graphics.drawable.Drawable createFromXml(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method @NonNull public static android.graphics.drawable.Drawable createFromXml(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser, @Nullable android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method @NonNull public static android.graphics.drawable.Drawable createFromXmlInner(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser, @NonNull android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; @@ -15805,10 +15756,10 @@ package android.graphics.drawable { method public final boolean isVisible(); method public void jumpToCurrentState(); method @NonNull public android.graphics.drawable.Drawable mutate(); - method protected void onBoundsChange(android.graphics.Rect); + method protected void onBoundsChange(@NonNull android.graphics.Rect); method public boolean onLayoutDirectionChanged(int); method protected boolean onLevelChange(int); - method protected boolean onStateChange(int[]); + method protected boolean onStateChange(@NonNull int[]); method public static int resolveOpacity(int, int); method public void scheduleSelf(@NonNull Runnable, long); method public abstract void setAlpha(@IntRange(from=0, to=255) int); @@ -15969,27 +15920,27 @@ package android.graphics.drawable { } public final class Icon implements android.os.Parcelable { - method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap); + method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap); method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull String); method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull android.net.Uri); - method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap); - method public static android.graphics.drawable.Icon createWithContentUri(String); - method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri); - method public static android.graphics.drawable.Icon createWithData(byte[], int, int); - method public static android.graphics.drawable.Icon createWithFilePath(String); - method public static android.graphics.drawable.Icon createWithResource(android.content.Context, @DrawableRes int); - method public static android.graphics.drawable.Icon createWithResource(String, @DrawableRes int); + method @NonNull public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap); + method @NonNull public static android.graphics.drawable.Icon createWithContentUri(String); + method @NonNull public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri); + method @NonNull public static android.graphics.drawable.Icon createWithData(byte[], int, int); + method @NonNull public static android.graphics.drawable.Icon createWithFilePath(String); + method @NonNull public static android.graphics.drawable.Icon createWithResource(android.content.Context, @DrawableRes int); + method @NonNull public static android.graphics.drawable.Icon createWithResource(String, @DrawableRes int); method public int describeContents(); method @DrawableRes public int getResId(); method @NonNull public String getResPackage(); method public int getType(); method @NonNull public android.net.Uri getUri(); - method public android.graphics.drawable.Drawable loadDrawable(android.content.Context); - method public void loadDrawableAsync(android.content.Context, android.os.Message); - method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler); - method public android.graphics.drawable.Icon setTint(@ColorInt int); + method @Nullable public android.graphics.drawable.Drawable loadDrawable(android.content.Context); + method public void loadDrawableAsync(@NonNull android.content.Context, @NonNull android.os.Message); + method public void loadDrawableAsync(@NonNull android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler); + method @NonNull public android.graphics.drawable.Icon setTint(@ColorInt int); method @NonNull public android.graphics.drawable.Icon setTintBlendMode(@NonNull android.graphics.BlendMode); - method public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList); + method @NonNull public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList); method @NonNull public android.graphics.drawable.Icon setTintMode(@NonNull android.graphics.PorterDuff.Mode); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR; @@ -16867,13 +16818,18 @@ package android.hardware { field public static final int REPORTING_MODE_ON_CHANGE = 1; // 0x1 field public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3; // 0x3 field public static final String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer"; + field public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES = "android.sensor.accelerometer_limited_axes"; + field public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = "android.sensor.accelerometer_limited_axes_uncalibrated"; field public static final String STRING_TYPE_ACCELEROMETER_UNCALIBRATED = "android.sensor.accelerometer_uncalibrated"; field public static final String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature"; field public static final String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector"; field public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.geomagnetic_rotation_vector"; field public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity"; field public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope"; + field public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES = "android.sensor.gyroscope_limited_axes"; + field public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = "android.sensor.gyroscope_limited_axes_uncalibrated"; field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated"; + field public static final String STRING_TYPE_HEADING = "android.sensor.heading"; field public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker"; field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat"; field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; @@ -16896,6 +16852,8 @@ package android.hardware { field public static final String STRING_TYPE_STEP_DETECTOR = "android.sensor.step_detector"; field @Deprecated public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature"; field public static final int TYPE_ACCELEROMETER = 1; // 0x1 + field public static final int TYPE_ACCELEROMETER_LIMITED_AXES = 38; // 0x26 + field public static final int TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40; // 0x28 field public static final int TYPE_ACCELEROMETER_UNCALIBRATED = 35; // 0x23 field public static final int TYPE_ALL = -1; // 0xffffffff field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd @@ -16904,7 +16862,10 @@ package android.hardware { field public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; // 0x14 field public static final int TYPE_GRAVITY = 9; // 0x9 field public static final int TYPE_GYROSCOPE = 4; // 0x4 + field public static final int TYPE_GYROSCOPE_LIMITED_AXES = 39; // 0x27 + field public static final int TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41; // 0x29 field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10 + field public static final int TYPE_HEADING = 42; // 0x2a field public static final int TYPE_HEAD_TRACKER = 37; // 0x25 field public static final int TYPE_HEART_BEAT = 31; // 0x1f field public static final int TYPE_HEART_RATE = 21; // 0x15 @@ -17334,12 +17295,14 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION; @@ -17433,6 +17396,8 @@ package android.hardware.camera2 { } public final class CameraExtensionCharacteristics { + method @NonNull public java.util.Set<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(int); + method @NonNull public java.util.Set<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(int); method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int); method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>); method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int); @@ -17457,6 +17422,7 @@ package android.hardware.camera2 { ctor public CameraExtensionSession.ExtensionCaptureCallback(); method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest); method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest); + method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult); method public void onCaptureSequenceAborted(@NonNull android.hardware.camera2.CameraExtensionSession, int); method public void onCaptureSequenceCompleted(@NonNull android.hardware.camera2.CameraExtensionSession, int); method public void onCaptureStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, long); @@ -17668,9 +17634,16 @@ package android.hardware.camera2 { field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5 field public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17; // 0x11 field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd + field public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19; // 0x13 field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe field public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; // 0x10 field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0; // 0x0 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 1; // 0x1 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 4; // 0x4 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 2; // 0x2 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 5; // 0x5 + field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 3; // 0x3 field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0 field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1 field public static final int SCALER_ROTATE_AND_CROP_180 = 2; // 0x2 @@ -18078,6 +18051,7 @@ package android.hardware.camera2.params { method public int get10BitFormat(); method @NonNull public java.util.List<android.util.Size> getAvailableSizes(); method public int getFormat(); + method public int getStreamUseCase(); method public boolean is10BitCapable(); method public boolean isInput(); method public boolean isMaximumSize(); @@ -18134,6 +18108,7 @@ package android.hardware.camera2.params { method public void enableSurfaceSharing(); method public int getDynamicRangeProfile(); method public int getMaxSharedSurfaceCount(); + method public int getStreamUseCase(); method @Nullable public android.view.Surface getSurface(); method public int getSurfaceGroupId(); method @NonNull public java.util.List<android.view.Surface> getSurfaces(); @@ -18141,6 +18116,7 @@ package android.hardware.camera2.params { method public void removeSurface(@NonNull android.view.Surface); method public void setDynamicRangeProfile(int); method public void setPhysicalCameraId(@Nullable String); + method public void setStreamUseCase(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR; field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff @@ -19676,10 +19652,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 } @@ -20195,14 +20174,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 { @@ -26517,50 +26499,6 @@ package android.net { method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int); } - public class TrafficStats { - ctor public TrafficStats(); - method public static void clearThreadStatsTag(); - method public static void clearThreadStatsUid(); - method public static int getAndSetThreadStatsTag(int); - method public static long getMobileRxBytes(); - method public static long getMobileRxPackets(); - method public static long getMobileTxBytes(); - method public static long getMobileTxPackets(); - method public static long getRxBytes(@NonNull String); - method public static long getRxPackets(@NonNull String); - method public static int getThreadStatsTag(); - method public static int getThreadStatsUid(); - method public static long getTotalRxBytes(); - method public static long getTotalRxPackets(); - method public static long getTotalTxBytes(); - method public static long getTotalTxPackets(); - method public static long getTxBytes(@NonNull String); - method public static long getTxPackets(@NonNull String); - method public static long getUidRxBytes(int); - method public static long getUidRxPackets(int); - method @Deprecated public static long getUidTcpRxBytes(int); - method @Deprecated public static long getUidTcpRxSegments(int); - method @Deprecated public static long getUidTcpTxBytes(int); - method @Deprecated public static long getUidTcpTxSegments(int); - method public static long getUidTxBytes(int); - method public static long getUidTxPackets(int); - method @Deprecated public static long getUidUdpRxBytes(int); - method @Deprecated public static long getUidUdpRxPackets(int); - method @Deprecated public static long getUidUdpTxBytes(int); - method @Deprecated public static long getUidUdpTxPackets(int); - method public static void incrementOperationCount(int); - method public static void incrementOperationCount(int, int); - method public static void setThreadStatsTag(int); - method public static void setThreadStatsUid(int); - method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; - method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; - method public static void tagSocket(java.net.Socket) throws java.net.SocketException; - method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; - method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; - method public static void untagSocket(java.net.Socket) throws java.net.SocketException; - field public static final int UNSUPPORTED = -1; // 0xffffffff - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method public abstract android.net.Uri.Builder buildUpon(); method public int compareTo(android.net.Uri); @@ -39367,6 +39305,7 @@ package android.speech { public interface RecognitionListener { method public void onBeginningOfSpeech(); method public void onBufferReceived(byte[]); + method public default void onEndOfSegmentedSession(); method public void onEndOfSpeech(); method public void onError(int); method public void onEvent(int, android.os.Bundle); @@ -39374,6 +39313,7 @@ package android.speech { method public void onReadyForSpeech(android.os.Bundle); method public void onResults(android.os.Bundle); method public void onRmsChanged(float); + method public default void onSegmentResults(@NonNull android.os.Bundle); } public abstract class RecognitionService extends android.app.Service { @@ -39391,6 +39331,7 @@ package android.speech { public class RecognitionService.Callback { method public void beginningOfSpeech() throws android.os.RemoteException; method public void bufferReceived(byte[]) throws android.os.RemoteException; + method public void endOfSegmentedSession() throws android.os.RemoteException; method public void endOfSpeech() throws android.os.RemoteException; method public void error(int) throws android.os.RemoteException; method @NonNull public android.content.AttributionSource getCallingAttributionSource(); @@ -39399,6 +39340,7 @@ package android.speech { method public void readyForSpeech(android.os.Bundle) throws android.os.RemoteException; method public void results(android.os.Bundle) throws android.os.RemoteException; method public void rmsChanged(float) throws android.os.RemoteException; + method public void segmentResults(@NonNull android.os.Bundle) throws android.os.RemoteException; } public static class RecognitionService.SupportCallback { @@ -39454,6 +39396,7 @@ package android.speech { field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT"; field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE"; field public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE"; + field public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION"; field public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS"; field public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS"; field public static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS"; @@ -42940,6 +42883,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"; @@ -43034,7 +42978,8 @@ package android.telephony { method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long); - method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>); + method @Deprecated public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>); + method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>, long); method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent); field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"; field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED"; @@ -43217,6 +43162,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot(); method public int getActiveModemCount(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public long getAllowedNetworkTypesForReason(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus(); method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public int getCallState(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getCallStateForSubscription(); @@ -43277,6 +43223,7 @@ package android.telephony { method public int getSubscriptionId(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getSubscriptionId(@NonNull android.telecom.PhoneAccountHandle); method public int getSupportedModemCount(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public long getSupportedRadioAccessFamily(); method @Nullable public String getTypeAllocationCode(); method @Nullable public String getTypeAllocationCode(int); method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo(); @@ -43322,6 +43269,7 @@ package android.telephony { method public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAllowedNetworkTypesForReason(int, long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean); @@ -43358,6 +43306,8 @@ package android.telephony { field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; field public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED"; field public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED"; + field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2 + field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0 field public static final int APPTYPE_CSIM = 4; // 0x4 field public static final int APPTYPE_ISIM = 5; // 0x5 field public static final int APPTYPE_RUIM = 3; // 0x3 @@ -43432,6 +43382,26 @@ package android.telephony { field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2 field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0 field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7 + field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L + field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L + field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L + field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L + field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L + field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L + field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L + field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L + field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L + field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L + field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L + field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L + field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L + field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L + field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L + field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L + field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L + field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L + field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L + field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L field public static final int NETWORK_TYPE_CDMA = 4; // 0x4 field public static final int NETWORK_TYPE_EDGE = 2; // 0x2 field public static final int NETWORK_TYPE_EHRPD = 14; // 0xe @@ -48894,16 +48864,15 @@ package android.view { method public default void onBackInvoked(); } - public abstract class OnBackInvokedDispatcher { - ctor public OnBackInvokedDispatcher(); - method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int); - method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback); + public interface OnBackInvokedDispatcher { + method public void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, @IntRange(from=0) int); + method public void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback); field public static final int PRIORITY_DEFAULT = 0; // 0x0 field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240 } public interface OnBackInvokedDispatcherOwner { - method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); } public interface OnReceiveContentListener { @@ -49331,7 +49300,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); @@ -49535,7 +49504,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(); @@ -51428,6 +51396,7 @@ package android.view.accessibility { method public CharSequence getPackageName(); method public android.view.accessibility.AccessibilityRecord getRecord(int); method public int getRecordCount(); + method public int getSpeechStateChangeTypes(); method public int getWindowChanges(); method public void initFromParcel(android.os.Parcel); method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int); @@ -51439,6 +51408,7 @@ package android.view.accessibility { method public void setEventType(int); method public void setMovementGranularity(int); method public void setPackageName(CharSequence); + method public void setSpeechStateChangeTypes(int); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4 field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200 @@ -51454,12 +51424,17 @@ package android.view.accessibility { field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityEvent> CREATOR; field public static final int INVALID_POSITION = -1; // 0xffffffff field @Deprecated public static final int MAX_TEXT_LENGTH = 500; // 0x1f4 + field public static final int SPEECH_STATE_LISTENING_END = 8; // 0x8 + field public static final int SPEECH_STATE_LISTENING_START = 4; // 0x4 + field public static final int SPEECH_STATE_SPEAKING_END = 2; // 0x2 + field public static final int SPEECH_STATE_SPEAKING_START = 1; // 0x1 field public static final int TYPES_ALL_MASK = -1; // 0xffffffff field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000 field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000 field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000 field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000 field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40 + field public static final int TYPE_SPEECH_STATE_CHANGE = 33554432; // 0x2000000 field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400 field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200 field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 3c1e1b715366..36d54f59ed3f 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -86,31 +86,6 @@ package android.app.admin { } -package android.app.usage { - - public class NetworkStatsManager { - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate(); - method public static int getCollapsedRatType(int); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException; - method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; - method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; - method public void registerUsageCallback(@NonNull android.net.NetworkTemplate, long, @NonNull java.util.concurrent.Executor, @NonNull android.app.usage.NetworkStatsManager.UsageCallback); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean); - field public static final int NETWORK_TYPE_5G_NSA = -2; // 0xfffffffe - } - - public abstract static class NetworkStatsManager.UsageCallback { - method public void onThresholdReached(@NonNull android.net.NetworkTemplate); - } - -} - package android.content { public abstract class ContentProvider implements android.content.ComponentCallbacks2 { @@ -139,9 +114,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 } } @@ -282,32 +262,6 @@ package android.net { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } - public class NetworkIdentity { - method public int getOemManaged(); - method public int getRatType(); - method @Nullable public String getSubscriberId(); - method public int getType(); - method @Nullable public String getWifiNetworkKey(); - method public boolean isDefaultNetwork(); - method public boolean isMetered(); - method public boolean isRoaming(); - } - - public static final class NetworkIdentity.Builder { - ctor public NetworkIdentity.Builder(); - method @NonNull public android.net.NetworkIdentity build(); - method @NonNull public android.net.NetworkIdentity.Builder clearRatType(); - method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot); - method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int); - method @NonNull public android.net.NetworkIdentity.Builder setRatType(int); - method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String); - method @NonNull public android.net.NetworkIdentity.Builder setType(int); - method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String); - } - public class NetworkPolicyManager { method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int); @@ -323,94 +277,6 @@ package android.net { method public default void onUidBlockedReasonChanged(int, int); } - public final class NetworkStateSnapshot implements android.os.Parcelable { - ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int); - method public int describeContents(); - method public int getLegacyType(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method @Nullable public String getSubscriberId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR; - } - - public class NetworkStatsCollection { - method @NonNull public java.util.Map<android.net.NetworkStatsCollection.Key,android.net.NetworkStatsHistory> getEntries(); - } - - public static final class NetworkStatsCollection.Builder { - ctor public NetworkStatsCollection.Builder(long); - method @NonNull public android.net.NetworkStatsCollection.Builder addEntry(@NonNull android.net.NetworkStatsCollection.Key, @NonNull android.net.NetworkStatsHistory); - method @NonNull public android.net.NetworkStatsCollection build(); - } - - public static class NetworkStatsCollection.Key { - ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int); - } - - public final class NetworkStatsHistory implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR; - } - - public static final class NetworkStatsHistory.Builder { - ctor public NetworkStatsHistory.Builder(long, int); - method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry); - method @NonNull public android.net.NetworkStatsHistory build(); - } - - public static final class NetworkStatsHistory.Entry { - ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long); - method public long getActiveTime(); - method public long getBucketStart(); - method public long getOperations(); - method public long getRxBytes(); - method public long getRxPackets(); - method public long getTxBytes(); - method public long getTxPackets(); - } - - public final class NetworkTemplate implements android.os.Parcelable { - method public int describeContents(); - method public int getDefaultNetworkStatus(); - method public int getMatchRule(); - method public int getMeteredness(); - method public int getOemManaged(); - method public int getRatType(); - method public int getRoaming(); - method @NonNull public java.util.Set<java.lang.String> getSubscriberIds(); - method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys(); - method public boolean matches(@NonNull android.net.NetworkIdentity); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR; - field public static final int MATCH_BLUETOOTH = 8; // 0x8 - field public static final int MATCH_CARRIER = 10; // 0xa - field public static final int MATCH_ETHERNET = 5; // 0x5 - field public static final int MATCH_MOBILE = 1; // 0x1 - field public static final int MATCH_WIFI = 4; // 0x4 - field public static final int NETWORK_TYPE_ALL = -1; // 0xffffffff - field public static final int OEM_MANAGED_ALL = -1; // 0xffffffff - field public static final int OEM_MANAGED_NO = 0; // 0x0 - field public static final int OEM_MANAGED_PAID = 1; // 0x1 - field public static final int OEM_MANAGED_PRIVATE = 2; // 0x2 - field public static final int OEM_MANAGED_YES = -2; // 0xfffffffe - } - - public static final class NetworkTemplate.Builder { - ctor public NetworkTemplate.Builder(int); - method @NonNull public android.net.NetworkTemplate build(); - method @NonNull public android.net.NetworkTemplate.Builder setDefaultNetworkStatus(int); - method @NonNull public android.net.NetworkTemplate.Builder setMeteredness(int); - method @NonNull public android.net.NetworkTemplate.Builder setOemManaged(int); - method @NonNull public android.net.NetworkTemplate.Builder setRatType(int); - method @NonNull public android.net.NetworkTemplate.Builder setRoaming(int); - method @NonNull public android.net.NetworkTemplate.Builder setSubscriberIds(@NonNull java.util.Set<java.lang.String>); - method @NonNull public android.net.NetworkTemplate.Builder setWifiNetworkKeys(@NonNull java.util.Set<java.lang.String>); - } - public class NetworkWatchlistManager { method @Nullable public byte[] getWatchlistConfigHash(); } @@ -429,21 +295,6 @@ package android.net { method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); } - public class TrafficStats { - method public static void attachSocketTagger(); - method public static void init(@NonNull android.content.Context); - } - - public final class UnderlyingNetworkInfo implements android.os.Parcelable { - ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>); - method public int describeContents(); - method @NonNull public String getInterface(); - method public int getOwnerUid(); - method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR; - } - public class VpnManager { field public static final int TYPE_VPN_LEGACY = 3; // 0x3 field public static final int TYPE_VPN_NONE = -1; // 0xffffffff diff --git a/core/api/removed.txt b/core/api/removed.txt index 311b110f1997..608a9a4efeca 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -250,10 +250,6 @@ package android.net { method @Deprecated public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache); } - public class TrafficStats { - method @Deprecated public static void setThreadStatsUidSelf(); - } - } package android.os { @@ -513,7 +509,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 6a458fbd30ee..e1d723aa480f 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"; @@ -2250,9 +2260,9 @@ package android.app.smartspace.uitemplatedata { public static final class SmartspaceCarouselUiTemplateData.CarouselItem implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getImage(); - method @Nullable public CharSequence getLowerText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getLowerText(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getTapAction(); - method @Nullable public CharSequence getUpperText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getUpperText(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> CREATOR; } @@ -2261,9 +2271,9 @@ package android.app.smartspace.uitemplatedata { ctor public SmartspaceCarouselUiTemplateData.CarouselItem.Builder(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem build(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setImage(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setTapAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText); } public final class SmartspaceCombinedCardsUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData { @@ -2279,15 +2289,15 @@ package android.app.smartspace.uitemplatedata { public class SmartspaceDefaultUiTemplateData implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getPrimaryTapAction(); - method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubTitleIcon(); - method @Nullable public CharSequence getSubtitleText(); - method @Nullable public CharSequence getSupplementalAlarmText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubtitleIcon(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSubtitleText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSupplementalAlarmText(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSupplementalSubtitleIcon(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSupplementalSubtitleTapAction(); - method @Nullable public CharSequence getSupplementalSubtitleText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSupplementalSubtitleText(); method public int getTemplateType(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getTitleIcon(); - method @Nullable public CharSequence getTitleText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getTitleText(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> CREATOR; } @@ -2296,23 +2306,23 @@ package android.app.smartspace.uitemplatedata { ctor public SmartspaceDefaultUiTemplateData.Builder(int); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData build(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull CharSequence); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText); } public final class SmartspaceHeadToHeadUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData { method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getHeadToHeadAction(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadFirstCompetitorIcon(); - method @Nullable public CharSequence getHeadToHeadFirstCompetitorText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadFirstCompetitorText(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadSecondCompetitorIcon(); - method @Nullable public CharSequence getHeadToHeadSecondCompetitorText(); - method @Nullable public CharSequence getHeadToHeadTitle(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadSecondCompetitorText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadTitle(); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData> CREATOR; } @@ -2321,16 +2331,17 @@ package android.app.smartspace.uitemplatedata { method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData build(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable CharSequence); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText); } public final class SmartspaceIcon implements android.os.Parcelable { method public int describeContents(); method @Nullable public CharSequence getContentDescription(); method @NonNull public android.graphics.drawable.Icon getIcon(); + method public boolean shouldTint(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceIcon> CREATOR; } @@ -2339,46 +2350,47 @@ package android.app.smartspace.uitemplatedata { ctor public SmartspaceIcon.Builder(@NonNull android.graphics.drawable.Icon); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon build(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setContentDescription(@NonNull CharSequence); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setShouldTint(boolean); } public final class SmartspaceSubCardUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData { method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubCardAction(); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubCardIcon(); - method @Nullable public CharSequence getSubCardText(); + method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSubCardText(); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData> CREATOR; } public static final class SmartspaceSubCardUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder { ctor public SmartspaceSubCardUiTemplateData.Builder(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData build(); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull CharSequence); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText); } public final class SmartspaceSubImageUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData { method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubImageAction(); - method @NonNull public java.util.List<java.lang.CharSequence> getSubImageTexts(); + method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText> getSubImageTexts(); method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon> getSubImages(); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData> CREATOR; } public static final class SmartspaceSubImageUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder { - ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>); + ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData build(); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setSubImageAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); } public final class SmartspaceSubListUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData { method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubListAction(); method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubListIcon(); - method @NonNull public java.util.List<java.lang.CharSequence> getSubListTexts(); + method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText> getSubListTexts(); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData> CREATOR; } public static final class SmartspaceSubListUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder { - ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>); + ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText>); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData build(); - method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction); method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon); } @@ -2402,6 +2414,21 @@ package android.app.smartspace.uitemplatedata { method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setUserHandle(@Nullable android.os.UserHandle); } + public final class SmartspaceText implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public CharSequence getText(); + method @NonNull public android.text.TextUtils.TruncateAt getTruncateAtType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceText> CREATOR; + } + + public static final class SmartspaceText.Builder { + ctor public SmartspaceText.Builder(@NonNull CharSequence); + ctor public SmartspaceText.Builder(@NonNull CharSequence, @NonNull android.text.TextUtils.TruncateAt); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceText build(); + method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceText.Builder setTruncateAtType(@NonNull android.text.TextUtils.TruncateAt); + } + } package android.app.time { @@ -2511,13 +2538,6 @@ package android.app.usage { field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService"; } - public class NetworkStatsManager { - method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats(); - method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats(); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider); - } - public static final class UsageEvents.Event { method public int getInstanceId(); method @Nullable public String getNotificationChannelId(); @@ -5925,11 +5945,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 +6115,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(); @@ -7032,8 +7065,8 @@ package android.media.tv.tuner.filter { method @Nullable public String acquireSharedFilterToken(); method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); + method public int delayCallbackForDurationMillis(long); method public int delayCallbackUntilBytesAccumulated(int); - method public int delayCallbackUntilMillisElapsed(long); method public int flush(); method public void freeSharedFilterToken(@NonNull String); method @Deprecated public int getId(); @@ -8397,48 +8430,6 @@ package android.net { field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; } - public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable { - ctor public NetworkStats(long, int); - method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats); - method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry); - method public int describeContents(); - method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator(); - method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR; - field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff - field public static final int DEFAULT_NETWORK_NO = 0; // 0x0 - field public static final int DEFAULT_NETWORK_YES = 1; // 0x1 - field public static final String IFACE_VT = "vt_data0"; - field public static final int METERED_ALL = -1; // 0xffffffff - field public static final int METERED_NO = 0; // 0x0 - field public static final int METERED_YES = 1; // 0x1 - field public static final int ROAMING_ALL = -1; // 0xffffffff - field public static final int ROAMING_NO = 0; // 0x0 - field public static final int ROAMING_YES = 1; // 0x1 - field public static final int SET_ALL = -1; // 0xffffffff - field public static final int SET_DEFAULT = 0; // 0x0 - field public static final int SET_FOREGROUND = 1; // 0x1 - field public static final int TAG_NONE = 0; // 0x0 - field public static final int UID_ALL = -1; // 0xffffffff - field public static final int UID_TETHERING = -5; // 0xfffffffb - } - - public static class NetworkStats.Entry { - ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); - method public int getDefaultNetwork(); - method public int getMetered(); - method public long getOperations(); - method public int getRoaming(); - method public long getRxBytes(); - method public long getRxPackets(); - method public int getSet(); - method public int getTag(); - method public long getTxBytes(); - method public long getTxPackets(); - method public int getUid(); - } - @Deprecated public class RssiCurve implements android.os.Parcelable { ctor @Deprecated public RssiCurve(int, int, byte[]); ctor @Deprecated public RssiCurve(int, int, byte[], int); @@ -8470,19 +8461,6 @@ package android.net { field @Deprecated public final android.net.RssiCurve rssiCurve; } - public class TrafficStats { - method public static void setThreadStatsTagApp(); - method public static void setThreadStatsTagBackup(); - method public static void setThreadStatsTagDownload(); - method public static void setThreadStatsTagRestore(); - field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f - field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80 - field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff - field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00 - field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f - field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -8663,23 +8641,6 @@ package android.net.metrics { } -package android.net.netstats.provider { - - public abstract class NetworkStatsProvider { - ctor public NetworkStatsProvider(); - method public void notifyAlertReached(); - method public void notifyLimitReached(); - method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats); - method public void notifyWarningReached(); - method public abstract void onRequestStatsUpdate(int); - method public abstract void onSetAlert(long); - method public abstract void onSetLimit(@NonNull String, long); - method public void onSetWarningAndLimit(@NonNull String, long, long); - field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff - } - -} - package android.net.sip { @Deprecated public class SipAudioCall { @@ -9651,7 +9612,7 @@ package android.os { method @NonNull public java.util.List<android.os.UserHandle> getAllProfiles(); method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String, boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); @@ -9947,7 +9908,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 @@ -10396,8 +10358,6 @@ package android.provider { } public static final class Settings.Secure extends android.provider.Settings.NameValueTable { - method public static int getIntForUser(@NonNull android.content.ContentResolver, @NonNull String, int, int); - method @Nullable public static String getStringForUser(@NonNull android.content.ContentResolver, @NonNull String, int); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, @Nullable String, boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); field @Deprecated public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled"; @@ -11121,6 +11081,7 @@ package android.service.euicc { field public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED"; field public static final String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT"; field public static final String EXTRA_RESOLUTION_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_PORT_INDEX"; + field public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID = "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID"; field public static final String EXTRA_RESOLUTION_USE_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX"; field public static final String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS"; field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1 @@ -11197,6 +11158,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); } @@ -11206,6 +11168,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); @@ -12234,6 +12201,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"; @@ -13177,7 +13145,6 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypesBitmask(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypesForReason(int); method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -13223,7 +13190,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int, int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Collection<android.telephony.UiccSlotMapping> getSimSlotMapping(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo(); @@ -13278,7 +13244,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAllowedNetworkTypesForReason(int, long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingEnabled(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean); @@ -13328,10 +13293,8 @@ package android.telephony { field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED"; field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"; - field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2 field public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; // 0x3 field public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; // 0x1 - field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0 field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2 field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1 field public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; // 0x5 @@ -13371,26 +13334,6 @@ package android.telephony { field public static final int KEY_TYPE_WLAN = 2; // 0x2 field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1 field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2 - field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L - field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L - field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L - field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L - field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L - field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L - field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L - field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L - field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L - field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L - field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L - field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L - field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L - field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L - field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L - field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L - field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L - field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L - field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L - field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2 field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3 field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 947900041090..4132c64a44c9 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -511,6 +511,8 @@ package android.app.admin { field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe + field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0 + field public static final int DEVICE_OWNER_TYPE_FINANCED = 1; // 0x1 field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17 field public static final int OPERATION_CREATE_AND_MANAGE_USER = 5; // 0x5 field public static final int OPERATION_INSTALL_CA_CERT = 24; // 0x18 @@ -614,10 +616,6 @@ package android.app.prediction { package android.app.usage { - public class NetworkStatsManager { - method public void setPollForce(boolean); - } - public class StorageStatsManager { method public boolean isQuotaSupported(@NonNull java.util.UUID); method public boolean isReservedSupported(@NonNull java.util.UUID); @@ -668,6 +666,7 @@ package android.content { ctor public AttributionSource(int, @Nullable String, @Nullable String); ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder); ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource); + method public void enforceCallingPid(); } public final class AutofillOptions implements android.os.Parcelable { @@ -985,7 +984,7 @@ package android.database.sqlite { package android.graphics { public final class ImageDecoder implements java.lang.AutoCloseable { - method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int); + method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, @NonNull java.io.InputStream, int); } public final class Rect implements android.os.Parcelable { @@ -1623,13 +1622,6 @@ package android.net { method @Nullable public byte[] getWatchlistConfigHash(); } - public class TrafficStats { - method public static long getLoopbackRxBytes(); - method public static long getLoopbackRxPackets(); - method public static long getLoopbackTxBytes(); - method public static long getLoopbackTxPackets(); - } - } package android.os { @@ -2518,7 +2510,6 @@ package android.telephony { method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily(); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); @@ -2800,6 +2791,10 @@ package android.view { field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 } + public interface OnBackInvokedDispatcher { + field public static final long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L; // 0xbade858L + } + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod { method public abstract String asyncImpl() default ""; } @@ -2816,7 +2811,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..a5526bc66431 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -160,7 +160,6 @@ java_library { filegroup { name: "framework-services-net-module-wifi-shared-srcs", srcs: [ - "android/net/DhcpResults.java", "android/util/LocalLog.java", ], } @@ -176,6 +175,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/animation/Animator.java b/core/java/android/animation/Animator.java index eb525d301986..a8ff36aae098 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -16,6 +16,7 @@ package android.animation; +import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo.Config; @@ -535,7 +536,7 @@ public abstract class Animator implements Cloneable { * @param animation The started animation. * @param isReverse Whether the animation is playing in reverse. */ - default void onAnimationStart(Animator animation, boolean isReverse) { + default void onAnimationStart(@NonNull Animator animation, boolean isReverse) { onAnimationStart(animation); } @@ -551,7 +552,7 @@ public abstract class Animator implements Cloneable { * @param animation The animation which reached its end. * @param isReverse Whether the animation is playing in reverse. */ - default void onAnimationEnd(Animator animation, boolean isReverse) { + default void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { onAnimationEnd(animation); } @@ -560,7 +561,7 @@ public abstract class Animator implements Cloneable { * * @param animation The started animation. */ - void onAnimationStart(Animator animation); + void onAnimationStart(@NonNull Animator animation); /** * <p>Notifies the end of the animation. This callback is not invoked @@ -568,7 +569,7 @@ public abstract class Animator implements Cloneable { * * @param animation The animation which reached its end. */ - void onAnimationEnd(Animator animation); + void onAnimationEnd(@NonNull Animator animation); /** * <p>Notifies the cancellation of the animation. This callback is not invoked @@ -576,14 +577,14 @@ public abstract class Animator implements Cloneable { * * @param animation The animation which was canceled. */ - void onAnimationCancel(Animator animation); + void onAnimationCancel(@NonNull Animator animation); /** * <p>Notifies the repetition of the animation.</p> * * @param animation The animation which was repeated. */ - void onAnimationRepeat(Animator animation); + void onAnimationRepeat(@NonNull Animator animation); } /** @@ -599,7 +600,7 @@ public abstract class Animator implements Cloneable { * @param animation The animaton being paused. * @see #pause() */ - void onAnimationPause(Animator animation); + void onAnimationPause(@NonNull Animator animation); /** * <p>Notifies that the animation was resumed, after being @@ -608,7 +609,7 @@ public abstract class Animator implements Cloneable { * @param animation The animation being resumed. * @see #resume() */ - void onAnimationResume(Animator animation); + void onAnimationResume(@NonNull Animator animation); } /** diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 3cbae99224c7..06b424bcb417 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -18,6 +18,7 @@ package android.animation; import android.annotation.CallSuper; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -1626,7 +1627,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * * @param animation The animation which was repeated. */ - void onAnimationUpdate(ValueAnimator animation); + void onAnimationUpdate(@NonNull ValueAnimator animation); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 530666b8f1d7..db865ced6a67 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -115,6 +115,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedCallback; import android.view.OnBackInvokedDispatcher; import android.view.OnBackInvokedDispatcherOwner; import android.view.RemoteAnimationDefinition; @@ -144,6 +145,7 @@ import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; import android.window.SplashScreen; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -791,6 +793,7 @@ public class Activity extends ContextThemeWrapper private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062; private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064; private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065; + private OnBackInvokedCallback mDefaultBackCallback; /** * After {@link Build.VERSION_CODES#TIRAMISU}, @@ -1617,7 +1620,16 @@ public class Activity extends ContextThemeWrapper } mRestoredFromBundle = savedInstanceState != null; mCalled = true; - + if (!WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + // Add onBackPressed as default back behavior. + mDefaultBackCallback = new OnBackInvokedCallback() { + @Override + public void onBackInvoked() { + navigateBack(); + } + }; + getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); + } } /** @@ -2653,6 +2665,10 @@ public class Activity extends ContextThemeWrapper if (mUiTranslationController != null) { mUiTranslationController.onActivityDestroyed(); } + + if (mDefaultBackCallback != null) { + getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback); + } } /** @@ -3773,10 +3789,13 @@ public class Activity extends ContextThemeWrapper * @see KeyEvent */ public boolean onKeyUp(int keyCode, KeyEvent event) { - if (getApplicationInfo().targetSdkVersion - >= Build.VERSION_CODES.ECLAIR) { - if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() - && !event.isCanceled()) { + int sdkVersion = getApplicationInfo().targetSdkVersion; + if (sdkVersion >= Build.VERSION_CODES.ECLAIR) { + if (keyCode == KeyEvent.KEYCODE_BACK + && event.isTracking() + && !event.isCanceled() + && mDefaultBackCallback == null) { + // Using legacy back handling. onBackPressed(); return true; } @@ -3841,6 +3860,10 @@ public class Activity extends ContextThemeWrapper if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) { return; } + navigateBack(); + } + + private void navigateBack() { if (!isTaskRoot()) { // If the activity is not the root of the task, allow finish to proceed normally. finishAfterTransition(); @@ -5503,6 +5526,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 +5544,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 +6173,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 +8780,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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index fdf37f6633ee..0d1bc05df67b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2363,11 +2363,11 @@ public class AppOpsManager { Manifest.permission.USE_BIOMETRIC, Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.SMS_FINANCIAL_TRANSACTIONS, - null, + Manifest.permission.READ_MEDIA_AUDIO, null, // no permission for OP_WRITE_MEDIA_AUDIO - null, + Manifest.permission.READ_MEDIA_VIDEO, null, // no permission for OP_WRITE_MEDIA_VIDEO - null, + Manifest.permission.READ_MEDIA_IMAGE, null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY 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..208477588885 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -52,6 +52,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedCallback; import android.view.OnBackInvokedDispatcher; import android.view.OnBackInvokedDispatcherOwner; import android.view.SearchEvent; @@ -62,6 +63,7 @@ import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.app.WindowDecorActionBar; @@ -156,6 +158,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** A {@link Runnable} to run instead of dismissing when {@link #dismiss()} is called. */ private Runnable mDismissOverride; + private OnBackInvokedCallback mDefaultBackCallback; /** * Creates a dialog window that uses the default dialog theme. @@ -453,6 +456,16 @@ public class Dialog implements DialogInterface, Window.Callback, */ protected void onStart() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); + if (mContext != null && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + // Add onBackPressed as default back behavior. + mDefaultBackCallback = new OnBackInvokedCallback() { + @Override + public void onBackInvoked() { + onBackPressed(); + } + }; + getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); + } } /** @@ -460,6 +473,9 @@ public class Dialog implements DialogInterface, Window.Callback, */ protected void onStop() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); + if (mDefaultBackCallback != null) { + getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback); + } } private static final String DIALOG_SHOWING_TAG = "android:dialogShowing"; @@ -685,7 +701,8 @@ public class Dialog implements DialogInterface, Window.Callback, public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) && event.isTracking() - && !event.isCanceled()) { + && !event.isCanceled() + && WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { onBackPressed(); return true; } @@ -1449,15 +1466,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/Instrumentation.java b/core/java/android/app/Instrumentation.java index eb4585dd7097..a74438ae8452 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -376,14 +376,16 @@ public class Instrumentation { Debug.stopMethodTracing(); } } - + /** - * Force the global system in or out of touch mode. This can be used if - * your instrumentation relies on the UI being in one more or the other - * when it starts. - * - * @param inTouch Set to true to be in touch mode, false to be in - * focus mode. + * Force the global system in or out of touch mode. This can be used if your + * instrumentation relies on the UI being in one more or the other when it starts. + * + * <p><b>Note:</b> Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method + * will only have an effect if the calling process is also the focused window owner or has + * {@link android.permission#MODIFY_TOUCH_MODE_STATE} permission granted. + * + * @param inTouch Set to true to be in touch mode, false to be in focus mode. */ public void setInTouchMode(boolean inTouch) { try { @@ -393,11 +395,11 @@ public class Instrumentation { // Shouldn't happen! } } - + /** * Schedule a callback for when the application's main thread goes idle * (has no more events to process). - * + * * @param recipient Called the next time the thread's message queue is * idle. */ diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 8fcb07f578e8..da1ba5265c4a 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -180,6 +180,8 @@ public class StatusBarManager { public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0; /** @hide */ public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1; + /** @hide */ + public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2; /** @hide */ public static final int WINDOW_STATUS_BAR = 1; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 4187ba0a10a5..f5f2fe0d0292 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -48,7 +48,6 @@ import android.app.timezonedetector.TimeZoneDetectorImpl; import android.app.trust.TrustManager; import android.app.usage.IStorageStatsManager; import android.app.usage.IUsageStatsManager; -import android.app.usage.NetworkStatsManager; import android.app.usage.StorageStatsManager; import android.app.usage.UsageStatsManager; import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager; @@ -140,7 +139,6 @@ import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.EthernetManager; import android.net.IEthernetManager; import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.IPacProxyManager; import android.net.IVpnManager; import android.net.NetworkPolicyManager; @@ -1013,17 +1011,6 @@ public final class SystemServiceRegistry { return new UsageStatsManager(ctx.getOuterContext(), service); }}); - registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, - new CachedServiceFetcher<NetworkStatsManager>() { - @Override - public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException { - // TODO: Replace with an initializer in the module, see - // {@code ConnectivityFrameworkInitializer}. - final INetworkStatsService service = INetworkStatsService.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)); - return new NetworkStatsManager(ctx.getOuterContext(), service); - }}); - registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class, new StaticServiceFetcher<PersistentDataBlockManager>() { @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 6cd991bc49b8..108412db7208 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -56,9 +56,11 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.PrivateDnsConnectivityChecker; import android.net.ProxyInfo; import android.net.Uri; +import android.net.wifi.WifiSsid; import android.nfc.NfcAdapter; import android.os.Binder; import android.os.Build; @@ -111,6 +113,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetSocketAddress; import java.net.Proxy; +import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; @@ -463,7 +466,9 @@ public class DevicePolicyManager { * <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li> * <li>{@link #getUserControlDisabledPackages(ComponentName)}</li> * <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li> + * <li>{@link #getOrganizationName(ComponentName)} </li> * <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li> + * <li>{@link #getShortSupportMessage(ComponentName)}</li> * <li>{@link #isBackupServiceEnabled(ComponentName)}</li> * <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li> * <li>{@link #isLockTaskPermitted(String)}</li> @@ -476,7 +481,9 @@ public class DevicePolicyManager { * <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li> * <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li> * </ul> + * <li>{@link #getLockTaskFeatures(ComponentName)}</li> * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li> + * <li>{@link #getLockTaskPackages(ComponentName)}</li> * <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li> * <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li> * <li>{@link #wipeData(int)}</li> @@ -487,6 +494,10 @@ public class DevicePolicyManager { * {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or * {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin * app (otherwise a {@link SecurityException} will be thrown)</li> + * <li>{@link #getPermissionGrantState(ComponentName, String, String)}, where + * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be + * used and device admin app is the only package that can be used to retrieve the permission + * permission grant state for (otherwise a {@link SecurityException} will be thrown)</li> * <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions * are permitted (otherwise a {@link SecurityException} will be thrown):</li> * <ul> @@ -497,7 +508,17 @@ public class DevicePolicyManager { * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li> * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li> * </ul> - * <li>{@link #clearUserRestriction(ComponentName, String)}</li> + * <li>{@link #getUserRestrictions(ComponentName)}</li> + * <li>{@link #clearUserRestriction(ComponentName, String)}, where the following user + * restrictions are permitted (otherwise a {@link SecurityException} will be thrown):</li> + * <ul> + * <li>{@link UserManager#DISALLOW_ADD_USER}</li> + * <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li> + * <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li> + * <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li> + * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li> + * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li> + * </ul> * </ul> * * @hide @@ -3257,6 +3278,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; /** @@ -3264,6 +3286,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi public static final int DEVICE_OWNER_TYPE_FINANCED = 1; /** @@ -14909,10 +14932,14 @@ public class DevicePolicyManager { mService.setSsidAllowlist(new ArrayList<>()); } else { int policyType = policy.getPolicyType(); + List<String> ssidList = new ArrayList<>(); + for (WifiSsid ssid : policy.getSsids()) { + ssidList.add(new String(ssid.getBytes(), StandardCharsets.UTF_8)); + } if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) { - mService.setSsidAllowlist(new ArrayList<>(policy.getSsids())); + mService.setSsidAllowlist(ssidList); } else { - mService.setSsidDenylist(new ArrayList<>(policy.getSsids())); + mService.setSsidDenylist(ssidList); } } } catch (RemoteException e) { @@ -14938,11 +14965,23 @@ public class DevicePolicyManager { try { List<String> allowlist = mService.getSsidAllowlist(); if (!allowlist.isEmpty()) { - return WifiSsidPolicy.createAllowlistPolicy(new ArraySet<>(allowlist)); + List<WifiSsid> wifiSsidAllowlist = new ArrayList<>(); + for (String ssid : allowlist) { + wifiSsidAllowlist.add( + WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8))); + } + return new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, + new ArraySet<>(wifiSsidAllowlist)); } List<String> denylist = mService.getSsidDenylist(); if (!denylist.isEmpty()) { - return WifiSsidPolicy.createDenylistPolicy(new ArraySet<>(denylist)); + List<WifiSsid> wifiSsidDenylist = new ArrayList<>(); + for (String ssid : denylist) { + wifiSsidDenylist.add( + WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8))); + } + return new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, + new ArraySet<>(wifiSsidDenylist)); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -15206,6 +15245,66 @@ public class DevicePolicyManager { } /** + * Similar to {@link #getDrawable(String, String, String, Callable)} but returns an + * {@link Icon} instead of a {@link Drawable}. + * + * @param drawableId The drawable ID to get the updated resource for. + * @param drawableStyle The drawable style to use. + * @param drawableSource The source for the caller. + * @param defaultIcon Returned if no updated drawable was set for the provided params. + */ + @Nullable + public Icon getDrawableAsIcon( + @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, + @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, + @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource, + @Nullable Icon defaultIcon) { + Objects.requireNonNull(drawableId, "drawableId can't be null"); + Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); + Objects.requireNonNull(drawableSource, "drawableSource can't be null"); + Objects.requireNonNull(defaultIcon, "defaultIcon can't be null"); + + if (Drawables.UNDEFINED.equals(drawableId)) { + return defaultIcon; + } + if (mService != null) { + try { + ParcelableResource resource = mService.getDrawable( + drawableId, drawableStyle, drawableSource); + if (resource == null) { + return defaultIcon; + } + return Icon.createWithResource(resource.getPackageName(), resource.getResourceId()); + } catch (RemoteException e) { + Log.e( + TAG, + "Error getting the updated drawable from DevicePolicyManagerService.", + e); + return defaultIcon; + } + } + return defaultIcon; + } + + /** + * Similar to {@link #getDrawable(String, String, Callable)} but returns an {@link Icon} + * instead of a {@link Drawable}. + * + * @param drawableId The drawable ID to get the updated resource for. + * @param drawableStyle The drawable style to use. + * @param defaultIcon Returned if no updated drawable was set for the provided params. + */ + @Nullable + public Icon getDrawableAsIcon( + @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, + @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, + @Nullable Icon defaultIcon) { + return getDrawableAsIcon( + drawableId, drawableStyle, Drawables.Source.UNDEFINED, defaultIcon); + } + + + /** * For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string * resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID * {@code callingPackageResourceId} (see {@link DevicePolicyResources.Strings}), meaning any @@ -15367,4 +15466,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/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java index 37150179cc68..e91807535c6e 100644 --- a/core/java/android/app/admin/WifiSsidPolicy.java +++ b/core/java/android/app/admin/WifiSsidPolicy.java @@ -18,6 +18,7 @@ package android.app.admin; import android.annotation.IntDef; import android.annotation.NonNull; +import android.net.wifi.WifiSsid; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; @@ -70,50 +71,37 @@ public final class WifiSsidPolicy implements Parcelable { public @interface WifiSsidPolicyType {} private @WifiSsidPolicyType int mPolicyType; - private ArraySet<String> mSsids; + private ArraySet<WifiSsid> mSsids; - private WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<String> ssids) { - mPolicyType = policyType; - mSsids = new ArraySet<>(ssids); - } - - private WifiSsidPolicy(Parcel in) { - mPolicyType = in.readInt(); - mSsids = (ArraySet<String>) in.readArraySet(null); - } /** - * Create the allowlist Wi-Fi SSID Policy. + * Create the Wi-Fi SSID Policy. * - * @param ssids allowlist of SSIDs in UTF-8 without double quotes format - * @throws IllegalArgumentException if the input ssids list is empty + * @param policyType indicate whether the policy is an allowlist or a denylist + * @param ssids set of {@link WifiSsid} + * @throws IllegalArgumentException if the input ssids set is empty or the policyType is invalid */ - @NonNull - public static WifiSsidPolicy createAllowlistPolicy(@NonNull Set<String> ssids) { + public WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<WifiSsid> ssids) { if (ssids.isEmpty()) { throw new IllegalArgumentException("SSID list cannot be empty"); } - return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids); + if (policyType != WIFI_SSID_POLICY_TYPE_ALLOWLIST + && policyType != WIFI_SSID_POLICY_TYPE_DENYLIST) { + throw new IllegalArgumentException("Invalid policy type"); + } + mPolicyType = policyType; + mSsids = new ArraySet<>(ssids); } - /** - * Create the denylist Wi-Fi SSID Policy. - * - * @param ssids denylist of SSIDs in UTF-8 without double quotes format - * @throws IllegalArgumentException if the input ssids list is empty - */ - @NonNull - public static WifiSsidPolicy createDenylistPolicy(@NonNull Set<String> ssids) { - if (ssids.isEmpty()) { - throw new IllegalArgumentException("SSID list cannot be empty"); - } - return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_DENYLIST, ssids); + private WifiSsidPolicy(Parcel in) { + mPolicyType = in.readInt(); + mSsids = (ArraySet<WifiSsid>) in.readArraySet(null); } /** - * Returns the set of SSIDs in UTF-8 without double quotes format. + * Returns the set of {@link WifiSsid} */ @NonNull - public Set<String> getSsids() { + public Set<WifiSsid> getSsids() { return mSsids; } diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java index 060931bdd437..3403ab0db8f3 100644 --- a/core/java/android/app/cloudsearch/SearchResult.java +++ b/core/java/android/app/cloudsearch/SearchResult.java @@ -76,7 +76,7 @@ public final class SearchResult implements Parcelable { public @interface SearchResultExtraInfoKey {} /** This App developer website's domain URL, String value expected. */ public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL"; - /** This App result's ICON URL, String value expected. */ + /** This App icon, android.graphics.drawable.Icon expected. */ public static final String EXTRAINFO_APP_ICON = "APP_ICON"; /** This App developer's name, String value expected. */ public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME"; @@ -114,7 +114,7 @@ public final class SearchResult implements Parcelable { public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE"; /** Web content's URL, String value expected. */ public static final String EXTRAINFO_WEB_URL = "WEB_URL"; - /** Web content's domain icon URL, String value expected. */ + /** Web content's domain icon, android.graphics.drawable.Icon expected. */ public static final String EXTRAINFO_WEB_ICON = "WEB_ICON"; @NonNull diff --git a/core/java/android/app/smartspace/SmartspaceUtils.java b/core/java/android/app/smartspace/SmartspaceUtils.java index f058ffa5a04b..4545f43a8260 100644 --- a/core/java/android/app/smartspace/SmartspaceUtils.java +++ b/core/java/android/app/smartspace/SmartspaceUtils.java @@ -17,6 +17,8 @@ package android.app.smartspace; import android.annotation.Nullable; +import android.app.smartspace.uitemplatedata.SmartspaceText; +import android.text.TextUtils; /** * Utilities for Smartspace data. @@ -28,10 +30,22 @@ public final class SmartspaceUtils { private SmartspaceUtils() { } + /** Returns true if the passed in {@link SmartspaceText} is null or its content is empty. */ + public static boolean isEmpty(@Nullable SmartspaceText text) { + return text == null || TextUtils.isEmpty(text.getText()); + } + + /** Returns true if the passed-in {@link SmartspaceText}s are equal. */ + public static boolean isEqual(@Nullable SmartspaceText text1, @Nullable SmartspaceText text2) { + if (text1 == null && text2 == null) return true; + if (text1 == null || text2 == null) return false; + return text1.equals(text2); + } + /** Returns true if the passed-in {@link CharSequence}s are equal. */ public static boolean isEqual(@Nullable CharSequence cs1, @Nullable CharSequence cs2) { - if ((cs1 == null && cs2 != null) || (cs1 != null && cs2 == null)) return false; if (cs1 == null && cs2 == null) return true; + if (cs1 == null || cs2 == null) return false; return cs1.toString().contentEquals(cs2); } } diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java index c4c4fdef67f9..e996056291bc 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java @@ -23,7 +23,6 @@ import android.app.smartspace.SmartspaceTarget; import android.app.smartspace.SmartspaceUtils; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; import java.util.List; import java.util.Objects; @@ -51,15 +50,15 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT } private SmartspaceCarouselUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, + @Nullable SmartspaceText supplementalAlarmText, @NonNull List<CarouselItem> carouselItems, @Nullable SmartspaceTapAction carouselAction) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, @@ -170,11 +169,11 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT if (mCarouselItems.isEmpty()) { throw new IllegalStateException("Carousel data is empty"); } + return new SmartspaceCarouselUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), - mCarouselItems, + getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mCarouselItems, mCarouselAction); } } @@ -184,7 +183,7 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT /** Text which is above the image item. */ @Nullable - private final CharSequence mUpperText; + private final SmartspaceText mUpperText; /** Image item. Can be empty. */ @Nullable @@ -192,7 +191,7 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT /** Text which is under the image item. */ @Nullable - private final CharSequence mLowerText; + private final SmartspaceText mLowerText; /** * Tap action for this {@link CarouselItem} instance. {@code mCarouselAction} is used if not @@ -202,14 +201,14 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT private final SmartspaceTapAction mTapAction; CarouselItem(@NonNull Parcel in) { - mUpperText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mUpperText = in.readTypedObject(SmartspaceText.CREATOR); mImage = in.readTypedObject(SmartspaceIcon.CREATOR); - mLowerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mLowerText = in.readTypedObject(SmartspaceText.CREATOR); mTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR); } - private CarouselItem(@Nullable CharSequence upperText, @Nullable SmartspaceIcon image, - @Nullable CharSequence lowerText, @Nullable SmartspaceTapAction tapAction) { + private CarouselItem(@Nullable SmartspaceText upperText, @Nullable SmartspaceIcon image, + @Nullable SmartspaceText lowerText, @Nullable SmartspaceTapAction tapAction) { mUpperText = upperText; mImage = image; mLowerText = lowerText; @@ -217,7 +216,7 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT } @Nullable - public CharSequence getUpperText() { + public SmartspaceText getUpperText() { return mUpperText; } @@ -227,7 +226,7 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT } @Nullable - public CharSequence getLowerText() { + public SmartspaceText getLowerText() { return mLowerText; } @@ -260,9 +259,9 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT @Override public void writeToParcel(@NonNull Parcel out, int flags) { - TextUtils.writeToParcel(mUpperText, out, flags); + out.writeTypedObject(mUpperText, flags); out.writeTypedObject(mImage, flags); - TextUtils.writeToParcel(mLowerText, out, flags); + out.writeTypedObject(mLowerText, flags); out.writeTypedObject(mTapAction, flags); } @@ -300,16 +299,16 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT @SystemApi public static final class Builder { - private CharSequence mUpperText; + private SmartspaceText mUpperText; private SmartspaceIcon mImage; - private CharSequence mLowerText; + private SmartspaceText mLowerText; private SmartspaceTapAction mTapAction; /** * Sets the upper text. */ @NonNull - public Builder setUpperText(@Nullable CharSequence upperText) { + public Builder setUpperText(@Nullable SmartspaceText upperText) { mUpperText = upperText; return this; } @@ -328,7 +327,7 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT * Sets the lower text. */ @NonNull - public Builder setLowerText(@Nullable CharSequence lowerText) { + public Builder setLowerText(@Nullable SmartspaceText lowerText) { mLowerText = lowerText; return this; } @@ -349,7 +348,8 @@ public final class SmartspaceCarouselUiTemplateData extends SmartspaceDefaultUiT */ @NonNull public CarouselItem build() { - if (TextUtils.isEmpty(mUpperText) && mImage == null && TextUtils.isEmpty( + if (SmartspaceUtils.isEmpty(mUpperText) && mImage == null + && SmartspaceUtils.isEmpty( mLowerText)) { throw new IllegalStateException("Carousel data is empty"); } diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java index 7e2f74eac4fe..9d4c8e23242c 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java @@ -47,15 +47,15 @@ public final class SmartspaceCombinedCardsUiTemplateData extends SmartspaceDefau } private SmartspaceCombinedCardsUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, + @Nullable SmartspaceText supplementalAlarmText, @NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction, @@ -146,7 +146,7 @@ public final class SmartspaceCombinedCardsUiTemplateData extends SmartspaceDefau throw new IllegalStateException("Please assign a value to all @NonNull args."); } return new SmartspaceCombinedCardsUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mCombinedCardDataList); diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java index 742d5c9bdc0e..a7ac9c7ed4bf 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java @@ -24,7 +24,6 @@ import android.app.smartspace.SmartspaceTarget.UiTemplateType; import android.app.smartspace.SmartspaceUtils; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; import java.util.Objects; @@ -50,17 +49,17 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * will be used, which has its own tap action applied to the title area. */ @Nullable - private final CharSequence mTitleText; + private final SmartspaceText mTitleText; @Nullable private final SmartspaceIcon mTitleIcon; /** Subtitle text and icon are shown at the second row. */ @Nullable - private final CharSequence mSubtitleText; + private final SmartspaceText mSubtitleText; @Nullable - private final SmartspaceIcon mSubTitleIcon; + private final SmartspaceIcon mSubtitleIcon; /** * Primary tap action for the entire card, including the blank spaces, except: 1. When title is @@ -75,7 +74,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Mainly used for weather info on non-weather card. */ @Nullable - private final CharSequence mSupplementalSubtitleText; + private final SmartspaceText mSupplementalSubtitleText; @Nullable private final SmartspaceIcon mSupplementalSubtitleIcon; @@ -92,19 +91,19 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * alarm". */ @Nullable - private final CharSequence mSupplementalAlarmText; + private final SmartspaceText mSupplementalAlarmText; SmartspaceDefaultUiTemplateData(@NonNull Parcel in) { mTemplateType = in.readInt(); - mTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mTitleText = in.readTypedObject(SmartspaceText.CREATOR); mTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR); - mSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mSubTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR); + mSubtitleText = in.readTypedObject(SmartspaceText.CREATOR); + mSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR); mPrimaryTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR); - mSupplementalSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mSupplementalSubtitleText = in.readTypedObject(SmartspaceText.CREATOR); mSupplementalSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR); mSupplementalSubtitleTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR); - mSupplementalAlarmText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mSupplementalAlarmText = in.readTypedObject(SmartspaceText.CREATOR); } /** @@ -112,20 +111,20 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * SmartspaceDefaultUiTemplateData.Builder. */ SmartspaceDefaultUiTemplateData(@UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, - @Nullable SmartspaceIcon subTitleIcon, + @Nullable SmartspaceText subtitleText, + @Nullable SmartspaceIcon subtitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText) { + @Nullable SmartspaceText supplementalAlarmText) { mTemplateType = templateType; mTitleText = titleText; mTitleIcon = titleIcon; mSubtitleText = subtitleText; - mSubTitleIcon = subTitleIcon; + mSubtitleIcon = subtitleIcon; mPrimaryTapAction = primaryTapAction; mSupplementalSubtitleText = supplementalSubtitleText; mSupplementalSubtitleIcon = supplementalSubtitleIcon; @@ -139,7 +138,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { } @Nullable - public CharSequence getTitleText() { + public SmartspaceText getTitleText() { return mTitleText; } @@ -149,28 +148,28 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { } @Nullable - public CharSequence getSubtitleText() { + public SmartspaceText getSubtitleText() { return mSubtitleText; } @Nullable - public SmartspaceIcon getSubTitleIcon() { - return mSubTitleIcon; + public SmartspaceIcon getSubtitleIcon() { + return mSubtitleIcon; } @Nullable - public CharSequence getSupplementalSubtitleText() { - return mSupplementalSubtitleText; + public SmartspaceTapAction getPrimaryTapAction() { + return mPrimaryTapAction; } @Nullable - public SmartspaceIcon getSupplementalSubtitleIcon() { - return mSupplementalSubtitleIcon; + public SmartspaceText getSupplementalSubtitleText() { + return mSupplementalSubtitleText; } @Nullable - public SmartspaceTapAction getPrimaryTapAction() { - return mPrimaryTapAction; + public SmartspaceIcon getSupplementalSubtitleIcon() { + return mSupplementalSubtitleIcon; } @Nullable @@ -179,7 +178,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { } @Nullable - public CharSequence getSupplementalAlarmText() { + public SmartspaceText getSupplementalAlarmText() { return mSupplementalAlarmText; } @@ -208,15 +207,15 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(mTemplateType); - TextUtils.writeToParcel(mTitleText, out, flags); + out.writeTypedObject(mTitleText, flags); out.writeTypedObject(mTitleIcon, flags); - TextUtils.writeToParcel(mSubtitleText, out, flags); - out.writeTypedObject(mSubTitleIcon, flags); + out.writeTypedObject(mSubtitleText, flags); + out.writeTypedObject(mSubtitleIcon, flags); out.writeTypedObject(mPrimaryTapAction, flags); - TextUtils.writeToParcel(mSupplementalSubtitleText, out, flags); + out.writeTypedObject(mSupplementalSubtitleText, flags); out.writeTypedObject(mSupplementalSubtitleIcon, flags); out.writeTypedObject(mSupplementalSubtitleTapAction, flags); - TextUtils.writeToParcel(mSupplementalAlarmText, out, flags); + out.writeTypedObject(mSupplementalAlarmText, flags); } @Override @@ -228,7 +227,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { that.mTitleText) && Objects.equals(mTitleIcon, that.mTitleIcon) && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText) - && Objects.equals(mSubTitleIcon, that.mSubTitleIcon) + && Objects.equals(mSubtitleIcon, that.mSubtitleIcon) && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction) && SmartspaceUtils.isEqual(mSupplementalSubtitleText, that.mSupplementalSubtitleText) @@ -240,7 +239,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { @Override public int hashCode() { - return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubTitleIcon, + return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon, mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction, mSupplementalAlarmText); } @@ -252,7 +251,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { + ", mTitleText=" + mTitleText + ", mTitleIcon=" + mTitleIcon + ", mSubtitleText=" + mSubtitleText - + ", mSubTitleIcon=" + mSubTitleIcon + + ", mSubTitleIcon=" + mSubtitleIcon + ", mPrimaryTapAction=" + mPrimaryTapAction + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon @@ -271,15 +270,15 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { public static class Builder { @UiTemplateType private final int mTemplateType; - private CharSequence mTitleText; + private SmartspaceText mTitleText; private SmartspaceIcon mTitleIcon; - private CharSequence mSubtitleText; - private SmartspaceIcon mSubTitleIcon; + private SmartspaceText mSubtitleText; + private SmartspaceIcon mSubtitleIcon; private SmartspaceTapAction mPrimaryTapAction; - private CharSequence mSupplementalSubtitleText; + private SmartspaceText mSupplementalSubtitleText; private SmartspaceIcon mSupplementalSubtitleIcon; private SmartspaceTapAction mSupplementalSubtitleTapAction; - private CharSequence mSupplementalAlarmText; + private SmartspaceText mSupplementalAlarmText; /** * A builder for {@link SmartspaceDefaultUiTemplateData}. @@ -300,7 +299,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - CharSequence getTitleText() { + SmartspaceText getTitleText() { return mTitleText; } @@ -314,15 +313,15 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - CharSequence getSubtitleText() { + SmartspaceText getSubtitleText() { return mSubtitleText; } /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - SmartspaceIcon getSubTitleIcon() { - return mSubTitleIcon; + SmartspaceIcon getSubtitleIcon() { + return mSubtitleIcon; } /** Should ONLY be used by the subclasses */ @@ -335,7 +334,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - CharSequence getSupplementalSubtitleText() { + SmartspaceText getSupplementalSubtitleText() { return mSupplementalSubtitleText; } @@ -356,7 +355,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - CharSequence getSupplementalAlarmText() { + SmartspaceText getSupplementalAlarmText() { return mSupplementalAlarmText; } @@ -364,7 +363,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Sets the card title. */ @NonNull - public Builder setTitleText(@NonNull CharSequence titleText) { + public Builder setTitleText(@NonNull SmartspaceText titleText) { mTitleText = titleText; return this; } @@ -382,7 +381,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Sets the card subtitle. */ @NonNull - public Builder setSubtitleText(@NonNull CharSequence subtitleText) { + public Builder setSubtitleText(@NonNull SmartspaceText subtitleText) { mSubtitleText = subtitleText; return this; } @@ -391,8 +390,8 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Sets the card subtitle icon. */ @NonNull - public Builder setSubTitleIcon(@NonNull SmartspaceIcon subTitleIcon) { - mSubTitleIcon = subTitleIcon; + public Builder setSubtitleIcon(@NonNull SmartspaceIcon subtitleIcon) { + mSubtitleIcon = subtitleIcon; return this; } @@ -409,7 +408,8 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Sets the supplemental subtitle text. */ @NonNull - public Builder setSupplementalSubtitleText(@NonNull CharSequence supplementalSubtitleText) { + public Builder setSupplementalSubtitleText( + @NonNull SmartspaceText supplementalSubtitleText) { mSupplementalSubtitleText = supplementalSubtitleText; return this; } @@ -440,7 +440,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { * Sets the supplemental alarm text. */ @NonNull - public Builder setSupplementalAlarmText(@NonNull CharSequence supplementalAlarmText) { + public Builder setSupplementalAlarmText(@NonNull SmartspaceText supplementalAlarmText) { mSupplementalAlarmText = supplementalAlarmText; return this; } @@ -451,7 +451,7 @@ public class SmartspaceDefaultUiTemplateData implements Parcelable { @NonNull public SmartspaceDefaultUiTemplateData build() { return new SmartspaceDefaultUiTemplateData(mTemplateType, mTitleText, mTitleIcon, - mSubtitleText, mSubTitleIcon, mPrimaryTapAction, mSupplementalSubtitleText, + mSubtitleText, mSubtitleIcon, mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction, mSupplementalAlarmText); } diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java index c76af27e7f16..bcd12eb0f527 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java @@ -22,7 +22,6 @@ import android.annotation.SystemApi; import android.app.smartspace.SmartspaceTarget; import android.app.smartspace.SmartspaceUtils; import android.os.Parcel; -import android.text.TextUtils; import java.util.Objects; @@ -35,15 +34,15 @@ import java.util.Objects; public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultUiTemplateData { @Nullable - private final CharSequence mHeadToHeadTitle; + private final SmartspaceText mHeadToHeadTitle; @Nullable private final SmartspaceIcon mHeadToHeadFirstCompetitorIcon; @Nullable private final SmartspaceIcon mHeadToHeadSecondCompetitorIcon; @Nullable - private final CharSequence mHeadToHeadFirstCompetitorText; + private final SmartspaceText mHeadToHeadFirstCompetitorText; @Nullable - private final CharSequence mHeadToHeadSecondCompetitorText; + private final SmartspaceText mHeadToHeadSecondCompetitorText; /** Tap action for the head-to-head secondary card. */ @Nullable @@ -51,29 +50,29 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU SmartspaceHeadToHeadUiTemplateData(@NonNull Parcel in) { super(in); - mHeadToHeadTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mHeadToHeadTitle = in.readTypedObject(SmartspaceText.CREATOR); mHeadToHeadFirstCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR); mHeadToHeadSecondCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR); - mHeadToHeadFirstCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mHeadToHeadSecondCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mHeadToHeadFirstCompetitorText = in.readTypedObject(SmartspaceText.CREATOR); + mHeadToHeadSecondCompetitorText = in.readTypedObject(SmartspaceText.CREATOR); mHeadToHeadAction = in.readTypedObject(SmartspaceTapAction.CREATOR); } private SmartspaceHeadToHeadUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, - @Nullable CharSequence headToHeadTitle, + @Nullable SmartspaceText supplementalAlarmText, + @Nullable SmartspaceText headToHeadTitle, @Nullable SmartspaceIcon headToHeadFirstCompetitorIcon, @Nullable SmartspaceIcon headToHeadSecondCompetitorIcon, - @Nullable CharSequence headToHeadFirstCompetitorText, - @Nullable CharSequence headToHeadSecondCompetitorText, + @Nullable SmartspaceText headToHeadFirstCompetitorText, + @Nullable SmartspaceText headToHeadSecondCompetitorText, @Nullable SmartspaceTapAction headToHeadAction) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction, @@ -87,7 +86,7 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU } @Nullable - public CharSequence getHeadToHeadTitle() { + public SmartspaceText getHeadToHeadTitle() { return mHeadToHeadTitle; } @@ -102,12 +101,12 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU } @Nullable - public CharSequence getHeadToHeadFirstCompetitorText() { + public SmartspaceText getHeadToHeadFirstCompetitorText() { return mHeadToHeadFirstCompetitorText; } @Nullable - public CharSequence getHeadToHeadSecondCompetitorText() { + public SmartspaceText getHeadToHeadSecondCompetitorText() { return mHeadToHeadSecondCompetitorText; } @@ -141,11 +140,11 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU @Override public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); - TextUtils.writeToParcel(mHeadToHeadTitle, out, flags); + out.writeTypedObject(mHeadToHeadTitle, flags); out.writeTypedObject(mHeadToHeadFirstCompetitorIcon, flags); out.writeTypedObject(mHeadToHeadSecondCompetitorIcon, flags); - TextUtils.writeToParcel(mHeadToHeadFirstCompetitorText, out, flags); - TextUtils.writeToParcel(mHeadToHeadSecondCompetitorText, out, flags); + out.writeTypedObject(mHeadToHeadFirstCompetitorText, flags); + out.writeTypedObject(mHeadToHeadSecondCompetitorText, flags); out.writeTypedObject(mHeadToHeadAction, flags); } @@ -195,11 +194,11 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU @SystemApi public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder { - private CharSequence mHeadToHeadTitle; + private SmartspaceText mHeadToHeadTitle; private SmartspaceIcon mHeadToHeadFirstCompetitorIcon; private SmartspaceIcon mHeadToHeadSecondCompetitorIcon; - private CharSequence mHeadToHeadFirstCompetitorText; - private CharSequence mHeadToHeadSecondCompetitorText; + private SmartspaceText mHeadToHeadFirstCompetitorText; + private SmartspaceText mHeadToHeadSecondCompetitorText; private SmartspaceTapAction mHeadToHeadAction; /** @@ -213,7 +212,7 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU * Sets the head-to-head card's title */ @NonNull - public Builder setHeadToHeadTitle(@Nullable CharSequence headToHeadTitle) { + public Builder setHeadToHeadTitle(@Nullable SmartspaceText headToHeadTitle) { mHeadToHeadTitle = headToHeadTitle; return this; } @@ -243,7 +242,7 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU */ @NonNull public Builder setHeadToHeadFirstCompetitorText( - @Nullable CharSequence headToHeadFirstCompetitorText) { + @Nullable SmartspaceText headToHeadFirstCompetitorText) { mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText; return this; } @@ -253,7 +252,7 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU */ @NonNull public Builder setHeadToHeadSecondCompetitorText( - @Nullable CharSequence headToHeadSecondCompetitorText) { + @Nullable SmartspaceText headToHeadSecondCompetitorText) { mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText; return this; } @@ -273,7 +272,7 @@ public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultU @NonNull public SmartspaceHeadToHeadUiTemplateData build() { return new SmartspaceHeadToHeadUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mHeadToHeadTitle, diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java index 70b30954afa7..1efbaeb8bd3c 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java @@ -42,14 +42,19 @@ public final class SmartspaceIcon implements Parcelable { @Nullable private final CharSequence mContentDescription; + private final boolean mShouldTint; + SmartspaceIcon(@NonNull Parcel in) { mIcon = in.readTypedObject(Icon.CREATOR); mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mShouldTint = in.readBoolean(); } - private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription) { + private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription, + boolean shouldTint) { mIcon = icon; mContentDescription = contentDescription; + mShouldTint = shouldTint; } @NonNull @@ -62,6 +67,11 @@ public final class SmartspaceIcon implements Parcelable { return mContentDescription; } + /** Return shouldTint value. The default value is true. */ + public boolean shouldTint() { + return mShouldTint; + } + @NonNull public static final Creator<SmartspaceIcon> CREATOR = new Creator<SmartspaceIcon>() { @Override @@ -80,13 +90,14 @@ public final class SmartspaceIcon implements Parcelable { if (this == o) return true; if (!(o instanceof SmartspaceIcon)) return false; SmartspaceIcon that = (SmartspaceIcon) o; - return mIcon.equals(that.mIcon) && SmartspaceUtils.isEqual(mContentDescription, - that.mContentDescription); + return mIcon.toString().equals(that.mIcon.toString()) && SmartspaceUtils.isEqual( + mContentDescription, + that.mContentDescription) && mShouldTint == that.mShouldTint; } @Override public int hashCode() { - return Objects.hash(mIcon, mContentDescription); + return Objects.hash(mIcon.toString(), mContentDescription, mShouldTint); } @Override @@ -98,13 +109,15 @@ public final class SmartspaceIcon implements Parcelable { public void writeToParcel(@NonNull Parcel out, int flags) { out.writeTypedObject(mIcon, flags); TextUtils.writeToParcel(mContentDescription, out, flags); + out.writeBoolean(mShouldTint); } @Override public String toString() { return "SmartspaceIcon{" - + "mImage=" + mIcon - + ", mContentDescription='" + mContentDescription + '\'' + + "mIcon=" + mIcon + + ", mContentDescription=" + mContentDescription + + ", mShouldTint=" + mShouldTint + '}'; } @@ -118,14 +131,16 @@ public final class SmartspaceIcon implements Parcelable { private Icon mIcon; private CharSequence mContentDescription; + private boolean mShouldTint; /** - * A builder for {@link SmartspaceIcon}. + * A builder for {@link SmartspaceIcon}, which sets shouldTint to true by default. * - * @param icon the icon image of this smartspace icon. + * @param icon the icon image of this {@link SmartspaceIcon} instance. */ public Builder(@NonNull Icon icon) { mIcon = Objects.requireNonNull(icon); + mShouldTint = true; } /** @@ -138,11 +153,20 @@ public final class SmartspaceIcon implements Parcelable { } /** + * Sets should tint icon. + */ + @NonNull + public Builder setShouldTint(boolean shouldTint) { + mShouldTint = shouldTint; + return this; + } + + /** * Builds a new SmartspaceIcon instance. */ @NonNull public SmartspaceIcon build() { - return new SmartspaceIcon(mIcon, mContentDescription); + return new SmartspaceIcon(mIcon, mContentDescription, mShouldTint); } } } diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java index 287cf8e61bc3..2db13d31bba1 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java @@ -22,7 +22,6 @@ import android.annotation.SystemApi; import android.app.smartspace.SmartspaceTarget; import android.app.smartspace.SmartspaceUtils; import android.os.Parcel; -import android.text.TextUtils; import java.util.Objects; @@ -40,7 +39,7 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe /** Text for the sub-card, which shows below the icon when being set. */ @Nullable - private final CharSequence mSubCardText; + private final SmartspaceText mSubCardText; /** Tap action for the sub-card secondary card. */ @Nullable @@ -49,22 +48,22 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe SmartspaceSubCardUiTemplateData(@NonNull Parcel in) { super(in); mSubCardIcon = in.readTypedObject(SmartspaceIcon.CREATOR); - mSubCardText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mSubCardText = in.readTypedObject(SmartspaceText.CREATOR); mSubCardAction = in.readTypedObject(SmartspaceTapAction.CREATOR); } private SmartspaceSubCardUiTemplateData(int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, + @Nullable SmartspaceText supplementalAlarmText, @NonNull SmartspaceIcon subCardIcon, - @Nullable CharSequence subCardText, + @Nullable SmartspaceText subCardText, @Nullable SmartspaceTapAction subCardAction) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction, @@ -80,7 +79,7 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe } @Nullable - public CharSequence getSubCardText() { + public SmartspaceText getSubCardText() { return mSubCardText; } @@ -115,7 +114,7 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); out.writeTypedObject(mSubCardIcon, flags); - TextUtils.writeToParcel(mSubCardText, out, flags); + out.writeTypedObject(mSubCardText, flags); out.writeTypedObject(mSubCardAction, flags); } @@ -153,7 +152,7 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder { private final SmartspaceIcon mSubCardIcon; - private CharSequence mSubCardText; + private SmartspaceText mSubCardText; private SmartspaceTapAction mSubCardAction; /** @@ -165,11 +164,11 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe } /** - * Sets the card title text. + * Sets the card text. */ @NonNull - public Builder setSubCardAction(@NonNull CharSequence subCardTitleText) { - mSubCardText = subCardTitleText; + public Builder setSubCardText(@NonNull SmartspaceText subCardText) { + mSubCardText = subCardText; return this; } @@ -188,7 +187,7 @@ public final class SmartspaceSubCardUiTemplateData extends SmartspaceDefaultUiTe @NonNull public SmartspaceSubCardUiTemplateData build() { return new SmartspaceSubCardUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon, mSubCardText, diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java index c4799936f8c9..2fe4cf87984a 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java @@ -22,8 +22,6 @@ import android.annotation.SystemApi; import android.app.smartspace.SmartspaceTarget; import android.os.Parcel; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -37,7 +35,7 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT /** Texts are shown next to the image as a vertical list */ @NonNull - private final List<CharSequence> mSubImageTexts; + private final List<SmartspaceText> mSubImageTexts; /** If multiple images are passed in, they will be rendered as GIF. */ @NonNull @@ -49,22 +47,22 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT SmartspaceSubImageUiTemplateData(@NonNull Parcel in) { super(in); - mSubImageTexts = Arrays.asList(in.readCharSequenceArray()); + mSubImageTexts = in.createTypedArrayList(SmartspaceText.CREATOR); mSubImages = in.createTypedArrayList(SmartspaceIcon.CREATOR); mSubImageAction = in.readTypedObject(SmartspaceTapAction.CREATOR); } private SmartspaceSubImageUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, - @NonNull List<CharSequence> subImageTexts, + @Nullable SmartspaceText supplementalAlarmText, + @NonNull List<SmartspaceText> subImageTexts, @NonNull List<SmartspaceIcon> subImages, @Nullable SmartspaceTapAction subImageAction) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, @@ -76,7 +74,7 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT } @NonNull - public List<CharSequence> getSubImageTexts() { + public List<SmartspaceText> getSubImageTexts() { return mSubImageTexts; } @@ -115,7 +113,7 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT @Override public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); - out.writeCharSequenceList(new ArrayList<>(mSubImageTexts)); + out.writeTypedList(mSubImageTexts); out.writeTypedList(mSubImages); out.writeTypedObject(mSubImageAction, flags); } @@ -153,14 +151,14 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT @SystemApi public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder { - private final List<CharSequence> mSubImageTexts; + private final List<SmartspaceText> mSubImageTexts; private final List<SmartspaceIcon> mSubImages; private SmartspaceTapAction mSubImageAction; /** * A builder for {@link SmartspaceSubImageUiTemplateData}. */ - public Builder(@NonNull List<CharSequence> subImageTexts, + public Builder(@NonNull List<SmartspaceText> subImageTexts, @NonNull List<SmartspaceIcon> subImages) { super(SmartspaceTarget.UI_TEMPLATE_SUB_IMAGE); mSubImageTexts = Objects.requireNonNull(subImageTexts); @@ -171,7 +169,7 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT * Sets the card tap action. */ @NonNull - public Builder setCarouselAction(@NonNull SmartspaceTapAction subImageAction) { + public Builder setSubImageAction(@NonNull SmartspaceTapAction subImageAction) { mSubImageAction = subImageAction; return this; } @@ -182,7 +180,7 @@ public final class SmartspaceSubImageUiTemplateData extends SmartspaceDefaultUiT @NonNull public SmartspaceSubImageUiTemplateData build() { return new SmartspaceSubImageUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts, mSubImages, diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java index b5d9645027d8..9512c7fb130e 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java @@ -22,11 +22,10 @@ import android.annotation.SystemApi; import android.app.smartspace.SmartspaceTarget; import android.os.Parcel; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; + /** * Holds all the relevant data needed to render a Smartspace card with the sub-list Ui Template. * @@ -38,7 +37,7 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe @Nullable private final SmartspaceIcon mSubListIcon; @NonNull - private final List<CharSequence> mSubListTexts; + private final List<SmartspaceText> mSubListTexts; /** Tap action for the sub-list secondary card. */ @Nullable @@ -47,22 +46,22 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe SmartspaceSubListUiTemplateData(@NonNull Parcel in) { super(in); mSubListIcon = in.readTypedObject(SmartspaceIcon.CREATOR); - mSubListTexts = Arrays.asList(in.readCharSequenceArray()); + mSubListTexts = in.createTypedArrayList(SmartspaceText.CREATOR); mSubListAction = in.readTypedObject(SmartspaceTapAction.CREATOR); } private SmartspaceSubListUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable CharSequence titleText, + @Nullable SmartspaceText titleText, @Nullable SmartspaceIcon titleIcon, - @Nullable CharSequence subtitleText, + @Nullable SmartspaceText subtitleText, @Nullable SmartspaceIcon subTitleIcon, @Nullable SmartspaceTapAction primaryTapAction, - @Nullable CharSequence supplementalSubtitleText, + @Nullable SmartspaceText supplementalSubtitleText, @Nullable SmartspaceIcon supplementalSubtitleIcon, @Nullable SmartspaceTapAction supplementalSubtitleTapAction, - @Nullable CharSequence supplementalAlarmText, + @Nullable SmartspaceText supplementalAlarmText, @Nullable SmartspaceIcon subListIcon, - @NonNull List<CharSequence> subListTexts, + @NonNull List<SmartspaceText> subListTexts, @Nullable SmartspaceTapAction subListAction) { super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction, supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction, @@ -78,7 +77,7 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe } @NonNull - public List<CharSequence> getSubListTexts() { + public List<SmartspaceText> getSubListTexts() { return mSubListTexts; } @@ -113,7 +112,7 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); out.writeTypedObject(mSubListIcon, flags); - out.writeCharSequenceList(new ArrayList<>(mSubListTexts)); + out.writeTypedList(mSubListTexts); out.writeTypedObject(mSubListAction, flags); } @@ -151,13 +150,13 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder { private SmartspaceIcon mSubListIcon; - private final List<CharSequence> mSubListTexts; + private final List<SmartspaceText> mSubListTexts; private SmartspaceTapAction mSubListAction; /** * A builder for {@link SmartspaceSubListUiTemplateData}. */ - public Builder(@NonNull List<CharSequence> subListTexts) { + public Builder(@NonNull List<SmartspaceText> subListTexts) { super(SmartspaceTarget.UI_TEMPLATE_SUB_LIST); mSubListTexts = Objects.requireNonNull(subListTexts); } @@ -175,7 +174,7 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe * Sets the card tap action. */ @NonNull - public Builder setCarouselAction(@NonNull SmartspaceTapAction subListAction) { + public Builder setSubListAction(@NonNull SmartspaceTapAction subListAction) { mSubListAction = subListAction; return this; } @@ -186,7 +185,7 @@ public final class SmartspaceSubListUiTemplateData extends SmartspaceDefaultUiTe @NonNull public SmartspaceSubListUiTemplateData build() { return new SmartspaceSubListUiTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(), + getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(), getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon, mSubListTexts, diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java new file mode 100644 index 000000000000..25d13e6521c6 --- /dev/null +++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java @@ -0,0 +1,143 @@ +/* + * 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.app.smartspace.uitemplatedata; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.smartspace.SmartspaceUtils; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.util.Objects; + +/** + * Holds the information for a Smartspace-card text: the text content and + * the truncate_at information. + * + * @hide + */ +@SystemApi +public final class SmartspaceText implements Parcelable { + + @NonNull + private final CharSequence mText; + + private final TextUtils.TruncateAt mTruncateAtType; + + SmartspaceText(Parcel in) { + mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mTruncateAtType = TextUtils.TruncateAt.valueOf(in.readString()); + } + + private SmartspaceText(@NonNull CharSequence text, TextUtils.TruncateAt truncateAtType) { + mText = text; + mTruncateAtType = truncateAtType; + } + + @NonNull + public CharSequence getText() { + return mText; + } + + @NonNull + public TextUtils.TruncateAt getTruncateAtType() { + return mTruncateAtType; + } + + @NonNull + public static final Creator<SmartspaceText> CREATOR = new Creator<SmartspaceText>() { + @Override + public SmartspaceText createFromParcel(Parcel in) { + return new SmartspaceText(in); + } + + @Override + public SmartspaceText[] newArray(int size) { + return new SmartspaceText[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SmartspaceText)) return false; + SmartspaceText that = (SmartspaceText) o; + return mTruncateAtType == that.mTruncateAtType && SmartspaceUtils.isEqual(mText, + that.mText); + } + + @Override + public int hashCode() { + return Objects.hash(mText, mTruncateAtType); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + TextUtils.writeToParcel(mText, out, flags); + out.writeString(mTruncateAtType.name()); + } + + /** + * A builder for {@link SmartspaceText} object. + * + * @hide + */ + @SystemApi + public static final class Builder { + private final CharSequence mText; + private TextUtils.TruncateAt mTruncateAtType; + + /** + * A builder for {@link SmartspaceText}, which sets TruncateAtType to AT_END by default. + */ + public Builder(@NonNull CharSequence text) { + mText = Objects.requireNonNull(text); + mTruncateAtType = TextUtils.TruncateAt.END; + } + + /** + * A builder for {@link SmartspaceText}. + */ + public Builder(@NonNull CharSequence text, @NonNull TextUtils.TruncateAt truncateAtType) { + mText = Objects.requireNonNull(text); + mTruncateAtType = Objects.requireNonNull(truncateAtType); + } + + /** + * Sets truncateAtType. + */ + @NonNull + public Builder setTruncateAtType(@NonNull TextUtils.TruncateAt truncateAtType) { + mTruncateAtType = Objects.requireNonNull(truncateAtType); + return this; + } + + /** + * Builds a new SmartspaceText instance. + */ + @NonNull + public SmartspaceText build() { + return new SmartspaceText(mText, mTruncateAtType); + } + } +} diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 157e709a67f0..3f2fa2188d24 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -155,8 +155,8 @@ public final class AttributionSource implements Parcelable { this(AttributionSourceState.CREATOR.createFromParcel(in)); // Since we just unpacked this object as part of it transiting a Binder - // call, this is the perfect time to enforce that its UID can be trusted - enforceCallingUid(); + // call, this is the perfect time to enforce that its UID and PID can be trusted + enforceCallingUidAndPid(); } /** @hide */ @@ -259,13 +259,24 @@ public final class AttributionSource implements Parcelable { } /** + * If you are handling an IPC and you don't trust the caller you need to validate whether the + * attribution source is one for the calling app to prevent the caller to pass you a source from + * another app without including themselves in the attribution chain. + * + * @throws SecurityException if the attribution source cannot be trusted to be from the caller. + */ + private void enforceCallingUidAndPid() { + enforceCallingUid(); + enforceCallingPid(); + } + + /** * If you are handling an IPC and you don't trust the caller you need to validate * whether the attribution source is one for the calling app to prevent the caller * to pass you a source from another app without including themselves in the * attribution chain. * - * @throws SecurityException if the attribution source cannot be trusted to be - * from the caller. + * @throws SecurityException if the attribution source cannot be trusted to be from the caller. */ public void enforceCallingUid() { if (!checkCallingUid()) { @@ -294,6 +305,33 @@ public final class AttributionSource implements Parcelable { return true; } + /** + * Validate that the pid being claimed for the calling app is not spoofed + * + * @throws SecurityException if the attribution source cannot be trusted to be from the caller. + * @hide + */ + @TestApi + public void enforceCallingPid() { + if (!checkCallingPid()) { + throw new SecurityException("Calling pid: " + Binder.getCallingPid() + + " doesn't match source pid: " + mAttributionSourceState.pid); + } + } + + /** + * Validate that the pid being claimed for the calling app is not spoofed + * + * @return if the attribution source cannot be trusted to be from the caller. + */ + private boolean checkCallingPid() { + final int callingPid = Binder.getCallingPid(); + if (mAttributionSourceState.pid != -1 && callingPid != mAttributionSourceState.pid) { + return false; + } + return true; + } + @Override public String toString() { if (Build.IS_DEBUGGABLE) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6ffea3f9217d..207412511198 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6504,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..e9466e99c5ca 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; /** @@ -3325,7 +3326,8 @@ public abstract class PackageManager { * <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio"; + public static final String FEATURE_TELEPHONY_RADIO_ACCESS = + "android.hardware.telephony.radio.access"; /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java index ed22de8dd594..85890c1912c5 100644 --- a/core/java/android/hardware/CameraStreamStats.java +++ b/core/java/android/hardware/CameraStreamStats.java @@ -16,6 +16,7 @@ package android.hardware; import android.hardware.camera2.params.DynamicRangeProfiles; +import android.hardware.camera2.CameraMetadata; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -47,6 +48,7 @@ public class CameraStreamStats implements Parcelable { private float[] mHistogramBins; private long[] mHistogramCounts; private int mDynamicRangeProfile; + private int mStreamUseCase; private static final String TAG = "CameraStreamStats"; @@ -63,11 +65,13 @@ public class CameraStreamStats implements Parcelable { mMaxAppBuffers = 0; mHistogramType = HISTOGRAM_TYPE_UNKNOWN; mDynamicRangeProfile = DynamicRangeProfiles.STANDARD; + mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; } public CameraStreamStats(int width, int height, int format, int dataSpace, long usage, long requestCount, long errorCount, - int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile) { + int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile, + int streamUseCase) { mWidth = width; mHeight = height; mFormat = format; @@ -80,6 +84,7 @@ public class CameraStreamStats implements Parcelable { mMaxAppBuffers = maxAppBuffers; mHistogramType = HISTOGRAM_TYPE_UNKNOWN; mDynamicRangeProfile = dynamicRangeProfile; + mStreamUseCase = streamUseCase; } public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR = @@ -126,6 +131,7 @@ public class CameraStreamStats implements Parcelable { dest.writeFloatArray(mHistogramBins); dest.writeLongArray(mHistogramCounts); dest.writeInt(mDynamicRangeProfile); + dest.writeInt(mStreamUseCase); } public void readFromParcel(Parcel in) { @@ -143,6 +149,7 @@ public class CameraStreamStats implements Parcelable { mHistogramBins = in.createFloatArray(); mHistogramCounts = in.createLongArray(); mDynamicRangeProfile = in.readInt(); + mStreamUseCase = in.readInt(); } public int getWidth() { @@ -200,4 +207,8 @@ public class CameraStreamStats implements Parcelable { public int getDynamicRangeProfile() { return mDynamicRangeProfile; } + + public int getStreamUseCase() { + return mStreamUseCase; + } } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 37cfb4935f0d..0d3aaf575729 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -726,6 +726,89 @@ public final class Sensor { public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker"; /** + * A constant describing a limited axes accelerometer sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + * + */ + public static final int TYPE_ACCELEROMETER_LIMITED_AXES = 38; + + /** + * A constant string describing a limited axes accelerometer sensor. + * + * @see #TYPE_ACCELEROMETER_LIMITED_AXES + * + */ + public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES = + "android.sensor.accelerometer_limited_axes"; + + /** + * A constant describing a limited axes gyroscope sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + * + */ + public static final int TYPE_GYROSCOPE_LIMITED_AXES = 39; + + /** + * A constant string describing a limited axes gyroscope sensor. + * + * @see #TYPE_GYROSCOPE_LIMITED_AXES + * + */ + public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES = + "android.sensor.gyroscope_limited_axes"; + + /** + * A constant describing an uncalibrated limited axes accelerometer sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + * + */ + public static final int TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40; + + /** + * A constant string describing an uncalibrated limited axes accelerometer sensor. + * + * @see #TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED + * + */ + public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = + "android.sensor.accelerometer_limited_axes_uncalibrated"; + + /** + * A constant describing an uncalibrated limited axes gyroscope sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + * + */ + public static final int TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41; + + /** + * A constant string describing an uncalibrated limited axes gyroscope sensor. + * + * @see #TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED + * + */ + public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = + "android.sensor.gyroscope_limited_axes_uncalibrated"; + + /** + * A constant string describing a heading sensor. + * + * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. + */ + public static final int TYPE_HEADING = 42; + + /** + * A constant string describing a heading sensor. + * + * @see #TYPE_HEADING + * + */ + public static final String STRING_TYPE_HEADING = "android.sensor.heading"; + + /** * A constant describing all sensor types. */ @@ -846,6 +929,11 @@ public final class Sensor { 6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED 1, // SENSOR_TYPE_HINGE_ANGLE 6, // SENSOR_TYPE_HEAD_TRACKER (discontinuity count is excluded) + 6, // SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES + 6, // SENSOR_TYPE_GYROSCOPE_LIMITED_AXES + 9, // SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED + 9, // SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED + 2, // SENSOR_TYPE_HEADING }; /** @@ -1301,6 +1389,21 @@ public final class Sensor { case TYPE_HEAD_TRACKER: mStringType = STRING_TYPE_HEAD_TRACKER; return true; + case TYPE_ACCELEROMETER_LIMITED_AXES: + mStringType = STRING_TYPE_ACCELEROMETER_LIMITED_AXES; + return true; + case TYPE_GYROSCOPE_LIMITED_AXES: + mStringType = STRING_TYPE_GYROSCOPE_LIMITED_AXES; + return true; + case TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + mStringType = STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED; + return true; + case TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + mStringType = STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED; + return true; + case TYPE_HEADING: + mStringType = STRING_TYPE_HEADING; + return true; default: return false; } diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index c77c8cc635e6..45d4c09921a6 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -676,6 +676,127 @@ public class SensorEvent { * <li> values[5] : Z component of Euler vector representing angular velocity</li> * </ul> * + * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES + * Sensor.TYPE_ACCELEROMETER_LIMITED_AXES}: + * </h4> Equivalent to TYPE_ACCELEROMETER, but supporting cases where one + * or two axes are not supported. + * + * The last three values represent whether the acceleration value for a + * given axis is supported. A value of 1.0 indicates that the axis is + * supported, while a value of 0 means it isn't supported. The supported + * axes should be determined at build time and these values do not change + * during runtime. + * + * The acceleration values for axes that are not supported are set to 0. + * + * Similar to {@link android.hardware.Sensor#TYPE_ACCELEROMETER}. + * + * <ul> + * <li> values[0]: Acceleration minus Gx on the x-axis (if supported)</li> + * <li> values[1]: Acceleration minus Gy on the y-axis (if supported)</li> + * <li> values[2]: Acceleration minus Gz on the z-axis (if supported)</li> + * <li> values[3]: Acceleration supported for x-axis</li> + * <li> values[4]: Acceleration supported for y-axis</li> + * <li> values[5]: Acceleration supported for z-axis</li> + * </ul> + * + * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE_LIMITED_AXES + * Sensor.TYPE_GYROSCOPE_LIMITED_AXES}: + * </h4> Equivalent to TYPE_GYROSCOPE, but supporting cases where one or two + * axes are not supported. + * + * The last three values represent whether the angular speed value for a + * given axis is supported. A value of 1.0 indicates that the axis is + * supported, while a value of 0 means it isn't supported. The supported + * axes should be determined at build time and these values do not change + * during runtime. + * + * The angular speed values for axes that are not supported are set to 0. + * + * Similar to {@link android.hardware.Sensor#TYPE_GYROSCOPE}. + * + * <ul> + * <li> values[0]: Angular speed around the x-axis (if supported)</li> + * <li> values[1]: Angular speed around the y-axis (if supported)</li> + * <li> values[2]: Angular speed around the z-axis (if supported)</li> + * <li> values[3]: Angular speed supported for x-axis</li> + * <li> values[4]: Angular speed supported for y-axis</li> + * <li> values[5]: Angular speed supported for z-axis</li> + * </ul> + * <p> + * + * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED + * Sensor.TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED}: + * </h4> Equivalent to TYPE_ACCELEROMETER_UNCALIBRATED, but supporting cases + * where one or two axes are not supported. + * + * The last three values represent whether the acceleration value for a + * given axis is supported. A value of 1.0 indicates that the axis is + * supported, while a value of 0 means it isn't supported. The supported + * axes should be determined at build time and these values do not change + * during runtime. + * + * The acceleration values and bias values for axes that are not supported + * are set to 0. + * + * <ul> + * <li> values[0]: x_uncalib without bias compensation (if supported)</li> + * <li> values[1]: y_uncalib without bias compensation (if supported)</li> + * <li> values[2]: z_uncalib without bias compensation (if supported)</li> + * <li> values[3]: estimated x_bias (if supported)</li> + * <li> values[4]: estimated y_bias (if supported)</li> + * <li> values[5]: estimated z_bias (if supported)</li> + * <li> values[6]: Acceleration supported for x-axis</li> + * <li> values[7]: Acceleration supported for y-axis</li> + * <li> values[8]: Acceleration supported for z-axis</li> + * </ul> + * </p> + * + * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED + * Sensor.TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED}: + * </h4> Equivalent to TYPE_GYROSCOPE_UNCALIBRATED, but supporting cases + * where one or two axes are not supported. + * + * The last three values represent whether the angular speed value for a + * given axis is supported. A value of 1.0 indicates that the axis is + * supported, while a value of 0 means it isn't supported. The supported + * axes should be determined at build time and these values do not change + * during runtime. + * + * The angular speed values and drift values for axes that are not supported + * are set to 0. + * + * <ul> + * <li> values[0]: Angular speed (w/o drift compensation) around the X axis (if supported)</li> + * <li> values[1]: Angular speed (w/o drift compensation) around the Y axis (if supported)</li> + * <li> values[2]: Angular speed (w/o drift compensation) around the Z axis (if supported)</li> + * <li> values[3]: estimated drift around X axis (if supported)</li> + * <li> values[4]: estimated drift around Y axis (if supported)</li> + * <li> values[5]: estimated drift around Z axis (if supported)</li> + * <li> values[6]: Angular speed supported for x-axis</li> + * <li> values[7]: Angular speed supported for y-axis</li> + * <li> values[8]: Angular speed supported for z-axis</li> + * </ul> + * </p> + * + * <h4>{@link android.hardware.Sensor#TYPE_HEADING Sensor.TYPE_HEADING}:</h4> + * + * A sensor of this type measures the direction in which the device is + * pointing relative to true north in degrees. The value must be between + * 0.0 (inclusive) and 360.0 (exclusive), with 0 indicating north, 90 east, + * 180 south, and 270 west. + * + * Accuracy is defined at 68% confidence. In the case where the underlying + * distribution is assumed Gaussian normal, this would be considered one + * standard deviation. For example, if heading returns 60 degrees, and + * accuracy returns 10 degrees, then there is a 68 percent probability of + * the true heading being between 50 degrees and 70 degrees. + * + * <ul> + * <li> values[0]: Measured heading in degrees.</li> + * <li> values[1]: Heading accuracy in degrees.</li> + * </ul> + * * @see GeomagneticField */ public final float[] values; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index d2dc314585d6..a29bffe6ea93 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -657,7 +657,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata */ - private <TKey> List<TKey> + <TKey> List<TKey> getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags, boolean includeSynthetic) { @@ -2214,6 +2214,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li> + * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE STREAM_USE_CASE}</li> * </ul> * * <p>This key is available on all devices.</p> @@ -2238,6 +2239,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR * @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING * @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT + * @see #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE */ @PublicKey @NonNull @@ -3475,6 +3477,90 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Boolean>("android.scaler.multiResolutionStreamSupported", boolean.class); /** + * <p>The stream use cases supported by this camera device.</p> + * <p>The stream use case indicates the purpose of a particular camera stream from + * the end-user perspective. Some examples of camera use cases are: preview stream for + * live viewfinder shown to the user, still capture for generating high quality photo + * capture, video record for encoding the camera output for the purpose of future playback, + * and video call for live realtime video conferencing.</p> + * <p>With this flag, the camera device can optimize the image processing pipeline + * parameters, such as tuning, sensor mode, and ISP settings, indepedent of + * the properties of the immediate camera output surface. For example, if the output + * surface is a SurfaceTexture, the stream use case flag can be used to indicate whether + * the camera frames eventually go to display, video encoder, + * still image capture, or all of them combined.</p> + * <p>The application sets the use case of a camera stream by calling + * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase }.</p> + * <p>A camera device with + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } + * capability must support the following stream use cases:</p> + * <ul> + * <li>DEFAULT</li> + * <li>PREVIEW</li> + * <li>STILL_CAPTURE</li> + * <li>VIDEO_RECORD</li> + * <li>PREVIEW_VIDEO_STILL</li> + * <li>VIDEO_CALL</li> + * </ul> + * <p>The guaranteed stream combinations related to stream use case for a camera device with + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } + * capability is documented in the camera device + * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline}. The + * application is strongly recommended to use one of the guaranteed stream combintations. + * If the application creates a session with a stream combination not in the guaranteed + * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session, + * the camera device may ignore some stream use cases due to hardware constraints + * and implementation details.</p> + * <p>For stream combinations not covered by the stream use case mandatory lists, such as + * reprocessable session, constrained high speed session, or RAW stream combinations, the + * application should leave stream use cases within the session as DEFAULT.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}</li> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW PREVIEW}</li> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE STILL_CAPTURE}</li> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD VIDEO_RECORD}</li> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL PREVIEW_VIDEO_STILL}</li> + * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL VIDEO_CALL}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL + * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL + */ + @PublicKey + @NonNull + public static final Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES = + new Key<int[]>("android.scaler.availableStreamUseCases", int[].class); + + /** + * <p>An array of mandatory stream combinations with stream use cases. + * This is an app-readable conversion of the mandatory stream combination + * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables} with + * each stream's use case being set.</p> + * <p>The array of + * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is + * generated according to the documented + * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for a + * camera device with + * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } + * capability. + * The mandatory stream combination array will be {@code null} in case the device doesn't + * have {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE } + * capability.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @SyntheticKey + public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS = + new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryUseCaseStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class); + + /** * <p>The area of the image sensor which corresponds to active pixels after any geometric * distortion correction has been applied.</p> * <p>This is the rectangle representing the size of the active region of the sensor (i.e. diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 47eb79d07469..1a42eaf541ca 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -404,7 +404,10 @@ public abstract class CameraDevice implements AutoCloseable { * (output format)/(surface type), or if the extension is not * supported, or if any of the output configurations select * a dynamic range different from - * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD} + * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD}, + * or if any of the output configurations sets a stream use + * case different from {@link + * android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}. * @see CameraExtensionCharacteristics#getSupportedExtensions * @see CameraExtensionCharacteristics#getExtensionSupportedSizes */ @@ -855,6 +858,31 @@ public abstract class CameraDevice implements AutoCloseable { * will cause a capture session initialization failure. * </p> * + * <p>Devices with the STREAM_USE_CASE capability ({@link + * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes {@link + * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}) support below additional + * stream combinations: + * + * <table> + * <tr><th colspan="10">STREAM_USE_CASE capability additional guaranteed configurations</th></tr> + * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple preview or in-app image processing</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video recording or in-app video processing</td> </tr> + * <tr> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple JPEG or YUV still image capture</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream for preview, video and still image capture</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video call</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Preview with JPEG or YUV still image capture</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td>Preview with video recording or in-app video processing</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td>Preview with in-application image processing</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr> + * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream with JPEG or YUV still capture</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr> + * </table><br> + * </p> + * * <p>Since the capabilities of camera devices vary greatly, a given camera device may support * target combinations with sizes outside of these guarantees, but this can only be tested for * by calling {@link #isSessionConfigurationSupported} or attempting to create a session with diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 5c636c7fe2f0..aa98f1fdf9bc 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -30,6 +30,7 @@ import android.hardware.camera2.extension.IInitializeSessionCallback; import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.LatencyRange; import android.hardware.camera2.extension.SizeList; +import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.os.ConditionVariable; @@ -49,6 +50,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -785,8 +787,8 @@ public final class CameraExtensionCharacteristics { if (latencyRange != null) { return new Range(latencyRange.min, latencyRange.max); } - } - } catch (RemoteException e) { + } + } catch (RemoteException e) { Log.e(TAG, "Failed to query the extension capture latency! Extension service does" + " not respond!"); } finally { @@ -795,4 +797,142 @@ public final class CameraExtensionCharacteristics { return null; } + + /** + * Returns the set of keys supported by a {@link CaptureRequest} submitted in a + * {@link CameraExtensionSession} with a given extension type. + * + * <p>The set returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * @param extension the extension type + * + * @return non-modifiable set of capture keys supported by camera extension session initialized + * with the given extension type. + * @throws IllegalArgumentException in case of unsupported extension. + */ + @NonNull + public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) { + long clientId = registerClient(mContext); + if (clientId < 0) { + throw new IllegalArgumentException("Unsupported extensions"); + } + + HashSet<CaptureRequest.Key> ret = new HashSet<>(); + + try { + if (!isExtensionSupported(mCameraId, extension, mChars)) { + throw new IllegalArgumentException("Unsupported extension"); + } + Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = + initializeExtension(extension); + extenders.second.onInit(mCameraId, mChars.getNativeMetadata()); + extenders.second.init(mCameraId, mChars.getNativeMetadata()); + CameraMetadataNative captureRequestMeta = + extenders.second.getAvailableCaptureRequestKeys(); + + if (captureRequestMeta != null) { + int[] requestKeys = captureRequestMeta.get( + CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS); + if (requestKeys == null) { + throw new AssertionError("android.request.availableRequestKeys must be non-null" + + " in the characteristics"); + } + CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta); + + Object crKey = CaptureRequest.Key.class; + Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey; + + ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped, + requestKeys, /*includeSynthetic*/ false)); + } + + // Jpeg quality and orientation must always be supported + if (!ret.contains(CaptureRequest.JPEG_QUALITY)) { + ret.add(CaptureRequest.JPEG_QUALITY); + } + if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) { + ret.add(CaptureRequest.JPEG_ORIENTATION); + } + extenders.second.onDeInit(); + } catch (RemoteException e) { + throw new IllegalStateException("Failed to query the available capture request keys!"); + } finally { + unregisterClient(clientId); + } + + return Collections.unmodifiableSet(ret); + } + + /** + * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to + * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}. + * + * <p>The set returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>In case the set is empty, then the extension is not able to support any capture results + * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable} + * callback will not be fired.</p> + * + * @param extension the extension type + * + * @return non-modifiable set of capture result keys supported by camera extension session + * initialized with the given extension type. + * @throws IllegalArgumentException in case of unsupported extension. + */ + @NonNull + public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) { + long clientId = registerClient(mContext); + if (clientId < 0) { + throw new IllegalArgumentException("Unsupported extensions"); + } + + HashSet<CaptureResult.Key> ret = new HashSet<>(); + try { + if (!isExtensionSupported(mCameraId, extension, mChars)) { + throw new IllegalArgumentException("Unsupported extension"); + } + + Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = + initializeExtension(extension); + extenders.second.onInit(mCameraId, mChars.getNativeMetadata()); + extenders.second.init(mCameraId, mChars.getNativeMetadata()); + CameraMetadataNative captureResultMeta = + extenders.second.getAvailableCaptureResultKeys(); + + if (captureResultMeta != null) { + int[] resultKeys = captureResultMeta.get( + CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS); + if (resultKeys == null) { + throw new AssertionError("android.request.availableResultKeys must be non-null " + + "in the characteristics"); + } + CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta); + Object crKey = CaptureResult.Key.class; + Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey; + + ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped, + resultKeys, /*includeSynthetic*/ false)); + + // Jpeg quality, orientation and sensor timestamp must always be supported + if (!ret.contains(CaptureResult.JPEG_QUALITY)) { + ret.add(CaptureResult.JPEG_QUALITY); + } + if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) { + ret.add(CaptureResult.JPEG_ORIENTATION); + } + if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) { + ret.add(CaptureResult.SENSOR_TIMESTAMP); + } + } + extenders.second.onDeInit(); + } catch (RemoteException e) { + throw new IllegalStateException("Failed to query the available capture result keys!"); + } finally { + unregisterClient(clientId); + } + + return Collections.unmodifiableSet(ret); + } } diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java index 5892f682dd49..ee3441fc16f1 100644 --- a/core/java/android/hardware/camera2/CameraExtensionSession.java +++ b/core/java/android/hardware/camera2/CameraExtensionSession.java @@ -172,6 +172,32 @@ public abstract class CameraExtensionSession implements AutoCloseable { int sequenceId) { // default empty implementation } + + /** + * This method is called when an image capture has fully completed and all the + * result metadata is available. + * + * <p>This callback will only be called in case + * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys} returns a valid + * non-empty list.</p> + * + * <p>The default implementation of this method does nothing.</p> + * + * @param session The session received during + * {@link StateCallback#onConfigured(CameraExtensionSession)} + * @param request The request that was given to the CameraDevice + * @param result The total output metadata from the capture, which only includes the + * capture result keys advertised as supported in + * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys}. + * + * @see #capture + * @see #setRepeatingRequest + * @see CameraExtensionCharacteristics#getAvailableCaptureResultKeys + */ + public void onCaptureResultAvailable(@NonNull CameraExtensionSession session, + @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { + // default empty implementation + } } /** diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 803684da6ddb..95238ee4e6a6 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1210,6 +1210,36 @@ public abstract class CameraMetadata<TKey> { */ public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18; + /** + * <p>The camera device supports selecting a per-stream use case via + * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase } + * so that the device can optimize camera pipeline parameters such as tuning, sensor + * mode, or ISP settings for a specific user scenario. + * Some sample usages of this capability are: + * * Distinguish high quality YUV captures from a regular YUV stream where + * the image quality may not be as good as the JPEG stream, or + * * Use one stream to serve multiple purposes: viewfinder, video recording and + * still capture. This is common with applications that wish to apply edits equally + * to preview, saved images, and saved videos.</p> + * <p>This capability requires the camera device to support the following + * stream use cases: + * * DEFAULT for backward compatibility where the application doesn't set + * a stream use case + * * PREVIEW for live viewfinder and in-app image analysis + * * STILL_CAPTURE for still photo capture + * * VIDEO_RECORD for recording video clips + * * PREVIEW_VIDEO_STILL for one single stream used for viewfinder, video + * recording, and still capture. + * * VIDEO_CALL for long running video calls</p> + * <p>{@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES } + * lists all of the supported stream use cases.</p> + * <p>Refer to {@link android.hardware.camera2.CameraDevice#createCaptureSession } for the + * mandatory stream combinations involving stream use cases, which can also be queried + * via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p> + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + */ + public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19; + // // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP // @@ -1336,6 +1366,89 @@ public abstract class CameraMetadata<TKey> { public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // + // Enumeration values for CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + // + + /** + * <p>Default stream use case.</p> + * <p>This use case is the same as when the application doesn't set any use case for + * the stream. The camera device uses the properties of the output target, such as + * format, dataSpace, or surface class type, to optimize the image processing pipeline.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0x0; + + /** + * <p>Live stream shown to the user.</p> + * <p>Optimized for performance and usability as a viewfinder, but not necessarily for + * image quality. The output is not meant to be persisted as saved images or video.</p> + * <p>No stall if android.control.<em> are set to FAST; may have stall if android.control.</em> + * are set to HIGH_QUALITY. This use case has the same behavior as the default + * SurfaceView and SurfaceTexture targets. Additionally, this use case can be used for + * in-app image analysis.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 0x1; + + /** + * <p>Still photo capture.</p> + * <p>Optimized for high-quality high-resolution capture, and not expected to maintain + * preview-like frame rates.</p> + * <p>The stream may have stalls regardless of whether android.control.* is HIGH_QUALITY. + * This use case has the same behavior as the default JPEG and RAW related formats.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 0x2; + + /** + * <p>Recording video clips.</p> + * <p>Optimized for high-quality video capture, including high-quality image stabilization + * if supported by the device and enabled by the application. As a result, may produce + * output frames with a substantial lag from real time, to allow for highest-quality + * stabilization or other processing. As such, such an output is not suitable for drawing + * to screen directly, and is expected to be persisted to disk or similar for later + * playback or processing. Only streams that set the VIDEO_RECORD use case are guaranteed + * to have video stabilization applied when the video stabilization control is set + * to ON, as opposed to PREVIEW_STABILIZATION.</p> + * <p>This use case has the same behavior as the default MediaRecorder and MediaCodec + * targets.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 0x3; + + /** + * <p>One single stream used for combined purposes of preview, video, and still capture.</p> + * <p>For such multi-purpose streams, the camera device aims to make the best tradeoff + * between the individual use cases. For example, the STILL_CAPTURE use case by itself + * may have stalls for achieving best image quality. But if combined with PREVIEW and + * VIDEO_RECORD, the camera device needs to trade off the additional image processing + * for speed so that preview and video recording aren't slowed down.</p> + * <p>Similarly, VIDEO_RECORD may produce frames with a substantial lag, but + * PREVIEW_VIDEO_STILL must have minimal output delay. This means that to enable video + * stabilization with this use case, the device must support and the app must select the + * PREVIEW_STABILIZATION mode for video stabilization.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 0x4; + + /** + * <p>Long-running video call optimized for both power efficienty and video quality.</p> + * <p>The camera sensor may run in a lower-resolution mode to reduce power consumption + * at the cost of some image and digital zoom quality. Unlike VIDEO_RECORD, VIDEO_CALL + * outputs are expected to work in dark conditions, so are usually accompanied with + * variable frame rate settings to allow sufficient exposure time in low light.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 0x5; + + /** + * <p>Vendor defined use cases. These depend on the vendor implementation.</p> + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + * @hide + */ + public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START = 0x10000; + + // // Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT // diff --git a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl index 022b084f613b..3c5f5ff63a34 100644 --- a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl +++ b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl @@ -17,6 +17,7 @@ package android.hardware.camera2.extension; import android.view.Surface; import android.hardware.camera2.extension.CaptureBundle; +import android.hardware.camera2.extension.IProcessResultImpl; import android.hardware.camera2.extension.Size; /** @hide */ @@ -25,5 +26,5 @@ interface ICaptureProcessorImpl void onOutputSurface(in Surface surface, int imageFormat); void onResolutionUpdate(in Size size); void onImageFormatUpdate(int imageFormat); - void process(in List<CaptureBundle> capturelist); + void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback); } diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl index 3ebf63793b79..a8a7866e5ca4 100644 --- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl @@ -39,4 +39,6 @@ interface IImageCaptureExtenderImpl int getMaxCaptureStage(); @nullable List<SizeList> getSupportedResolutions(); LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize); + CameraMetadataNative getAvailableCaptureRequestKeys(); + CameraMetadataNative getAvailableCaptureResultKeys(); } diff --git a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl index f7e4023c5067..ecd098be9d3b 100644 --- a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl +++ b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl @@ -17,6 +17,7 @@ package android.hardware.camera2.extension; import android.hardware.camera2.impl.CameraMetadataNative; import android.view.Surface; +import android.hardware.camera2.extension.IProcessResultImpl; import android.hardware.camera2.extension.ParcelImage; import android.hardware.camera2.extension.Size; @@ -26,5 +27,6 @@ interface IPreviewImageProcessorImpl void onOutputSurface(in Surface surface, int imageFormat); void onResolutionUpdate(in Size size); void onImageFormatUpdate(int imageFormat); - void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId); + void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId, + in IProcessResultImpl resultCallback); } diff --git a/core/java/android/net/DhcpResults.aidl b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl index f4db3c366d1e..4114edb37a8c 100644 --- a/core/java/android/net/DhcpResults.aidl +++ b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2012, The Android Open Source Project + * 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. @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.hardware.camera2.extension; -package android.net; +import android.hardware.camera2.impl.CameraMetadataNative; -parcelable DhcpResults; +/** @hide */ +interface IProcessResultImpl +{ + void onCaptureCompleted(long shutterTimestamp, in CameraMetadataNative results); +} diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 9d2c901ed049..cd392ced2081 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -105,7 +105,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes @RequiresPermission(android.Manifest.permission.CAMERA) public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession( @NonNull CameraDevice cameraDevice, @NonNull Context ctx, - @NonNull ExtensionSessionConfiguration config) + @NonNull ExtensionSessionConfiguration config, int sessionId) throws CameraAccessException, RemoteException { long clientId = CameraExtensionCharacteristics.registerClient(ctx); if (clientId < 0) { @@ -135,6 +135,11 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes throw new IllegalArgumentException("Unsupported dynamic range profile: " + c.getDynamicRangeProfile()); } + if (c.getStreamUseCase() != + CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) { + throw new IllegalArgumentException("Unsupported stream use case: " + + c.getStreamUseCase()); + } } int suitableSurfaceCount = 0; diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 2920e670f15b..87553d8c42ab 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -30,9 +30,11 @@ import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; import android.os.ConditionVariable; import android.util.Range; +import android.util.Log; import android.view.Surface; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -56,6 +58,7 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl private final CameraCharacteristics mCharacteristics; private final CameraCaptureSessionImpl mSessionImpl; private final ConditionVariable mInitialized = new ConditionVariable(); + private final String TAG = "CameraConstrainedHighSpeedCaptureSessionImpl"; /** * Create a new CameraCaptureSession. @@ -95,10 +98,33 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl StreamConfigurationMap config = mCharacteristics.get(ck); SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config); - // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize - // the preview frame rate, should use maxBatch size for that high speed stream - // configuration. We choose the former for now. - int requestListSize = fpsRange.getUpper() / 30; + // Check the high speed video fps ranges for video size and find the min value from the list + // and assign it to previewFps which will be used to calculate the requestList size. + Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRangesFor( + SurfaceUtils.getSurfaceSize(outputSurfaces.iterator().next())); + Log.v(TAG, "High speed fps ranges: " + Arrays.toString(highSpeedFpsRanges)); + int previewFps = Integer.MAX_VALUE; + for (Range<Integer> range : highSpeedFpsRanges) { + int rangeMin = range.getLower(); + if (previewFps > rangeMin) { + previewFps = rangeMin; + } + } + // Since we only want to support 60fps apart from 30fps, if the min value is not 60, + // then continue to calculate the requestList size using value 30. + if (previewFps != 60 && previewFps != 30) { + Log.w(TAG, "previewFps is neither 60 nor 30."); + previewFps = 30; + } + Log.v(TAG, "previewFps: " + previewFps); + + int requestListSize = fpsRange.getUpper() / previewFps; + // If it's a preview, keep requestList size fixed = 1. + if (fpsRange.getUpper() > fpsRange.getLower()) { + requestListSize = 1; + } + + Log.v(TAG, "Request list size is: " + requestListSize); List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); // Prepare the Request builders: need carry over the request controls. diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 9b19fc4d3ef2..3cb0c93d8409 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -2495,10 +2495,10 @@ public class CameraDeviceImpl extends CameraDevice if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) { mCurrentAdvancedExtensionSession = CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession( - this, mContext, extensionConfiguration); + this, mContext, extensionConfiguration, mNextSessionId++); } else { mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession( - this, mContext, extensionConfiguration); + this, mContext, extensionConfiguration, mNextSessionId++); } } catch (RemoteException e) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR); diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java index bf4593260a70..d148d87fc9d8 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java @@ -19,6 +19,7 @@ package android.hardware.camera2.impl; import android.annotation.SuppressLint; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.extension.IPreviewImageProcessorImpl; +import android.hardware.camera2.extension.IProcessResultImpl; import android.hardware.camera2.extension.ParcelImage; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; @@ -114,12 +115,12 @@ public class CameraExtensionForwardProcessor { } } - public void process(ParcelImage image, TotalCaptureResult totalCaptureResult) - throws RemoteException { + public void process(ParcelImage image, TotalCaptureResult totalCaptureResult, + IProcessResultImpl resultCallback) throws RemoteException { if ((mIntermediateSurface != null) && (mIntermediateSurface.isValid()) && !mOutputAbandoned) { mProcessor.process(image, totalCaptureResult.getNativeMetadata(), - totalCaptureResult.getSequenceId()); + totalCaptureResult.getSequenceId(), resultCallback); } } diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java index 425f22c31306..1514a2be5de8 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java @@ -24,6 +24,7 @@ import android.graphics.ImageFormat; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.extension.CaptureBundle; import android.hardware.camera2.extension.ICaptureProcessorImpl; +import android.hardware.camera2.extension.IProcessResultImpl; import android.media.Image; import android.media.Image.Plane; import android.media.ImageReader; @@ -183,11 +184,13 @@ public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl { int cropLeft, int cropTop, int cropRight, int cropBottom, int rot90); - public void process(List<CaptureBundle> captureBundle) throws RemoteException { + @Override + public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback) + throws RemoteException { JpegParameters jpegParams = getJpegParameters(captureBundle); try { mJpegParameters.add(jpegParams); - mProcessor.process(captureBundle); + mProcessor.process(captureBundle, captureCallback); } catch (Exception e) { mJpegParameters.remove(jpegParams); throw e; diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index c8ecfd0bdea9..a50db57e48cd 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -36,6 +36,7 @@ import android.hardware.camera2.extension.ICaptureProcessorImpl; import android.hardware.camera2.extension.IImageCaptureExtenderImpl; import android.hardware.camera2.extension.IInitializeSessionCallback; import android.hardware.camera2.extension.IPreviewExtenderImpl; +import android.hardware.camera2.extension.IProcessResultImpl; import android.hardware.camera2.extension.IRequestUpdateProcessorImpl; import android.hardware.camera2.extension.ParcelImage; import android.hardware.camera2.TotalCaptureResult; @@ -67,6 +68,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; public final class CameraExtensionSessionImpl extends CameraExtensionSession { @@ -83,6 +85,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private final StateCallback mCallbacks; private final List<Size> mSupportedPreviewSizes; private final InitializeSessionHandler mInitializeHandler; + private final int mSessionId; + private final Set<CaptureRequest.Key> mSupportedRequestKeys; + private final Set<CaptureResult.Key> mSupportedResultKeys; + private boolean mCaptureResultsSupported; private CameraCaptureSession mCaptureSession = null; private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface; @@ -121,7 +127,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { public static CameraExtensionSessionImpl createCameraExtensionSession( @NonNull CameraDevice cameraDevice, @NonNull Context ctx, - @NonNull ExtensionSessionConfiguration config) + @NonNull ExtensionSessionConfiguration config, + int sessionId) throws CameraAccessException, RemoteException { long clientId = CameraExtensionCharacteristics.registerClient(ctx); if (clientId < 0) { @@ -151,6 +158,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { throw new IllegalArgumentException("Unsupported dynamic range profile: " + c.getDynamicRangeProfile()); } + if (c.getStreamUseCase() != + CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) { + throw new IllegalArgumentException("Unsupported stream use case: " + + c.getStreamUseCase()); + } } Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders = @@ -197,7 +209,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { repeatingRequestSurface, burstCaptureSurface, config.getStateCallback(), - config.getExecutor()); + config.getExecutor(), + sessionId, + extensionChars.getAvailableCaptureRequestKeys(config.getExtension()), + extensionChars.getAvailableCaptureResultKeys(config.getExtension())); session.initialize(); @@ -212,7 +227,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface, @NonNull StateCallback callback, - @NonNull Executor executor) { + @NonNull Executor executor, + int sessionId, + @NonNull Set<CaptureRequest.Key> requestKeys, + @Nullable Set<CaptureResult.Key> resultKeys) { mExtensionClientId = extensionClientId; mImageExtender = imageExtender; mPreviewExtender = previewExtender; @@ -227,6 +245,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mHandler = new Handler(mHandlerThread.getLooper()); mInitialized = false; mInitializeHandler = new InitializeSessionHandler(); + mSessionId = sessionId; + mSupportedRequestKeys = requestKeys; + mSupportedResultKeys = resultKeys; + mCaptureResultsSupported = !resultKeys.isEmpty(); } private void initializeRepeatingRequestPipeline() throws RemoteException { @@ -483,7 +505,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return captureStageList; } - private static List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice, + private List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice, List<CaptureStageImpl> captureStageList, CaptureRequest clientRequest, Surface target, int captureTemplate, Map<CaptureRequest, Integer> captureMap) { CaptureRequest.Builder requestBuilder; @@ -495,16 +517,13 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return null; } - // This will override the extension capture stage jpeg parameters with the user set - // jpeg quality and rotation. This will guarantee that client configured jpeg - // parameters always have highest priority. - Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION); - if (jpegRotation != null) { - captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation); - } - Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY); - if (jpegQuality != null) { - captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality); + // This will guarantee that client configured + // parameters always have the highest priority. + for (CaptureRequest.Key requestKey : mSupportedRequestKeys){ + Object value = clientRequest.get(requestKey); + if (value != null) { + captureStage.parameters.set(requestKey, value); + } } requestBuilder.addTarget(target); @@ -517,10 +536,9 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return ret; } - private static CaptureRequest createRequest(CameraDevice cameraDevice, - List<CaptureStageImpl> captureStageList, - Surface target, - int captureTemplate) throws CameraAccessException { + private CaptureRequest createRequest(CameraDevice cameraDevice, + List<CaptureStageImpl> captureStageList, Surface target, int captureTemplate, + CaptureRequest clientRequest) throws CameraAccessException { CaptureRequest.Builder requestBuilder; requestBuilder = cameraDevice.createCaptureRequest(captureTemplate); if (target != null) { @@ -528,14 +546,35 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } CaptureRequest ret = requestBuilder.build(); + CameraMetadataNative nativeMeta = ret.getNativeMetadata(); for (CaptureStageImpl captureStage : captureStageList) { if (captureStage != null) { - CameraMetadataNative.update(ret.getNativeMetadata(), captureStage.parameters); + CameraMetadataNative.update(nativeMeta, captureStage.parameters); + } + } + + if (clientRequest != null) { + // This will guarantee that client configured + // parameters always have the highest priority. + for (CaptureRequest.Key requestKey : mSupportedRequestKeys) { + Object value = clientRequest.get(requestKey); + if (value != null) { + nativeMeta.set(requestKey, value); + } } } + return ret; } + private CaptureRequest createRequest(CameraDevice cameraDevice, + List<CaptureStageImpl> captureStageList, + Surface target, + int captureTemplate) throws CameraAccessException { + return createRequest(cameraDevice, captureStageList, target, captureTemplate, + /*clientRequest*/ null); + } + @Override public int capture(@NonNull CaptureRequest request, @NonNull Executor executor, @@ -629,12 +668,17 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } private int setRepeatingRequest(CaptureStageImpl captureStage, - CameraCaptureSession.CaptureCallback requestHandler) + CameraCaptureSession.CaptureCallback requestHandler) throws CameraAccessException { + return setRepeatingRequest(captureStage, requestHandler, /*clientRequest*/ null); + } + + private int setRepeatingRequest(CaptureStageImpl captureStage, + CameraCaptureSession.CaptureCallback requestHandler, CaptureRequest clientRequest) throws CameraAccessException { ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>(); captureStageList.add(captureStage); - CaptureRequest repeatingRequest = createRequest(mCameraDevice, - captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW); + CaptureRequest repeatingRequest = createRequest(mCameraDevice, captureStageList, + mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, clientRequest); return mCaptureSession.setSingleRepeatingRequest(repeatingRequest, new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler); } @@ -843,6 +887,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private ImageCallback mImageCallback = null; private boolean mCaptureFailed = false; + private CaptureResultHandler mCaptureResultHandler = null; public BurstRequestHandler(@NonNull CaptureRequest request, @NonNull Executor executor, @NonNull ExtensionCaptureCallback callbacks, @@ -963,20 +1008,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); if (timestamp != null) { + if (mCaptureResultsSupported && (mCaptureResultHandler == null)) { + mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor, + mCallbacks, result.getSessionId()); + } if (mImageProcessor != null) { if (mCapturePendingMap.indexOfKey(timestamp) >= 0) { Image img = mCapturePendingMap.get(timestamp).first; - mCaptureStageMap.put(stageId, - new Pair<>(img, - result)); + mCaptureStageMap.put(stageId, new Pair<>(img, result)); checkAndFireBurstProcessing(); } else { - mCapturePendingMap.put(timestamp, - new Pair<>(null, - stageId)); - mCaptureStageMap.put(stageId, - new Pair<>(null, - result)); + mCapturePendingMap.put(timestamp, new Pair<>(null, stageId)); + mCaptureStageMap.put(stageId, new Pair<>(null, result)); } } else { mCaptureRequestMap.clear(); @@ -986,6 +1029,18 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { () -> mCallbacks .onCaptureProcessStarted(CameraExtensionSessionImpl.this, mClientRequest)); + + if (mCaptureResultHandler != null) { + CameraMetadataNative captureResults = new CameraMetadataNative(); + for (CaptureResult.Key key : mSupportedResultKeys) { + Object value = result.get(key); + if (value != null) { + captureResults.set(key, value); + } + } + mCaptureResultHandler.onCaptureCompleted(timestamp, + captureResults); + } } finally { Binder.restoreCallingIdentity(ident); } @@ -1013,7 +1068,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap, jpegOrientation, jpegQuality); try { - mImageProcessor.process(captureList); + mImageProcessor.process(captureList, mCaptureResultHandler); } catch (RemoteException e) { Log.e(TAG, "Failed to process multi-frame request! Extension service " + "does not respond!"); @@ -1228,6 +1283,43 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } } + private class CaptureResultHandler extends IProcessResultImpl.Stub { + private final Executor mExecutor; + private final ExtensionCaptureCallback mCallbacks; + private final CaptureRequest mClientRequest; + private final int mRequestId; + + public CaptureResultHandler(@NonNull CaptureRequest clientRequest, + @NonNull Executor executor, @NonNull ExtensionCaptureCallback listener, + int requestId) { + mClientRequest = clientRequest; + mExecutor = executor; + mCallbacks = listener; + mRequestId = requestId; + } + + @Override + public void onCaptureCompleted(long shutterTimestamp, CameraMetadataNative result) { + if (result == null) { + Log.e(TAG,"Invalid capture result!"); + return; + } + + result.set(CaptureResult.SENSOR_TIMESTAMP, shutterTimestamp); + TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result, + mClientRequest, mRequestId, shutterTimestamp, new ArrayList<CaptureResult>(), + mSessionId, new PhysicalCaptureResultInfo[0]); + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute( + () -> mCallbacks.onCaptureResultAvailable(CameraExtensionSessionImpl.this, + mClientRequest, totalResult)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + // This handler can operate in two modes: // 1) Using valid client callbacks, which means camera buffers will be propagated the // registered output surfaces and clients will be notified accordingly. @@ -1242,6 +1334,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private OnImageAvailableListener mImageCallback = null; private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap = new LongSparseArray<>(); + private CaptureResultHandler mCaptureResultHandler = null; private boolean mRequestUpdatedNeeded = false; @@ -1375,6 +1468,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { synchronized (mInterfaceLock) { final Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP); if (timestamp != null) { + if (mCaptureResultsSupported && mClientNotificationsEnabled && + (mCaptureResultHandler == null)) { + mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor, + mCallbacks, result.getSessionId()); + } if (mPreviewProcessorType == IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) { CaptureStageImpl captureStage = null; @@ -1387,7 +1485,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } if (captureStage != null) { try { - setRepeatingRequest(captureStage, this); + setRepeatingRequest(captureStage, this, request); mRequestUpdatedNeeded = true; } catch (IllegalStateException e) { // This is possible in case the camera device closes and the @@ -1406,7 +1504,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { ParcelImage parcelImage = initializeParcelImage( mPendingResultMap.get(timestamp).first); try { - mPreviewImageProcessor.process(parcelImage, result); + mPreviewImageProcessor.process(parcelImage, result, + mCaptureResultHandler); } catch (RemoteException e) { processStatus = false; Log.e(TAG, "Extension service does not respond during " + @@ -1444,6 +1543,19 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { .onCaptureProcessStarted( CameraExtensionSessionImpl.this, mClientRequest)); + if ((mCaptureResultHandler != null) && (mPreviewProcessorType != + IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) { + CameraMetadataNative captureResults = + new CameraMetadataNative(); + for (CaptureResult.Key key : mSupportedResultKeys) { + Object value = result.get(key); + if (value != null) { + captureResults.set(key, value); + } + } + mCaptureResultHandler.onCaptureCompleted(timestamp, + captureResults); + } } else { mExecutor.execute( () -> mCallbacks @@ -1587,7 +1699,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { ParcelImage parcelImage = initializeParcelImage(img); try { mPreviewImageProcessor.process(parcelImage, - mPendingResultMap.get(timestamp).second); + mPendingResultMap.get(timestamp).second, mCaptureResultHandler); } catch (RemoteException e) { processStatus = false; Log.e(TAG, "Extension service does not respond during " + diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 0f8bdf64e132..9b67633fe72b 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -333,6 +333,7 @@ public class CameraMetadataNative implements Parcelable { private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1; private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2; private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3; + private static final int MANDATORY_STREAM_CONFIGURATIONS_USE_CASE = 4; private static String translateLocationProviderToProcess(final String provider) { if (provider == null) { @@ -700,6 +701,16 @@ public class CameraMetadataNative implements Parcelable { }); sGetCommandMap.put( + CameraCharacteristics.SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS.getNativeKey(), + new GetCommand() { + @Override + @SuppressWarnings("unchecked") + public <T> T getValue(CameraMetadataNative metadata, Key<T> key) { + return (T) metadata.getMandatoryUseCaseStreamCombinations(); + } + }); + + sGetCommandMap.put( CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() { @Override @SuppressWarnings("unchecked") @@ -1413,6 +1424,9 @@ public class CameraMetadataNative implements Parcelable { case MANDATORY_STREAM_CONFIGURATIONS_10BIT: combs = build.getAvailableMandatory10BitStreamCombinations(); break; + case MANDATORY_STREAM_CONFIGURATIONS_USE_CASE: + combs = build.getAvailableMandatoryStreamUseCaseCombinations(); + break; default: combs = build.getAvailableMandatoryStreamCombinations(); } @@ -1446,6 +1460,10 @@ public class CameraMetadataNative implements Parcelable { return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_DEFAULT); } + private MandatoryStreamCombination[] getMandatoryUseCaseStreamCombinations() { + return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_USE_CASE); + } + private StreamConfigurationMap getStreamConfigurationMap() { StreamConfiguration[] configurations = getBase( CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 32c15da4a909..0d93c985b793 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -26,6 +26,9 @@ import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.OutputConfiguration.StreamUseCase; +import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.HashCodeHelpers; import android.media.CamcorderProfile; import android.util.Log; @@ -63,6 +66,7 @@ public final class MandatoryStreamCombination { private final boolean mIsUltraHighResolution; private final boolean mIsMaximumSize; private final boolean mIs10BitCapable; + private final int mStreamUseCase; /** * Create a new {@link MandatoryStreamInformation}. @@ -141,6 +145,30 @@ public final class MandatoryStreamCombination { public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format, boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution, boolean is10BitCapable) { + this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution, + is10BitCapable, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT); + } + + /** + * Create a new {@link MandatoryStreamInformation}. + * + * @param availableSizes List of possible stream sizes. + * @param format Image format. + * @param isMaximumSize Whether this is a maximum size stream. + * @param isInput Flag indicating whether this stream is input. + * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution + * stream. + * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit + * @param streamUseCase The stream use case. + * + * @throws IllegalArgumentException + * if sizes is empty or if the format was not user-defined in + * ImageFormat/PixelFormat. + * @hide + */ + public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format, + boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution, + boolean is10BitCapable, @StreamUseCase int streamUseCase) { if (availableSizes.isEmpty()) { throw new IllegalArgumentException("No available sizes"); } @@ -150,6 +178,7 @@ public final class MandatoryStreamCombination { mIsInput = isInput; mIsUltraHighResolution = isUltraHighResolution; mIs10BitCapable = is10BitCapable; + mStreamUseCase = streamUseCase; } /** @@ -272,6 +301,20 @@ public final class MandatoryStreamCombination { } /** + * Retrieve the mandatory stream use case. + * + * <p>If this {@link MandatoryStreamInformation} is part of a mandatory stream + * combination for stream use cases, the return value will be a non-DEFAULT value. + * For {@link MandatoryStreamInformation} belonging to other mandatory stream + * combinations, the return value will be DEFAULT. </p> + * + * @return the integer stream use case. + */ + public @StreamUseCase int getStreamUseCase() { + return mStreamUseCase; + } + + /** * Check if this {@link MandatoryStreamInformation} is equal to another * {@link MandatoryStreamInformation}. * @@ -292,6 +335,7 @@ public final class MandatoryStreamCombination { final MandatoryStreamInformation other = (MandatoryStreamInformation) obj; if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) || (mIsUltraHighResolution != other.mIsUltraHighResolution) || + (mStreamUseCase != other.mStreamUseCase) || (mAvailableSizes.size() != other.mAvailableSizes.size())) { return false; } @@ -308,7 +352,8 @@ public final class MandatoryStreamCombination { @Override public int hashCode() { return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput), - Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode()); + Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode(), + mStreamUseCase); } } @@ -316,6 +361,21 @@ public final class MandatoryStreamCombination { private final boolean mIsReprocessable; private final ArrayList<MandatoryStreamInformation> mStreamsInformation = new ArrayList<MandatoryStreamInformation>(); + + /** + * Short hand for stream use cases + */ + private static final int STREAM_USE_CASE_PREVIEW = + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW; + private static final int STREAM_USE_CASE_STILL_CAPTURE = + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE; + private static final int STREAM_USE_CASE_RECORD = + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD; + private static final int STREAM_USE_CASE_PREVIEW_VIDEO_STILL = + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL; + private static final int STREAM_USE_CASE_VIDEO_CALL = + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL; + /** * Create a new {@link MandatoryStreamCombination}. * @@ -411,15 +471,15 @@ public final class MandatoryStreamCombination { private static final class StreamTemplate { public int mFormat; public SizeThreshold mSizeThreshold; - public boolean mIsInput; + public int mStreamUseCase; public StreamTemplate(int format, SizeThreshold sizeThreshold) { - this(format, sizeThreshold, /*isInput*/false); + this(format, sizeThreshold, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT); } public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold, - boolean isInput) { + @StreamUseCase int streamUseCase) { mFormat = format; mSizeThreshold = sizeThreshold; - mIsInput = isInput; + mStreamUseCase = streamUseCase; } } @@ -1034,6 +1094,174 @@ public final class MandatoryStreamCombination { "High-resolution recording with video snapshot"), }; + private static StreamCombinationTemplate sStreamUseCaseCombinations[] = { + // Single stream combinations + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW) }, + "Simple preview"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW) }, + "Simple in-application image processing"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD) }, + "Simple video recording"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD) }, + "Simple in-application video processing"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE) }, + "Simple JPEG still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE) }, + "Simple YUV still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL) }, + "Multi-purpose stream for preview, video and still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL) }, + "Multi-purpose YUV stream for preview, video and still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p, + STREAM_USE_CASE_VIDEO_CALL) }, + "Simple video call"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p, + STREAM_USE_CASE_VIDEO_CALL) }, + "Simple YUV video call"), + + // 2-stream combinations + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview with JPEG still image capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview with YUV still image capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD)}, + "Preview with video recording"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD)}, + "Preview with in-application video processing"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW)}, + "Preview with in-application image processing"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p, + STREAM_USE_CASE_VIDEO_CALL)}, + "Preview with video call"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p, + STREAM_USE_CASE_VIDEO_CALL)}, + "Preview with YUV video call"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Multi-purpose stream with JPEG still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Multi-purpose stream with YUV still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Multi-purpose YUV stream with JPEG still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p, + STREAM_USE_CASE_PREVIEW_VIDEO_STILL), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Multi-purpose YUV stream with YUV still capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, + STREAM_USE_CASE_STILL_CAPTURE), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "YUV and JPEG concurrent still image capture (for testing)"), + + // 3-stream combinations + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, video record and JPEG video snapshot"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, video record and YUV video snapshot"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, in-application video processing and JPEG video snapshot"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_RECORD), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, in-application video processing and YUV video snapshot"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, in-application image processing, and JPEG still image capture"), + new StreamCombinationTemplate(new StreamTemplate [] { + new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW, + STREAM_USE_CASE_PREVIEW), + new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM, + STREAM_USE_CASE_STILL_CAPTURE)}, + "Preview, in-application image processing, and YUV still image capture"), + }; + /** * Helper builder class to generate a list of available mandatory stream combinations. * @hide @@ -1153,6 +1381,76 @@ public final class MandatoryStreamCombination { } /** + * Retrieve a list of all available mandatory stream combinations with stream use cases. + * when the camera device has {@link + * CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability. + * + * @return a non-modifiable list of supported mandatory stream combinations with stream + * use cases. Null in case the device doesn't have {@link + * CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} + * capability. + */ + public @NonNull List<MandatoryStreamCombination> + getAvailableMandatoryStreamUseCaseCombinations() { + if (!isCapabilitySupported( + CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) { + return null; + } + + HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes = + enumerateAvailableSizes(); + if (availableSizes == null) { + Log.e(TAG, "Available size enumeration failed!"); + return null; + } + + ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>(); + availableStreamCombinations.ensureCapacity(sStreamUseCaseCombinations.length); + for (StreamCombinationTemplate combTemplate : sStreamUseCaseCombinations) { + ArrayList<MandatoryStreamInformation> streamsInfo = + new ArrayList<MandatoryStreamInformation>(); + streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length); + + for (StreamTemplate template : combTemplate.mStreamTemplates) { + List<Size> sizes = null; + Pair<SizeThreshold, Integer> pair; + pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold, + new Integer(template.mFormat)); + sizes = availableSizes.get(pair); + + MandatoryStreamInformation streamInfo; + boolean isMaximumSize = + (template.mSizeThreshold == SizeThreshold.MAXIMUM); + try { + streamInfo = new MandatoryStreamInformation(sizes, template.mFormat, + isMaximumSize, /*isInput*/false, /*isUltraHighResolution*/false, + /*is10BitCapable*/ false, template.mStreamUseCase); + } catch (IllegalArgumentException e) { + Log.e(TAG, "No available sizes found for format: " + template.mFormat + + " size threshold: " + template.mSizeThreshold + " combination: " + + combTemplate.mDescription); + return null; + } + streamsInfo.add(streamInfo); + } + + MandatoryStreamCombination streamCombination; + try { + streamCombination = new MandatoryStreamCombination(streamsInfo, + combTemplate.mDescription, /*isReprocessable*/ false); + } catch (IllegalArgumentException e) { + Log.e(TAG, "No stream information for mandatory combination: " + + combTemplate.mDescription); + return null; + } + + availableStreamCombinations.add(streamCombination); + } + + return Collections.unmodifiableList(availableStreamCombinations); + } + + /** * Retrieve a list of all available mandatory concurrent stream combinations. * This method should only be called for devices which are listed in combinations returned * by CameraManager.getConcurrentCameraIds. @@ -1632,6 +1930,8 @@ public final class MandatoryStreamCombination { Size recordingMaxSize = new Size(0, 0); Size previewMaxSize = new Size(0, 0); Size vgaSize = new Size(640, 480); + Size s720pSize = new Size(1280, 720); + Size s1440pSize = new Size(1920, 1440); // For external camera, or hidden physical camera, CamcorderProfile may not be // available, so get maximum recording size using stream configuration map. if (isExternalCamera() || mIsHiddenPhysicalCamera) { @@ -1682,6 +1982,12 @@ public final class MandatoryStreamCombination { pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat); availableSizes.put(pair, Arrays.asList(sizes)); + + pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s720p, intFormat); + availableSizes.put(pair, getSizesWithinBound(sizes, s720pSize)); + + pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s1440p, intFormat); + availableSizes.put(pair, getSizesWithinBound(sizes, s1440pSize)); } return availableSizes; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index f2b881ba7758..d8295c962154 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -159,6 +159,17 @@ public final class OutputConfiguration implements Parcelable { CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}) public @interface SensorPixelMode {}; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"STREAM_USE_CASE_"}, value = + {CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT, + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW, + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE, + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD, + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL, + CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL}) + public @interface StreamUseCase {}; + /** * Create a new {@link OutputConfiguration} instance with a {@link Surface}. * @@ -355,6 +366,7 @@ public final class OutputConfiguration implements Parcelable { mIsMultiResolution = false; mSensorPixelModesUsed = new ArrayList<Integer>(); mDynamicRangeProfile = DynamicRangeProfiles.STANDARD; + mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; } /** @@ -453,6 +465,7 @@ public final class OutputConfiguration implements Parcelable { mIsMultiResolution = false; mSensorPixelModesUsed = new ArrayList<Integer>(); mDynamicRangeProfile = DynamicRangeProfiles.STANDARD; + mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; } /** @@ -730,6 +743,73 @@ public final class OutputConfiguration implements Parcelable { } /** + * Set stream use case for this OutputConfiguration + * + * <p>Stream use case is used to describe the purpose of the stream, whether it's for live + * preview, still image capture, video recording, or their combinations. This flag is useful + * for scenarios where the immediate consumer target isn't sufficient to indicate the stream's + * usage.</p> + * + * <p>The main difference beteween stream use case and capture intent is that the former + * enables the camera device to optimize camera hardware and software pipelines based on user + * scenarios for each stream, whereas the latter is mainly a hint to camera to decide + * optimal 3A strategy that's applicable to the whole session. The camera device carries out + * configurations such as selecting tuning parameters, choosing camera sensor mode, and + * constructing image processing pipeline based on the streams's use cases. Capture intents are + * then used to fine tune 3A behaviors such as adjusting AE/AF convergence speed, and capture + * intents may change during the lifetime of a session. For example, for a session with a + * PREVIEW_VIDEO_STILL use case stream and a STILL_CAPTURE use case stream, the capture intents + * may be PREVIEW with fast 3A convergence speed and flash metering with automatic control for + * live preview, STILL_CAPTURE with best 3A parameters for still photo capture, or VIDEO_RECORD + * with slower 3A convergence speed for better video playback experience.</p> + * + * <p>The supported stream use cases supported by a camera device can be queried by + * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}.</p> + * + * <p>The mandatory stream combinations involving stream use cases can be found at {@link + * android.hardware.camera2.CameraDevice#createCaptureSession}, as well as queried via + * {@link android.hardware.camera2.params.MandatoryStreamCombination}. The application is + * strongly recommended to select one of the guaranteed stream combinations where all streams' + * use cases are set to non-DEFAULT values. If the application chooses a stream combination + * not in the mandatory list, the camera device may ignore some use case flags due to + * hardware constraints or implementation details.</p> + * + * <p>This function must be called before {@link CameraDevice#createCaptureSession} or {@link + * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after + * {@link CameraDevice#createCaptureSession} or + * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect to the camera + * session.</p> + * + * @param streamUseCase The stream use case to be set. + * + * @throws IllegalArgumentException If the streamUseCase isn't within the range of valid + * values. + */ + public void setStreamUseCase(@StreamUseCase int streamUseCase) { + // Verify that the value is in range + int maxUseCaseValue = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL; + if (streamUseCase > maxUseCaseValue && + streamUseCase < CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START) { + throw new IllegalArgumentException("Not a valid stream use case value " + + streamUseCase); + } + + mStreamUseCase = streamUseCase; + } + + /** + * Get the current stream use case + * + * <p>If no {@link #setStreamUseCase} is called first, this function returns + * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}.</p> + * + * @return the currently set stream use case + */ + public int getStreamUseCase() { + return mStreamUseCase; + } + + /** * Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration} * instance. * @@ -756,6 +836,7 @@ public final class OutputConfiguration implements Parcelable { this.mIsMultiResolution = other.mIsMultiResolution; this.mSensorPixelModesUsed = other.mSensorPixelModesUsed; this.mDynamicRangeProfile = other.mDynamicRangeProfile; + this.mStreamUseCase = other.mStreamUseCase; } /** @@ -774,6 +855,8 @@ public final class OutputConfiguration implements Parcelable { String physicalCameraId = source.readString(); boolean isMultiResolutionOutput = source.readInt() == 1; int[] sensorPixelModesUsed = source.createIntArray(); + int streamUseCase = source.readInt(); + checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); int dynamicRangeProfile = source.readInt(); DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile); @@ -801,6 +884,7 @@ public final class OutputConfiguration implements Parcelable { mIsMultiResolution = isMultiResolutionOutput; mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed); mDynamicRangeProfile = dynamicRangeProfile; + mStreamUseCase = streamUseCase; } /** @@ -917,6 +1001,7 @@ public final class OutputConfiguration implements Parcelable { // writeList doesn't seem to work well with Integer list. dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed)); dest.writeInt(mDynamicRangeProfile); + dest.writeInt(mStreamUseCase); } /** @@ -947,7 +1032,8 @@ public final class OutputConfiguration implements Parcelable { mConfiguredDataspace != other.mConfiguredDataspace || mConfiguredGenerationId != other.mConfiguredGenerationId || !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) || - mIsMultiResolution != other.mIsMultiResolution) + mIsMultiResolution != other.mIsMultiResolution || + mStreamUseCase != other.mStreamUseCase) return false; if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) { return false; @@ -985,7 +1071,7 @@ public final class OutputConfiguration implements Parcelable { mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0, mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(), mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(), - mDynamicRangeProfile); + mDynamicRangeProfile, mStreamUseCase); } return HashCodeHelpers.hashCode( @@ -994,7 +1080,7 @@ public final class OutputConfiguration implements Parcelable { mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0, mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(), mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(), - mDynamicRangeProfile); + mDynamicRangeProfile, mStreamUseCase); } private static final String TAG = "OutputConfiguration"; @@ -1028,4 +1114,6 @@ public final class OutputConfiguration implements Parcelable { private ArrayList<Integer> mSensorPixelModesUsed; // Dynamic range profile private int mDynamicRangeProfile; + // Stream use case + private int mStreamUseCase; } diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index dbe7a418b0fc..c3113799ad45 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -38,6 +38,12 @@ public final class Light implements Parcelable { /** Type for lights that indicate microphone usage */ public static final int LIGHT_TYPE_MICROPHONE = 8; + /** Type for lights that indicate camera usage + * + * @hide + */ + public static final int LIGHT_TYPE_CAMERA = 9; + // These enum values start from 10001 to avoid collision with expanding of HAL light types. /** * Type for lights that indicate a monochrome color LED light. diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index e5c22e4de08e..c3bb381bb740 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -219,7 +219,7 @@ final class NavigationBarController { // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary. final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT | (mShouldShowImeSwitcherWhenImeIsShown - ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN + ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0); navigationBarView.setNavigationIconHints(hints); } @@ -470,7 +470,7 @@ final class NavigationBarController { } final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT | (shouldShowImeSwitcherWhenImeIsShown - ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0); + ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0); navigationBarView.setNavigationIconHints(hints); } @@ -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/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java index 42847784dd2b..a2d71054c65d 100644 --- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java +++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java @@ -270,7 +270,7 @@ public final class NavigationBarView extends FrameLayout { // Update IME button visibility, a11y and rotate button always overrides the appearance final boolean imeSwitcherVisible = - (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0; + (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0; getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE); getBackButton().setVisibility(View.VISIBLE); diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java deleted file mode 100644 index 82ba156b08d0..000000000000 --- a/core/java/android/net/DhcpResults.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Log; - -import com.android.net.module.util.InetAddressUtils; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * A simple object for retrieving the results of a DHCP request. - * Optimized (attempted) for that jni interface - * TODO: remove this class and replace with other existing constructs - * @hide - */ -public final class DhcpResults implements Parcelable { - private static final String TAG = "DhcpResults"; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public LinkAddress ipAddress; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public InetAddress gateway; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public final ArrayList<InetAddress> dnsServers = new ArrayList<>(); - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public String domains; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public Inet4Address serverAddress; - - /** Vendor specific information (from RFC 2132). */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public String vendorInfo; - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int leaseDuration; - - /** Link MTU option. 0 means unset. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int mtu; - - public String serverHostName; - - @Nullable - public String captivePortalApiUrl; - - public DhcpResults() { - super(); - } - - /** - * Create a {@link StaticIpConfiguration} based on the DhcpResults. - */ - public StaticIpConfiguration toStaticIpConfiguration() { - return new StaticIpConfiguration.Builder() - .setIpAddress(ipAddress) - .setGateway(gateway) - .setDnsServers(dnsServers) - .setDomains(domains) - .build(); - } - - public DhcpResults(StaticIpConfiguration source) { - if (source != null) { - ipAddress = source.getIpAddress(); - gateway = source.getGateway(); - dnsServers.addAll(source.getDnsServers()); - domains = source.getDomains(); - } - } - - /** copy constructor */ - public DhcpResults(DhcpResults source) { - this(source == null ? null : source.toStaticIpConfiguration()); - if (source != null) { - serverAddress = source.serverAddress; - vendorInfo = source.vendorInfo; - leaseDuration = source.leaseDuration; - mtu = source.mtu; - serverHostName = source.serverHostName; - captivePortalApiUrl = source.captivePortalApiUrl; - } - } - - /** - * @see StaticIpConfiguration#getRoutes(String) - * @hide - */ - public List<RouteInfo> getRoutes(String iface) { - return toStaticIpConfiguration().getRoutes(iface); - } - - /** - * Test if this DHCP lease includes vendor hint that network link is - * metered, and sensitive to heavy data transfers. - */ - public boolean hasMeteredHint() { - if (vendorInfo != null) { - return vendorInfo.contains("ANDROID_METERED"); - } else { - return false; - } - } - - public void clear() { - ipAddress = null; - gateway = null; - dnsServers.clear(); - domains = null; - serverAddress = null; - vendorInfo = null; - leaseDuration = 0; - mtu = 0; - serverHostName = null; - captivePortalApiUrl = null; - } - - @Override - public String toString() { - StringBuffer str = new StringBuffer(super.toString()); - - str.append(" DHCP server ").append(serverAddress); - str.append(" Vendor info ").append(vendorInfo); - str.append(" lease ").append(leaseDuration).append(" seconds"); - if (mtu != 0) str.append(" MTU ").append(mtu); - str.append(" Servername ").append(serverHostName); - if (captivePortalApiUrl != null) { - str.append(" CaptivePortalApiUrl ").append(captivePortalApiUrl); - } - - return str.toString(); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) return true; - - if (!(obj instanceof DhcpResults)) return false; - - DhcpResults target = (DhcpResults)obj; - - return toStaticIpConfiguration().equals(target.toStaticIpConfiguration()) - && Objects.equals(serverAddress, target.serverAddress) - && Objects.equals(vendorInfo, target.vendorInfo) - && Objects.equals(serverHostName, target.serverHostName) - && leaseDuration == target.leaseDuration - && mtu == target.mtu - && Objects.equals(captivePortalApiUrl, target.captivePortalApiUrl); - } - - /** - * Implement the Parcelable interface - */ - public static final @android.annotation.NonNull Creator<DhcpResults> CREATOR = - new Creator<DhcpResults>() { - public DhcpResults createFromParcel(Parcel in) { - return readFromParcel(in); - } - - public DhcpResults[] newArray(int size) { - return new DhcpResults[size]; - } - }; - - /** Implement the Parcelable interface */ - public void writeToParcel(Parcel dest, int flags) { - toStaticIpConfiguration().writeToParcel(dest, flags); - dest.writeInt(leaseDuration); - dest.writeInt(mtu); - InetAddressUtils.parcelInetAddress(dest, serverAddress, flags); - dest.writeString(vendorInfo); - dest.writeString(serverHostName); - dest.writeString(captivePortalApiUrl); - } - - @Override - public int describeContents() { - return 0; - } - - private static DhcpResults readFromParcel(Parcel in) { - final StaticIpConfiguration s = StaticIpConfiguration.CREATOR.createFromParcel(in); - final DhcpResults dhcpResults = new DhcpResults(s); - dhcpResults.leaseDuration = in.readInt(); - dhcpResults.mtu = in.readInt(); - dhcpResults.serverAddress = (Inet4Address) InetAddressUtils.unparcelInetAddress(in); - dhcpResults.vendorInfo = in.readString(); - dhcpResults.serverHostName = in.readString(); - dhcpResults.captivePortalApiUrl = in.readString(); - return dhcpResults; - } - - // Utils for jni population - false on success - // Not part of the superclass because they're only used by the JNI iterface to the DHCP daemon. - public boolean setIpAddress(String addrString, int prefixLength) { - try { - Inet4Address addr = (Inet4Address) InetAddresses.parseNumericAddress(addrString); - ipAddress = new LinkAddress(addr, prefixLength); - } catch (IllegalArgumentException|ClassCastException e) { - Log.e(TAG, "setIpAddress failed with addrString " + addrString + "/" + prefixLength); - return true; - } - return false; - } - - public boolean setGateway(String addrString) { - try { - gateway = InetAddresses.parseNumericAddress(addrString); - } catch (IllegalArgumentException e) { - Log.e(TAG, "setGateway failed with addrString " + addrString); - return true; - } - return false; - } - - public boolean addDns(String addrString) { - if (TextUtils.isEmpty(addrString) == false) { - try { - dnsServers.add(InetAddresses.parseNumericAddress(addrString)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "addDns failed with addrString " + addrString); - return true; - } - } - return false; - } - - public LinkAddress getIpAddress() { - return ipAddress; - } - - public void setIpAddress(LinkAddress ipAddress) { - this.ipAddress = ipAddress; - } - - public InetAddress getGateway() { - return gateway; - } - - public void setGateway(InetAddress gateway) { - this.gateway = gateway; - } - - public List<InetAddress> getDnsServers() { - return dnsServers; - } - - /** - * Add a DNS server to this configuration. - */ - public void addDnsServer(InetAddress server) { - dnsServers.add(server); - } - - public String getDomains() { - return domains; - } - - public void setDomains(String domains) { - this.domains = domains; - } - - public Inet4Address getServerAddress() { - return serverAddress; - } - - public void setServerAddress(Inet4Address addr) { - serverAddress = addr; - } - - public int getLeaseDuration() { - return leaseDuration; - } - - public void setLeaseDuration(int duration) { - leaseDuration = duration; - } - - public String getVendorInfo() { - return vendorInfo; - } - - public void setVendorInfo(String info) { - vendorInfo = info; - } - - public int getMtu() { - return mtu; - } - - public void setMtu(int mtu) { - this.mtu = mtu; - } - - public String getCaptivePortalApiUrl() { - return captivePortalApiUrl; - } - - public void setCaptivePortalApiUrl(String url) { - captivePortalApiUrl = url; - } -} diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 6284f56c8258..dc241066a60a 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -72,9 +72,9 @@ interface INetworkPolicyManager { SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template); void notifyStatsProviderWarningOrLimitReached(); SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage); - void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage); + void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, long expirationDurationMillis, String callingPackage); String getSubscriptionPlansOwner(int subId); - void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage); + void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long expirationDurationMillis, String callingPackage); void factoryReset(String subscriber); diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 596f4317dce3..714227d87aeb 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -338,7 +338,9 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { out.writeInt(TEMPLATE_BACKUP_VERSION_LATEST); out.writeInt(template.getMatchRule()); - BackupUtils.writeString(out, template.getSubscriberIds().iterator().next()); + final Set<String> subscriberIds = template.getSubscriberIds(); + BackupUtils.writeString(out, subscriberIds.isEmpty() + ? null : subscriberIds.iterator().next()); BackupUtils.writeString(out, template.getWifiNetworkKeys().isEmpty() ? null : template.getWifiNetworkKeys().iterator().next()); out.writeInt(template.getMeteredness()); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 9122adfece53..d8f098eb880b 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -482,8 +482,8 @@ public class NetworkPolicyManager { * @param networkTypes the network types this override applies to. If no * network types are specified, override values will be ignored. * {@see TelephonyManager#getAllNetworkTypes()} - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the * requested state until explicitly cleared, or the next reboot, * whichever happens first * @param callingPackage the name of the package making the call. @@ -491,11 +491,11 @@ public class NetworkPolicyManager { */ public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, @SubscriptionOverrideMask int overrideValue, - @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis, + @NonNull @Annotation.NetworkType int[] networkTypes, long expirationDurationMillis, @NonNull String callingPackage) { try { mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes, - timeoutMillis, callingPackage); + expirationDurationMillis, callingPackage); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -506,13 +506,16 @@ public class NetworkPolicyManager { * * @param subId the subscriber this relationship applies to. * @param plans the list of plans. + * @param expirationDurationMillis the duration after which the subscription plans + * will be automatically cleared, or {@code 0} to leave the plans until + * explicitly cleared, or the next reboot, whichever happens first * @param callingPackage the name of the package making the call * @hide */ public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans, - @NonNull String callingPackage) { + long expirationDurationMillis, @NonNull String callingPackage) { try { - mService.setSubscriptionPlans(subId, plans, callingPackage); + mService.setSubscriptionPlans(subId, plans, expirationDurationMillis, callingPackage); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } 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/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index d9c9a2b55abd..bc7fb789b538 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -63,7 +63,7 @@ interface IUserManager { boolean isUserTypeEnabled(in String userType); boolean canAddMoreUsersOfType(in String userType); int getRemainingCreatableUserCount(in String userType); - int getRemainingCreatableProfileCount(in String userType, int userId, boolean allowedToRemoveOne); + int getRemainingCreatableProfileCount(in String userType, int userId); boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne); boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne); UserInfo getProfileParent(int userId); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7b8d34b3b570..2bd1dbb238e8 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4055,9 +4055,6 @@ public class UserManager { * <p>Note that is applicable to any profile type (currently not including Restricted profiles). * * @param userType the type of profile, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. - * @param allowedToRemoveOne whether removing an existing profile of given type -if there is- - * from the context user to make up space should be taken into account - * for the calculation. * @return how many additional profiles can be created. * @hide */ @@ -4068,13 +4065,11 @@ public class UserManager { android.Manifest.permission.QUERY_USERS }) @UserHandleAware - public int getRemainingCreatableProfileCount(@NonNull String userType, - boolean allowedToRemoveOne) { + public int getRemainingCreatableProfileCount(@NonNull String userType) { Objects.requireNonNull(userType, "userType must not be null"); try { // TODO(b/142482943): Perhaps let the following code apply to restricted users too. - return mService.getRemainingCreatableProfileCount(userType, mUserId, - allowedToRemoveOne); + return mService.getRemainingCreatableProfileCount(userType, mUserId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } 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/provider/Settings.java b/core/java/android/provider/Settings.java index ee6f9c033271..3f41458886a3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6094,11 +6094,9 @@ public final class Settings { } /** @hide */ - @SystemApi - @Nullable - @SuppressLint("VisiblySynchronized") - public static String getStringForUser(@NonNull ContentResolver resolver, - @NonNull String name, int userHandle) { + @UnsupportedAppUsage + public static String getStringForUser(ContentResolver resolver, String name, + int userHandle) { if (MOVED_TO_GLOBAL.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure" + " to android.provider.Settings.Global."); @@ -6330,9 +6328,8 @@ public final class Settings { } /** @hide */ - @SystemApi - public static int getIntForUser(@NonNull ContentResolver cr, @NonNull String name, - int def, int userHandle) { + @UnsupportedAppUsage + public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) { String v = getStringForUser(cr, name, userHandle); return parseIntSettingWithDefault(v, def); } @@ -16819,6 +16816,16 @@ public final class Settings { "low_power_standby_active_during_maintenance"; /** + * Timeout for the system server watchdog. + * + * @see {@link com.android.server.Watchdog}. + * + * @hide + */ + public static final String WATCHDOG_TIMEOUT_MILLIS = + "system_server_watchdog_timeout_ms"; + + /** * Settings migrated from Wear OS settings provider. * @hide */ @@ -17325,6 +17332,12 @@ public final class Settings { "clockwork_long_press_to_assistant_enabled"; /* + * Whether the device has Cooldown Mode enabled. + * @hide + */ + public static final String COOLDOWN_MODE_ON = "cooldown_mode_on"; + + /* * Whether the device has Wet Mode/ Touch Lock Mode enabled. * @hide */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 34e35d4512b9..3ff0161b80d5 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1425,25 +1425,6 @@ public final class Telephony { public static final String KEY_TYPE = "key_type"; /** - * MVNO type: - * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}. - * <P> Type: TEXT </P> - */ - public static final String MVNO_TYPE = "mvno_type"; - - /** - * MVNO data. - * Use the following examples. - * <ul> - * <li>SPN: A MOBILE, BEN NL, ...</li> - * <li>IMSI: 302720x94, 2060188, ...</li> - * <li>GID: 4E, 33, ...</li> - * </ul> - * <P> Type: TEXT </P> - */ - public static final String MVNO_MATCH_DATA = "mvno_match_data"; - - /** * The carrier public key that is used for the IMSI encryption. * <P> Type: TEXT </P> */ @@ -1470,6 +1451,11 @@ public final class Telephony { public static final String LAST_MODIFIED = "last_modified"; /** + * Carrier ID of the operetor. + * <P> Type: TEXT </P> + */ + public static final String CARRIER_ID = "carrier_id"; + /** * The {@code content://} style URL for this table. */ @NonNull 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/service/games/IGameSessionController.aidl b/core/java/android/service/games/IGameSessionController.aidl index 84311dc0aedf..fd994044775f 100644 --- a/core/java/android/service/games/IGameSessionController.aidl +++ b/core/java/android/service/games/IGameSessionController.aidl @@ -24,6 +24,6 @@ import com.android.internal.infra.AndroidFuture; */ oneway interface IGameSessionController { void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)") void restartGame(in int taskId); } diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl index 7c79b1ae15de..986a41c64053 100644 --- a/core/java/android/speech/IRecognitionListener.aidl +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -78,6 +78,24 @@ oneway interface IRecognitionListener { void onPartialResults(in Bundle results); /** + * Called for each ready segment of a recognition request. To request segmented speech results + * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called + * any number of times between {@link #onBeginningOfSpeech()} and + * {@link #onEndOfSegmentedSession()}. + * + * @param segmentResults the returned results. To retrieve the results in + * ArrayList<String> format use {@link Bundle#getStringArrayList(String)} with + * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter + */ + void onSegmentResults(in Bundle results); + + /** + * Called at the end of a segmented recognition request. To request segmented speech results + * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. + */ + void onEndOfSegmentedSession(); + + /** * Reserved for adding future events. * * @param eventType the type of the occurred event diff --git a/core/java/android/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java index c94b60f847f4..64fd09ff7faf 100644 --- a/core/java/android/speech/RecognitionListener.java +++ b/core/java/android/speech/RecognitionListener.java @@ -15,6 +15,7 @@ */ package android.speech; +import android.annotation.NonNull; import android.content.Intent; import android.os.Bundle; @@ -69,7 +70,13 @@ public interface RecognitionListener { /** * Called when recognition results are ready. - * + * + * <p> + * Called with the results for the full speech since {@link #onReadyForSpeech(Bundle)}. + * To get recognition results in segments rather than for the full session see + * {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. + * </p> + * * @param results the recognition results. To retrieve the results in {@code * ArrayList<String>} format use {@link Bundle#getStringArrayList(String)} with * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter. A float array of @@ -92,6 +99,24 @@ public interface RecognitionListener { void onPartialResults(Bundle partialResults); /** + * Called for each ready segment of a recognition request. To request segmented speech results + * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called + * any number of times between {@link #onReadyForSpeech(Bundle)} and + * {@link #onEndOfSegmentedSession()}. + * + * @param segmentResults the returned results. To retrieve the results in + * ArrayList<String> format use {@link Bundle#getStringArrayList(String)} with + * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter + */ + default void onSegmentResults(@NonNull Bundle segmentResults) {} + + /** + * Called at the end of a segmented recognition request. To request segmented speech results + * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. + */ + default void onEndOfSegmentedSession() {} + + /** * Reserved for adding future events. * * @param eventType the type of the occurred event diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 5dbbc045077e..08136761f75c 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -427,6 +427,26 @@ public abstract class RecognitionService extends Service { } /** + * The service should call this method for each ready segment of a long recognition session. + * + * @param results the recognition results. To retrieve the results in {@code + * ArrayList<String>} format use {@link Bundle#getStringArrayList(String)} with + * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter + */ + @SuppressLint({"CallbackMethodName", "RethrowRemoteException"}) + public void segmentResults(@NonNull Bundle results) throws RemoteException { + mListener.onSegmentResults(results); + } + + /** + * The service should call this method to end a segmented session. + */ + @SuppressLint({"CallbackMethodName", "RethrowRemoteException"}) + public void endOfSegmentedSession() throws RemoteException { + mListener.onEndOfSegmentedSession(); + } + + /** * Return the Linux uid assigned to the process that sent you the current transaction that * is being processed. This is obtained from {@link Binder#getCallingUid()}. */ diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index 3183f15fe16c..271e3072c4d9 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -426,4 +426,16 @@ public class RecognizerIntent { * */ public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE"; + + /** + * Optional boolean, when true and supported by the recognizer implementation it will split + * the recognition results in segments, returned via + * {@link RecognitionListener#onSegmentResults(Bundle)} and terminate the session with + * {@link RecognitionListener#onEndOfSegmentedSession()}. There will be no call to + * {@link RecognitionListener#onResults(Bundle)}. Callers can use + * {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} and + * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS} to tune how long the segments + * will be. Defaults to false. + */ + public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION"; } diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index 71c1e882a1f6..502558e355e6 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -768,6 +768,8 @@ public class SpeechRecognizer { private static final int MSG_PARTIAL_RESULTS = 7; private static final int MSG_RMS_CHANGED = 8; private static final int MSG_ON_EVENT = 9; + private static final int MSG_SEGMENT_RESULTS = 10; + private static final int MSG_SEGMENT_END_SESSION = 11; private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) { @Override @@ -803,6 +805,12 @@ public class SpeechRecognizer { case MSG_ON_EVENT: mInternalListener.onEvent(msg.arg1, (Bundle) msg.obj); break; + case MSG_SEGMENT_RESULTS: + mInternalListener.onSegmentResults((Bundle) msg.obj); + break; + case MSG_SEGMENT_END_SESSION: + mInternalListener.onEndOfSegmentedSession(); + break; } } }; @@ -839,6 +847,14 @@ public class SpeechRecognizer { Message.obtain(mInternalHandler, MSG_RMS_CHANGED, rmsdB).sendToTarget(); } + public void onSegmentResults(final Bundle bundle) { + Message.obtain(mInternalHandler, MSG_SEGMENT_RESULTS, bundle).sendToTarget(); + } + + public void onEndOfSegmentedSession() { + Message.obtain(mInternalHandler, MSG_SEGMENT_END_SESSION).sendToTarget(); + } + public void onEvent(final int eventType, final Bundle params) { Message.obtain(mInternalHandler, MSG_ON_EVENT, eventType, eventType, params) .sendToTarget(); diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java index 05c312b56cc7..5c4ee89bfbd3 100644 --- a/core/java/android/view/OnBackInvokedDispatcher.java +++ b/core/java/android/view/OnBackInvokedDispatcher.java @@ -17,8 +17,14 @@ package android.view; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.TestApi; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.os.Build; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -31,25 +37,56 @@ import java.lang.annotation.RetentionPolicy; * Attribute updates are proactively pushed to the window manager if they change the dispatch * target (a.k.a. the callback to be invoked next), or its behavior. */ -public abstract class OnBackInvokedDispatcher { +public interface OnBackInvokedDispatcher { + /** + * Enables dispatching the "back" action via {@link android.view.OnBackInvokedDispatcher}. + * + * When enabled, the following APIs are no longer invoked: + * <ul> + * <li> {@link android.app.Activity#onBackPressed} + * <li> {@link android.app.Dialog#onBackPressed} + * <li> {@link android.view.KeyEvent#KEYCODE_BACK} is no longer dispatched. + * </ul> + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L; + + /** @hide */ + String TAG = "OnBackInvokedDispatcher"; + + /** @hide */ + boolean DEBUG = Build.isDebuggable(); + /** @hide */ @IntDef({ PRIORITY_DEFAULT, PRIORITY_OVERLAY, }) @Retention(RetentionPolicy.SOURCE) - public @interface Priority{} + @interface Priority{} /** * Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and * navigation drawers that should receive back dispatch before non-overlays. */ - public static final int PRIORITY_OVERLAY = 1000000; + int PRIORITY_OVERLAY = 1000000; /** * Default priority level of {@link OnBackInvokedCallback}s. */ - public static final int PRIORITY_DEFAULT = 0; + int PRIORITY_DEFAULT = 0; + + /** + * Priority level of {@link OnBackInvokedCallback}s registered by the system. + * + * System back animation will play when the callback to receive dispatch has this priority. + * @hide + */ + int PRIORITY_SYSTEM = -1; /** * Registers a {@link OnBackInvokedCallback}. @@ -61,10 +98,11 @@ public abstract class OnBackInvokedDispatcher { * registered, the existing instance (no matter its priority) will be * unregistered and registered again. * @param priority The priority of the callback. + * @throws {@link IllegalArgumentException} if the priority is negative. */ @SuppressLint("SamShouldBeLast") - public abstract void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, @Priority int priority); + void registerOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback, @Priority @IntRange(from = 0) int priority); /** * Unregisters a {@link OnBackInvokedCallback}. @@ -72,5 +110,20 @@ public abstract class OnBackInvokedDispatcher { * @param callback The callback to be unregistered. Does nothing if the callback has not been * registered. */ - public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback); + void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback); + + /** + * Returns the most prioritized callback to receive back dispatch next. + * @hide + */ + @Nullable + default OnBackInvokedCallback getTopCallback() { + return null; + } + + /** + * Registers a {@link OnBackInvokedCallback} with system priority. + * @hide + */ + default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { } } 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..777e89d145b2 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,10 +313,15 @@ 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(); + /** + * Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events + * to view root for apps using legacy back behavior. + */ + private OnBackInvokedCallback mCompatOnBackInvokedCallback; /** * Callback for notifying about global configuration changes. @@ -893,7 +898,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 +1148,6 @@ public final class ViewRootImpl implements ViewParent, if (pendingInsetsController != null) { pendingInsetsController.replayAndAttach(mInsetsController); } - ((RootViewSurfaceTaker) mView) - .provideWindowOnBackInvokedDispatcher() - .attachToWindow(mWindowSession, mWindow); } try { @@ -1193,6 +1194,15 @@ public final class ViewRootImpl implements ViewParent, getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame); setFrame(mTmpFrames.frame); + registerBackCallbackOnWindow(); + if (WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + // For apps requesting legacy back behavior, we add a compat callback that + // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views. + // This way from system point of view, these apps are providing custom + // {@link OnBackInvokedCallback}s, and will not play system back animations + // for them. + registerCompatOnBackInvokedCallback(); + } if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; @@ -1335,7 +1345,7 @@ public final class ViewRootImpl implements ViewParent, private void setTag() { final String[] split = mWindowAttributes.getTitle().toString().split("\\."); if (split.length > 0) { - mTag = TAG + "[" + split[split.length - 1] + "]"; + mTag = "VRI[" + split[split.length - 1] + "]"; } } @@ -5934,7 +5944,7 @@ public final class ViewRootImpl implements ViewParent, } } - private boolean isBack(InputEvent event) { + boolean isBack(InputEvent event) { if (event instanceof KeyEvent) { return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK; } else { @@ -6474,6 +6484,19 @@ public final class ViewRootImpl implements ViewParent, return FINISH_NOT_HANDLED; } + if (isBack(event) + && mContext != null + && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + // Invoke the appropriate {@link OnBackInvokedCallback} if the new back + // navigation should be used, and the key event is not handled by anything else. + OnBackInvokedCallback topCallback = + getOnBackInvokedDispatcher().getTopCallback(); + if (topCallback != null) { + topCallback.onBackInvoked(); + return FINISH_HANDLED; + } + } + // This dispatch is for windows that don't have a Window.Callback. Otherwise, // the Window.Callback usually will have already called this (see // DecorView.superDispatchKeyEvent) leaving this call a no-op. @@ -8417,6 +8440,8 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; } + unregisterCompatOnBackInvokedCallback(); + mOnBackInvokedDispatcher.detachFromWindow(); WindowManagerGlobal.getInstance().doRemoveView(this); } @@ -10771,12 +10796,49 @@ 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(); + @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); + } + + private void sendBackKeyEvent(int action) { + long when = SystemClock.uptimeMillis(); + final KeyEvent ev = new KeyEvent(when, when, action, + KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, + InputDevice.SOURCE_KEYBOARD); + + ev.setDisplayId(mContext.getDisplay().getDisplayId()); + if (mView != null) { + mView.dispatchKeyEvent(ev); + } + } + + private void registerCompatOnBackInvokedCallback() { + mCompatOnBackInvokedCallback = new OnBackInvokedCallback() { + @Override + public void onBackInvoked() { + sendBackKeyEvent(KeyEvent.ACTION_DOWN); + sendBackKeyEvent(KeyEvent.ACTION_UP); + } + }; + mOnBackInvokedDispatcher.registerOnBackInvokedCallback( + mCompatOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + } + + private void unregisterCompatOnBackInvokedCallback() { + if (mCompatOnBackInvokedCallback != null) { + mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback); } - return mFallbackOnBackInvokedDispatcher; } @Override diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index a427ab8fe837..cd8dd86b8e02 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -553,8 +553,20 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_ASSIST_READING_CONTEXT = 0x01000000; /** - * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: - * The type of change is not defined. + * Represents a change in the speech state defined by the content-change types. A change in the + * speech state occurs when another service is either speaking or listening for human speech. + * This event helps avoid conflicts where two services want to speak or one listens + * when another speaks. + * @see #SPEECH_STATE_SPEAKING_START + * @see #SPEECH_STATE_SPEAKING_END + * @see #SPEECH_STATE_LISTENING_START + * @see #SPEECH_STATE_LISTENING_END + */ + public static final int TYPE_SPEECH_STATE_CHANGE = 0x02000000; + + /** + * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The type of change is not + * defined. */ public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000; @@ -641,6 +653,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200; + /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */ + public static final int SPEECH_STATE_SPEAKING_START = 0x00000001; + + /** + * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is no longer + * speaking. + */ + public static final int SPEECH_STATE_SPEAKING_END = 0x00000002; + + /** + * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is listening to the + * microphone. + */ + public static final int SPEECH_STATE_LISTENING_START = 0x00000004; + + /** + * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is no longer + * listening to the microphone. + */ + public static final int SPEECH_STATE_LISTENING_END = 0x00000008; + /** * Change type for {@link #TYPE_WINDOWS_CHANGED} event: * The window was added. @@ -730,50 +763,69 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "CONTENT_CHANGE_TYPE_" }, + @IntDef( + flag = true, + prefix = {"CONTENT_CHANGE_TYPE_"}, value = { - CONTENT_CHANGE_TYPE_UNDEFINED, - CONTENT_CHANGE_TYPE_SUBTREE, - CONTENT_CHANGE_TYPE_TEXT, - CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, - CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, - CONTENT_CHANGE_TYPE_PANE_TITLE, - CONTENT_CHANGE_TYPE_PANE_APPEARED, - CONTENT_CHANGE_TYPE_PANE_DISAPPEARED, - CONTENT_CHANGE_TYPE_DRAG_STARTED, - CONTENT_CHANGE_TYPE_DRAG_DROPPED, - CONTENT_CHANGE_TYPE_DRAG_CANCELLED + CONTENT_CHANGE_TYPE_UNDEFINED, + CONTENT_CHANGE_TYPE_SUBTREE, + CONTENT_CHANGE_TYPE_TEXT, + CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, + CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, + CONTENT_CHANGE_TYPE_PANE_TITLE, + CONTENT_CHANGE_TYPE_PANE_APPEARED, + CONTENT_CHANGE_TYPE_PANE_DISAPPEARED, + CONTENT_CHANGE_TYPE_DRAG_STARTED, + CONTENT_CHANGE_TYPE_DRAG_DROPPED, + CONTENT_CHANGE_TYPE_DRAG_CANCELLED, }) public @interface ContentChangeTypes {} /** @hide */ - @IntDef(flag = true, prefix = { "TYPE_" }, value = { - TYPE_VIEW_CLICKED, - TYPE_VIEW_LONG_CLICKED, - TYPE_VIEW_SELECTED, - TYPE_VIEW_FOCUSED, - TYPE_VIEW_TEXT_CHANGED, - TYPE_WINDOW_STATE_CHANGED, - TYPE_NOTIFICATION_STATE_CHANGED, - TYPE_VIEW_HOVER_ENTER, - TYPE_VIEW_HOVER_EXIT, - TYPE_TOUCH_EXPLORATION_GESTURE_START, - TYPE_TOUCH_EXPLORATION_GESTURE_END, - TYPE_WINDOW_CONTENT_CHANGED, - TYPE_VIEW_SCROLLED, - TYPE_VIEW_TEXT_SELECTION_CHANGED, - TYPE_ANNOUNCEMENT, - TYPE_VIEW_ACCESSIBILITY_FOCUSED, - TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, - TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, - TYPE_GESTURE_DETECTION_START, - TYPE_GESTURE_DETECTION_END, - TYPE_TOUCH_INTERACTION_START, - TYPE_TOUCH_INTERACTION_END, - TYPE_WINDOWS_CHANGED, - TYPE_VIEW_CONTEXT_CLICKED, - TYPE_ASSIST_READING_CONTEXT - }) + @Retention(RetentionPolicy.SOURCE) + @IntDef( + flag = true, + prefix = {"SPEECH_STATE_"}, + value = { + SPEECH_STATE_SPEAKING_START, + SPEECH_STATE_SPEAKING_END, + SPEECH_STATE_LISTENING_START, + SPEECH_STATE_LISTENING_END + }) + public @interface SpeechStateChangeTypes {} + + /** @hide */ + @IntDef( + flag = true, + prefix = {"TYPE_"}, + value = { + TYPE_VIEW_CLICKED, + TYPE_VIEW_LONG_CLICKED, + TYPE_VIEW_SELECTED, + TYPE_VIEW_FOCUSED, + TYPE_VIEW_TEXT_CHANGED, + TYPE_WINDOW_STATE_CHANGED, + TYPE_NOTIFICATION_STATE_CHANGED, + TYPE_VIEW_HOVER_ENTER, + TYPE_VIEW_HOVER_EXIT, + TYPE_TOUCH_EXPLORATION_GESTURE_START, + TYPE_TOUCH_EXPLORATION_GESTURE_END, + TYPE_WINDOW_CONTENT_CHANGED, + TYPE_VIEW_SCROLLED, + TYPE_VIEW_TEXT_SELECTION_CHANGED, + TYPE_ANNOUNCEMENT, + TYPE_VIEW_ACCESSIBILITY_FOCUSED, + TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, + TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, + TYPE_GESTURE_DETECTION_START, + TYPE_GESTURE_DETECTION_END, + TYPE_TOUCH_INTERACTION_START, + TYPE_TOUCH_INTERACTION_END, + TYPE_WINDOWS_CHANGED, + TYPE_VIEW_CONTEXT_CLICKED, + TYPE_ASSIST_READING_CONTEXT, + TYPE_SPEECH_STATE_CHANGE + }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} @@ -814,6 +866,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par int mAction; int mContentChangeTypes; int mWindowChangeTypes; + int mSpeechStateChangeTypes; /** * The stack trace describing where this event originated from on the app side. @@ -867,6 +920,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mMovementGranularity = event.mMovementGranularity; mAction = event.mAction; mContentChangeTypes = event.mContentChangeTypes; + mSpeechStateChangeTypes = event.mSpeechStateChangeTypes; mWindowChangeTypes = event.mWindowChangeTypes; mEventTime = event.mEventTime; mPackageName = event.mPackageName; @@ -1008,6 +1062,51 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** + * Gets the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event + * + * @see #SPEECH_STATE_SPEAKING_START + * @see #SPEECH_STATE_SPEAKING_END + * @see #SPEECH_STATE_LISTENING_START + * @see #SPEECH_STATE_LISTENING_END + */ + public int getSpeechStateChangeTypes() { + return mSpeechStateChangeTypes; + } + + private static String speechStateChangedTypesToString(int types) { + return BitUtils.flagsToString( + types, AccessibilityEvent::singleSpeechStateChangeTypeToString); + } + + private static String singleSpeechStateChangeTypeToString(int type) { + switch (type) { + case SPEECH_STATE_SPEAKING_START: + return "SPEECH_STATE_SPEAKING_START"; + case SPEECH_STATE_LISTENING_START: + return "SPEECH_STATE_LISTENING_START"; + case SPEECH_STATE_SPEAKING_END: + return "SPEECH_STATE_SPEAKING_END"; + case SPEECH_STATE_LISTENING_END: + return "SPEECH_STATE_LISTENING_END"; + default: + return Integer.toHexString(type); + } + } + + /** + * Sets the speech state type signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event + * + * @see #SPEECH_STATE_SPEAKING_START + * @see #SPEECH_STATE_SPEAKING_END + * @see #SPEECH_STATE_LISTENING_START + * @see #SPEECH_STATE_LISTENING_END + */ + public void setSpeechStateChangeTypes(int state) { + enforceNotSealed(); + mSpeechStateChangeTypes = state; + } + + /** * Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A * single event may represent multiple change types. * @@ -1239,6 +1338,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mAction = 0; mContentChangeTypes = 0; mWindowChangeTypes = 0; + mSpeechStateChangeTypes = 0; mPackageName = null; mEventTime = 0; if (mRecords != null) { @@ -1261,6 +1361,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mAction = parcel.readInt(); mContentChangeTypes = parcel.readInt(); mWindowChangeTypes = parcel.readInt(); + mSpeechStateChangeTypes = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); @@ -1332,6 +1433,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par parcel.writeInt(mAction); parcel.writeInt(mContentChangeTypes); parcel.writeInt(mWindowChangeTypes); + parcel.writeInt(mSpeechStateChangeTypes); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); @@ -1500,6 +1602,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par case TYPE_WINDOWS_CHANGED: return "TYPE_WINDOWS_CHANGED"; case TYPE_VIEW_CONTEXT_CLICKED: return "TYPE_VIEW_CONTEXT_CLICKED"; case TYPE_ASSIST_READING_CONTEXT: return "TYPE_ASSIST_READING_CONTEXT"; + case TYPE_SPEECH_STATE_CHANGE: return "TYPE_SPEECH_STATE_CHANGE"; default: return Integer.toHexString(eventType); } } 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/widget/TextView.java b/core/java/android/widget/TextView.java index 0fe2ed51beb6..9efa583a894a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10821,8 +10821,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener && mSavedMarqueeModeLayout.getLineWidth(0) > width)); } + /** + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private void startMarquee() { + protected void startMarquee() { // Do not ellipsize EditText if (getKeyListener() != null) return; @@ -10848,7 +10851,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private void stopMarquee() { + /** + * @hide + */ + protected void stopMarquee() { if (mMarquee != null && !mMarquee.isStopped()) { mMarquee.stop(); } 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..4de977a91d05 --- /dev/null +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -0,0 +1,179 @@ +/* + * 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 implements 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)); + } + if (priority < 0) { + throw new IllegalArgumentException("Application registered OnBackInvokedCallback " + + "cannot have negative priority. Priority: " + priority); + } + registerOnBackInvokedCallbackUnchecked(callback, priority); + } + + @Override + public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { + registerOnBackInvokedCallbackUnchecked(callback, PRIORITY_SYSTEM); + } + + @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)); + } + } + + private void registerOnBackInvokedCallbackUnchecked( + @NonNull OnBackInvokedCallback callback, int priority) { + synchronized (mLock) { + mCallbacks.add(Pair.create(callback, priority)); + if (mActualDispatcherOwner != null) { + mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + callback, priority); + } + } + } + + /** + * 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) { + int priority = callbackPair.second; + if (priority >= 0) { + dispatcher.registerOnBackInvokedCallback(callbackPair.first, priority); + } else { + dispatcher.registerSystemOnBackInvokedCallback(callbackPair.first); + } + } + 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/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 29786046e49c..d37d3b42872f 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -18,8 +18,10 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.compat.CompatChanges; import android.os.Handler; import android.os.RemoteException; +import android.os.SystemProperties; import android.util.Log; import android.view.IWindow; import android.view.IWindowSession; @@ -31,7 +33,7 @@ import java.util.HashMap; import java.util.TreeMap; /** - * Provides window based implementation of {@link OnBackInvokedDispatcher}. + * Provides window based implementation of {@link android.view.OnBackInvokedDispatcher}. * * Callbacks with higher priorities receive back dispatching first. * Within the same priority, callbacks receive back dispatching in the reverse order @@ -39,16 +41,19 @@ import java.util.TreeMap; * * When the top priority callback is updated, the new callback is propagated to the Window Manager * if the window the instance is associated with has been attached. It is allowed to register / - * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks - * will not receive dispatches until window attachment. + * unregister {@link android.view.OnBackInvokedCallback}s before the window is attached, although + * callbacks will not receive dispatches until window attachment. * * @hide */ -public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher { +public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; private static final String TAG = "WindowOnBackDispatcher"; private static final boolean DEBUG = false; + private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; + private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties + .getInt(BACK_PREDICTABILITY_PROP, 0) > 0; /** The currently most prioritized callback. */ @Nullable @@ -82,6 +87,15 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher { @Override public void registerOnBackInvokedCallback( @NonNull OnBackInvokedCallback callback, @Priority int priority) { + if (priority < 0) { + throw new IllegalArgumentException("Application registered OnBackInvokedCallback " + + "cannot have negative priority. Priority: " + priority); + } + registerOnBackInvokedCallbackUnchecked(callback, priority); + } + + private void registerOnBackInvokedCallbackUnchecked( + @NonNull OnBackInvokedCallback callback, @Priority int priority) { if (!mOnBackInvokedCallbacks.containsKey(priority)) { mOnBackInvokedCallbacks.put(priority, new ArrayList<>()); } @@ -120,6 +134,11 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher { } } + @Override + public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { + registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM); + } + /** Clears all registered callbacks on the instance. */ public void clear() { mAllCallbacks.clear(); @@ -198,4 +217,21 @@ public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher { Handler.getMain().post(() -> mCallback.onBackInvoked()); } } + + @Override + public OnBackInvokedCallback getTopCallback() { + return mTopCallback == null ? null : mTopCallback.getCallback(); + } + + /** + * Returns if the legacy back behavior should be used. + * + * Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered + * {@link android.view.OnBackInvokedCallback}. + * + */ + public static boolean shouldUseLegacyBack() { + return !CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME) + || !IS_BACK_PREDICTABILITY_ENABLED; + } } 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/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 2925341cd948..40e40856b000 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -87,7 +87,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.OnBackInvokedDispatcher; import android.view.PendingInsetsController; import android.view.ThreadedRenderer; import android.view.View; @@ -109,7 +108,6 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.PopupWindow; -import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; @@ -297,7 +295,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return true; }; private Consumer<Boolean> mCrossWindowBlurEnabledListener; - private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { @@ -326,7 +323,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); - mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(); } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1880,7 +1876,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } mPendingInsetsController.detach(); - mOnBackInvokedDispatcher.detachFromWindow(); } @Override @@ -1925,11 +1920,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return mPendingInsetsController; } - @Override - public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - private ActionMode createActionMode( int type, ActionMode.Callback2 callback, View originatingView) { switch (type) { @@ -2384,7 +2374,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } } - mOnBackInvokedDispatcher.clear(); } @Override @@ -2666,15 +2655,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - /** - * Returns the {@link OnBackInvokedDispatcher} on the decor view. - */ - @Override - @Nullable - public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - @Override public String toString() { return "DecorView@" + Integer.toHexString(this.hashCode()) + "[" diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 7755b694af03..12f38a4d5b9a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -91,6 +91,8 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; import android.view.ScrollCaptureCallback; import android.view.SearchEvent; import android.view.SurfaceHolder.Callback2; @@ -110,6 +112,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import android.window.ProxyOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.view.menu.ContextMenuBuilder; @@ -134,7 +137,8 @@ import java.util.List; * * @hide */ -public class PhoneWindow extends Window implements MenuBuilder.Callback { +public class PhoneWindow extends Window implements MenuBuilder.Callback, + OnBackInvokedDispatcherOwner { private final static String TAG = "PhoneWindow"; @@ -340,6 +344,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean mDecorFitsSystemWindows = true; + private ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher = + new ProxyOnBackInvokedDispatcher(); + static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); @@ -2146,6 +2153,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */ void onViewRootImplSet(ViewRootImpl viewRoot) { viewRoot.setActivityConfigCallback(mActivityConfigCallback); + mProxyOnBackInvokedDispatcher.setActualDispatcherOwner(viewRoot); applyDecorFitsSystemWindows(); } @@ -3993,4 +4001,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public AttachedSurfaceControl getRootSurfaceControl() { return getViewRootImplOrNull(); } + + @NonNull + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + return mProxyOnBackInvokedDispatcher; + } } diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index 4b89bf5082ba..3ab9a335c8b7 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -15,12 +15,10 @@ */ package com.android.internal.view; -import android.annotation.NonNull; import android.annotation.Nullable; import android.view.InputQueue; import android.view.PendingInsetsController; import android.view.SurfaceHolder; -import android.window.WindowOnBackInvokedDispatcher; /** hahahah */ public interface RootViewSurfaceTaker { @@ -31,6 +29,4 @@ public interface RootViewSurfaceTaker { InputQueue.Callback willYouTakeTheInputQueue(); void onRootViewScrollYChanged(int scrollY); @Nullable PendingInsetsController providePendingInsetsController(); - /** @hide */ - @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher(); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 8bb9a0a0d6ff..955f46b977b7 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -309,8 +309,6 @@ cc_library_shared { "libdl_android", "libtimeinstate", "server_configurable_flags", - // TODO: delete when ConnectivityT moves to APEX. - "libframework-connectivity-tiramisu-jni", ], export_shared_lib_headers: [ // our headers include libnativewindow's public headers 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_Binder.cpp b/core/jni/android_util_Binder.cpp index 61b91ddaa2e7..13ca13322cc7 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -873,7 +873,7 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, const char* exceptionToThrow; char msg[128]; // TransactionTooLargeException is a checked exception, only throw from certain methods. - // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION + // TODO(b/28321379): Transaction size is the most common cause for FAILED_TRANSACTION // but it is not the only one. The Binder driver can return BR_FAILED_REPLY // for other reasons also, such as if the transaction is malformed or // refers to an FD that has been closed. We should change the driver @@ -890,8 +890,9 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, exceptionToThrow = (canThrowRemoteException) ? "android/os/DeadObjectException" : "java/lang/RuntimeException"; - snprintf(msg, sizeof(msg)-1, - "Transaction failed on small parcel; remote process probably died"); + snprintf(msg, sizeof(msg) - 1, + "Transaction failed on small parcel; remote process probably died, but " + "this could also be caused by running out of binder buffer space"); } jniThrowException(env, exceptionToThrow, msg); } break; 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/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index aacf700b1168..5efc4db52623 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -202,6 +202,8 @@ static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000; */ static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5; +static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn); + /** * A helper class containing accounting information for USAPs. */ @@ -1249,7 +1251,11 @@ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_d auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name); auto cePath = StringPrintf("%s/user", volPath.c_str()); auto dePath = StringPrintf("%s/user_de", volPath.c_str()); + // Wait until dir user is created. + WaitUntilDirReady(cePath.c_str(), fail_fn); MountAppDataTmpFs(cePath.c_str(), fail_fn); + // Wait until dir user_de is created. + WaitUntilDirReady(dePath.c_str(), fail_fn); MountAppDataTmpFs(dePath.c_str(), fail_fn); } closedir(dir); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9db715bfc4e1..74bf152344c3 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" /> @@ -977,6 +978,61 @@ android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> + <!-- Required to be able to read audio files from shared storage. + <p>Protection level: dangerous --> + <permission-group android:name="android.permission-group.READ_MEDIA_AURAL" + android:icon="@drawable/perm_group_read_media_aural" + android:label="@string/permgrouplab_readMediaAural" + android:description="@string/permgroupdesc_readMediaAural" + android:priority="950" /> + + <!-- Allows an application to read audio files from external storage. + <p>This permission is enforced starting in API level + {@link android.os.Build.VERSION_CODES#TIRAMISU}. + For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code + targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission + must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + <p>Protection level: dangerous --> + <permission android:name="android.permission.READ_MEDIA_AUDIO" + android:permissionGroup="android.permission-group.UNDEFINED" + android:label="@string/permlab_readMediaAudio" + android:description="@string/permdesc_readMediaAudio" + android:protectionLevel="dangerous" /> + + <!-- Required to be able to read image and video files from shared storage. + <p>Protection level: dangerous --> + <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL" + android:icon="@drawable/perm_group_read_media_visual" + android:label="@string/permgrouplab_readMediaVisual" + android:description="@string/permgroupdesc_readMediaVisual" + android:priority="1000" /> + + <!-- Allows an application to read audio files from external storage. + <p>This permission is enforced starting in API level + {@link android.os.Build.VERSION_CODES#TIRAMISU}. + For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code + targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission + must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + <p>Protection level: dangerous --> + <permission android:name="android.permission.READ_MEDIA_VIDEO" + android:permissionGroup="android.permission-group.UNDEFINED" + android:label="@string/permlab_readMediaVideo" + android:description="@string/permdesc_readMediaVideo" + android:protectionLevel="dangerous" /> + + <!-- Allows an application to read image files from external storage. + <p>This permission is enforced starting in API level + {@link android.os.Build.VERSION_CODES#TIRAMISU}. + For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code + targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission + must not be used and the READ_EXTERNAL_STORAGE permission must be used instead. + <p>Protection level: dangerous --> + <permission android:name="android.permission.READ_MEDIA_IMAGE" + android:permissionGroup="android.permission-group.UNDEFINED" + android:label="@string/permlab_readMediaImage" + android:description="@string/permdesc_readMediaImage" + android:protectionLevel="dangerous" /> + <!-- Allows an application to write to external storage. <p class="note"><strong>Note:</strong> If <em>both</em> your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code @@ -2027,6 +2083,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 --> <!-- ======================================= --> @@ -6554,6 +6615,13 @@ 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/drawable/perm_group_read_media_aural.xml b/core/res/res/drawable/perm_group_read_media_aural.xml new file mode 100644 index 000000000000..6fc9c69254cc --- /dev/null +++ b/core/res/res/drawable/perm_group_read_media_aural.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M10,21q-1.65,0 -2.825,-1.175Q6,18.65 6,17q0,-1.65 1.175,-2.825Q8.35,13 10,13q0.575,0 1.063,0.137 0.487,0.138 0.937,0.413V3h6v4h-4v10q0,1.65 -1.175,2.825Q11.65,21 10,21z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/perm_group_read_media_visual.xml b/core/res/res/drawable/perm_group_read_media_visual.xml new file mode 100644 index 000000000000..a5db2718c983 --- /dev/null +++ b/core/res/res/drawable/perm_group_read_media_visual.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 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" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/> +</vector>
\ No newline at end of file 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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 3a2fb6e70ba8..cb40e86f1535 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2839,6 +2839,14 @@ <attr name="path" /> <attr name="minSdkVersion" /> <attr name="maxSdkVersion" /> + <!-- The order in which the apex system services are initiated. When there are dependencies + among apex system services, setting this attribute for each of them ensures that they are + created in the order required by those dependencies. The apex-system-services that are + started manually within SystemServer ignore the initOrder and are not considered for + automatic starting of the other services. + The value is a simple integer, with higher number being initialized first. If not specified, + the default order is 0. --> + <attr name="initOrder" format="integer" /> </declare-styleable> <!-- The <code>receiver</code> tag declares an diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 59d6005ba193..54325e590347 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -448,4 +448,7 @@ <color name="accessibility_magnification_background">#F50D60</color> <color name="accessibility_daltonizer_background">#00BCD4</color> <color name="accessibility_color_inversion_background">#546E7A</color> + + <!-- Color of camera light when camera is in use --> + <color name="camera_privacy_light">#FFFFFF</color> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0dbd4175b716..47b4d38cf0ce 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -876,9 +876,19 @@ <string name="permgroupdesc_sms">send and view SMS messages</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgrouplab_storage">Files and media</string> + <string name="permgrouplab_storage">Files & documents</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgroupdesc_storage">access photos, media, and files on your device</string> + <string name="permgroupdesc_storage">access files and documents on your device</string> + + <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]--> + <string name="permgrouplab_readMediaAural">Music & other audio</string> + <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]--> + <string name="permgroupdesc_readMediaAural">access audio files on your device</string> + + <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]--> + <string name="permgrouplab_readMediaVisual">Photos & videos</string> + <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]--> + <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_microphone">Microphone</string> @@ -1893,6 +1903,21 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permlab_readMediaAudio">read audio files from shared storage</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permdesc_readMediaAudio">Allows the app to read audio files from your shared storage.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permlab_readMediaVideo">read video files from shared storage</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permdesc_readMediaVideo">Allows the app to read video files from your shared storage.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permlab_readMediaImage">read image files from shared storage</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] --> + <string name="permdesc_readMediaImage">Allows the app to read image files from your shared storage.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] --> @@ -5439,15 +5464,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] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 764b2738bce8..2b25c3eb099b 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" /> @@ -4725,4 +4722,6 @@ <java-symbol type="bool" name="config_lowPowerStandbySupported" /> <java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" /> <java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" /> + + <java-symbol type="color" name="camera_privacy_light"/> </resources> diff --git a/core/tests/bandwidthtests/Android.bp b/core/tests/bandwidthtests/Android.bp index f1ecd45073eb..d0b42f783bef 100644 --- a/core/tests/bandwidthtests/Android.bp +++ b/core/tests/bandwidthtests/Android.bp @@ -23,6 +23,7 @@ package { android_test { name: "BandwidthTests", + defaults: ["framework-connectivity-test-defaults"], // Include all test java files. srcs: ["src/**/*.java"], libs: [ diff --git a/core/tests/benchmarks/Android.bp b/core/tests/benchmarks/Android.bp index 4cd546753dbf..0888776f1683 100644 --- a/core/tests/benchmarks/Android.bp +++ b/core/tests/benchmarks/Android.bp @@ -27,6 +27,7 @@ package { java_library { name: "frameworks-base-core-benchmarks", + defaults: ["framework-connectivity-test-defaults"], installable: true, srcs: ["src/**/*.java"], libs: ["caliper-api-target"], diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt index 3c8f90c9c0f8..6360a2deb544 100644 --- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt +++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt @@ -16,7 +16,9 @@ package android.net +import android.net.NetworkStats.METERED_YES import android.net.NetworkTemplate.MATCH_BLUETOOTH +import android.net.NetworkTemplate.MATCH_CARRIER import android.net.NetworkTemplate.MATCH_ETHERNET import android.net.NetworkTemplate.MATCH_MOBILE import android.net.NetworkTemplate.MATCH_WIFI @@ -39,11 +41,19 @@ class NetworkPolicyTest { @Test fun testTemplateBackupRestore() { assertPolicyBackupRestore(createTestPolicyForTemplate( - NetworkTemplate.buildTemplateWifi(TEST_WIFI_NETWORK_KEY1))) + NetworkTemplate.Builder(MATCH_WIFI) + .setWifiNetworkKeys(setOf(TEST_WIFI_NETWORK_KEY1)) + .build())) assertPolicyBackupRestore(createTestPolicyForTemplate( - NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1))) + NetworkTemplate.Builder(MATCH_MOBILE) + .setSubscriberIds(setOf(TEST_IMSI1)) + .setMeteredness(METERED_YES) + .build())) assertPolicyBackupRestore(createTestPolicyForTemplate( - NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI1))) + NetworkTemplate.Builder(MATCH_CARRIER) + .setSubscriberIds(setOf(TEST_IMSI1)) + .setMeteredness(METERED_YES) + .build())) } private fun createTestPolicyForTemplate(template: NetworkTemplate): NetworkPolicy { diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java index 6df9002608af..ddc27aacc86f 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java @@ -42,7 +42,7 @@ public class AccessibilityEventTest { // and assertAccessibilityEventCleared /** The number of properties of the {@link AccessibilityEvent} class. */ - private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 32; + private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33; // The number of fields tested in the corresponding CTS AccessibilityRecordTest: // assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord, 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/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index c20293b2443b..95225b2e4f39 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -16,6 +16,9 @@ package com.android.internal.os; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; @@ -94,7 +97,8 @@ public class MobileRadioPowerCalculatorTest { // Note application network activity NetworkStats networkStats = new NetworkStats(10000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)); mStatsRule.setNetworkStats(networkStats); ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, @@ -160,7 +164,8 @@ public class MobileRadioPowerCalculatorTest { // Note application network activity mStatsRule.setNetworkStats(new NetworkStats(10000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100))); stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000, mNetworkStatsManager); @@ -169,7 +174,8 @@ public class MobileRadioPowerCalculatorTest { BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); mStatsRule.setNetworkStats(new NetworkStats(12000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200)); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200))); stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000, mNetworkStatsManager); @@ -241,7 +247,8 @@ public class MobileRadioPowerCalculatorTest { // Note application network activity NetworkStats networkStats = new NetworkStats(10000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)); mStatsRule.setNetworkStats(networkStats); ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, @@ -306,7 +313,8 @@ public class MobileRadioPowerCalculatorTest { // Note application network activity mStatsRule.setNetworkStats(new NetworkStats(10000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100))); stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager); @@ -314,7 +322,8 @@ public class MobileRadioPowerCalculatorTest { BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); mStatsRule.setNetworkStats(new NetworkStats(12000, 1) - .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200)); + .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200))); stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager); diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index a36839910742..f74e72becf4a 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -17,6 +17,9 @@ package com.android.internal.os; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; @@ -77,8 +80,12 @@ public class WifiPowerCalculatorTest { private NetworkStats buildNetworkStats(long elapsedRealtime, int rxBytes, int rxPackets, int txBytes, int txPackets) { return new NetworkStats(elapsedRealtime, 1) - .insertEntry("wifi", APP_UID, 0, 0, rxBytes, rxPackets, txBytes, txPackets, 100) - .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + .addEntry(new NetworkStats.Entry("wifi", APP_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, rxPackets, + txBytes, txPackets, 100)) + .addEntry(new NetworkStats.Entry("wifi", Process.WIFI_UID, 0, 0, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1111, 111, + 2222, 22, 111)); } /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */ diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp index d40d1d2bb6e2..1d5c16c7a536 100644 --- a/core/tests/utillib/Android.bp +++ b/core/tests/utillib/Android.bp @@ -23,6 +23,7 @@ package { java_library { name: "frameworks-core-util-lib", + defaults: ["framework-connectivity-test-defaults"], srcs: ["**/*.java"], diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 92fca3661fbc..88920c865511 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -231,6 +231,18 @@ targetSdk="29"> <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" /> </split-permission> + <split-permission name="android.permission.READ_EXTERNAL_STORAGE" + targetSdk="33"> + <new-permission name="android.permission.READ_MEDIA_AUDIO" /> + </split-permission> + <split-permission name="android.permission.READ_EXTERNAL_STORAGE" + targetSdk="33"> + <new-permission name="android.permission.READ_MEDIA_VIDEO" /> + </split-permission> + <split-permission name="android.permission.READ_EXTERNAL_STORAGE" + targetSdk="33"> + <new-permission name="android.permission.READ_MEDIA_IMAGE" /> + </split-permission> <split-permission name="android.permission.BLUETOOTH" targetSdk="31"> <new-permission name="android.permission.BLUETOOTH_SCAN" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index a331b6eb6750..83c4024e7867 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -334,6 +334,7 @@ applications that come with the platform <permission name="android.permission.MANAGE_ACCESSIBILITY"/> <permission name="android.permission.MANAGE_DEVICE_ADMINS"/> <permission name="android.permission.MANAGE_GAME_MODE"/> + <permission name="android.permission.MANAGE_GAME_ACTIVITY" /> <permission name="android.permission.MANAGE_LOW_POWER_STANDBY" /> <permission name="android.permission.MANAGE_ROLLBACKS"/> <permission name="android.permission.MANAGE_USB"/> @@ -400,6 +401,7 @@ applications that come with the platform <!-- Permission required for CTS test - TrustTestCases --> <permission name="android.permission.PROVIDE_TRUST_AGENT" /> <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <permission name="android.permission.TRUST_LISTENER" /> <!-- Permissions required for Incremental CTS tests --> <permission name="com.android.permission.USE_INSTALLER_V2"/> <permission name="android.permission.LOADER_USAGE_STATS"/> 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/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 373228586161..1629b6ace35d 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -368,7 +368,7 @@ public final class ImageDecoder implements AutoCloseable { * Further, unlike other Sources, this one is not reusable. */ private static class InputStreamSource extends Source { - InputStreamSource(Resources res, InputStream is, int inputDensity) { + InputStreamSource(Resources res, @NonNull InputStream is, int inputDensity) { if (is == null) { throw new IllegalArgumentException("The InputStream cannot be null"); } @@ -1020,7 +1020,7 @@ public final class ImageDecoder implements AutoCloseable { */ @AnyThread @NonNull - public static Source createSource(Resources res, InputStream is) { + public static Source createSource(Resources res, @NonNull InputStream is) { return new InputStreamSource(res, is, Bitmap.getDefaultDensity()); } @@ -1034,7 +1034,7 @@ public final class ImageDecoder implements AutoCloseable { @AnyThread @TestApi @NonNull - public static Source createSource(Resources res, InputStream is, int density) { + public static Source createSource(Resources res, @NonNull InputStream is, int density) { return new InputStreamSource(res, is, density); } diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 57046f5de61d..2ff888b06dd8 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -34,8 +34,6 @@ public class RuntimeShader extends Shader { RuntimeShader.class.getClassLoader(), nativeGetFinalizer()); } - private boolean mForceOpaque; - /** * Current native shader builder instance. */ @@ -47,33 +45,17 @@ public class RuntimeShader extends Shader { * @param shader The text of AGSL shader program to run. */ public RuntimeShader(@NonNull String shader) { - this(shader, false); - } - - /** - * Creates a new RuntimeShader. - * - * @param shader The text of AGSL shader program to run. - * @param forceOpaque If true then all pixels produced by the AGSL shader program will have an - * alpha of 1.0f. - */ - public RuntimeShader(@NonNull String shader, boolean forceOpaque) { // colorspace is required, but the RuntimeShader always produces colors in the destination // buffer's colorspace regardless of the value specified here. super(ColorSpace.get(ColorSpace.Named.SRGB)); if (shader == null) { throw new NullPointerException("RuntimeShader requires a non-null AGSL string"); } - mForceOpaque = forceOpaque; mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(shader); NoImagePreloadHolder.sRegistry.registerNativeAllocation( this, mNativeInstanceRuntimeShaderBuilder); } - public boolean isForceOpaque() { - return mForceOpaque; - } - /** * Sets the uniform color value corresponding to this shader. If the shader does not have a * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and @@ -322,7 +304,7 @@ public class RuntimeShader extends Shader { /** @hide */ @Override protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { - return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mForceOpaque); + return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix); } /** @hide */ @@ -332,8 +314,7 @@ public class RuntimeShader extends Shader { private static native long nativeGetFinalizer(); private static native long nativeCreateBuilder(String agsl); - private static native long nativeCreateShader( - long shaderBuilder, long matrix, boolean isOpaque); + private static native long nativeCreateShader(long shaderBuilder, long matrix); private static native void nativeUpdateUniforms( long shaderBuilder, String uniformName, float[] uniforms, boolean isColor); private static native void nativeUpdateUniforms( diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 28b3b04b827d..4972e928dd22 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -288,8 +288,7 @@ public abstract class Drawable { * * @return A copy of the drawable's bounds */ - @NonNull - public final Rect copyBounds() { + public final @NonNull Rect copyBounds() { return new Rect(mBounds); } @@ -308,8 +307,7 @@ public abstract class Drawable { * @see #copyBounds() * @see #copyBounds(android.graphics.Rect) */ - @NonNull - public final Rect getBounds() { + public final @NonNull Rect getBounds() { if (mBounds == ZERO_BOUNDS_RECT) { mBounds = new Rect(); } @@ -327,8 +325,7 @@ public abstract class Drawable { * * @return The dirty bounds of this drawable */ - @NonNull - public Rect getDirtyBounds() { + public @NonNull Rect getDirtyBounds() { return getBounds(); } @@ -457,8 +454,7 @@ public abstract class Drawable { * * @see #setCallback(android.graphics.drawable.Drawable.Callback) */ - @Nullable - public Callback getCallback() { + public @Nullable Callback getCallback() { return mCallback != null ? mCallback.get() : null; } @@ -569,8 +565,7 @@ public abstract class Drawable { * The default return value is 255 if the class does not override this method to return a value * specific to its use of alpha. */ - @IntRange(from=0,to=255) - public int getAlpha() { + public @IntRange(from=0,to=255) int getAlpha() { return 0xFF; } @@ -999,7 +994,8 @@ public abstract class Drawable { * * @see android.graphics.PixelFormat */ - @Deprecated public abstract @PixelFormat.Opacity int getOpacity(); + @Deprecated + public abstract @PixelFormat.Opacity int getOpacity(); /** * Return the appropriate opacity value for two source opacities. If @@ -1059,7 +1055,7 @@ public abstract class Drawable { * if it looks the same and there is no need to redraw it since its * last state. */ - protected boolean onStateChange(int[] state) { + protected boolean onStateChange(@NonNull int[] state) { return false; } @@ -1078,7 +1074,7 @@ public abstract class Drawable { * Override this in your subclass to change appearance if you vary based on * the bounds. */ - protected void onBoundsChange(Rect bounds) { + protected void onBoundsChange(@NonNull Rect bounds) { // Stub method. } @@ -1209,7 +1205,8 @@ public abstract class Drawable { /** * Create a drawable from an inputstream */ - public static Drawable createFromStream(InputStream is, String srcName) { + public static @Nullable Drawable createFromStream(@Nullable InputStream is, + @Nullable String srcName) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable"); try { return createFromResourceStream(null, null, is, srcName); @@ -1222,8 +1219,8 @@ public abstract class Drawable { * Create a drawable from an inputstream, using the given resources and * value to determine density information. */ - public static Drawable createFromResourceStream(Resources res, TypedValue value, - InputStream is, String srcName) { + public static @Nullable Drawable createFromResourceStream(@Nullable Resources res, + @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable"); try { return createFromResourceStream(res, value, is, srcName, null); @@ -1238,8 +1235,7 @@ public abstract class Drawable { * * @deprecated Prefer the version without an Options object. */ - @Nullable - public static Drawable createFromResourceStream(@Nullable Resources res, + public static @Nullable Drawable createFromResourceStream(@Nullable Resources res, @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName, @Nullable BitmapFactory.Options opts) { if (is == null) { @@ -1281,7 +1277,8 @@ public abstract class Drawable { return null; } - private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + private static Drawable getBitmapDrawable(Resources res, @Nullable TypedValue value, + @NonNull InputStream is) { try { ImageDecoder.Source source = null; if (value != null) { @@ -1369,9 +1366,9 @@ public abstract class Drawable { * a tag in an XML document, tries to create a Drawable from that tag. * Returns null if the tag is not a valid drawable. */ - @NonNull - public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser, - @NonNull AttributeSet attrs) throws XmlPullParserException, IOException { + public static @NonNull Drawable createFromXmlInner(@NonNull Resources r, + @NonNull XmlPullParser parser, @NonNull AttributeSet attrs) + throws XmlPullParserException, IOException { return createFromXmlInner(r, parser, attrs, null); } @@ -1381,9 +1378,8 @@ public abstract class Drawable { * document, tries to create a Drawable from that tag. Returns {@code null} * if the tag is not a valid drawable. */ - @NonNull - public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser, - @NonNull AttributeSet attrs, @Nullable Theme theme) + public static @NonNull Drawable createFromXmlInner(@NonNull Resources r, + @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { return createFromXmlInnerForDensity(r, parser, attrs, 0, theme); } @@ -1392,8 +1388,7 @@ public abstract class Drawable { * Version of {@link #createFromXmlInner(Resources, XmlPullParser, AttributeSet, Theme)} that * accepts an override density. */ - @NonNull - static Drawable createFromXmlInnerForDensity(@NonNull Resources r, + static @NonNull Drawable createFromXmlInnerForDensity(@NonNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, int density, @Nullable Theme theme) throws XmlPullParserException, IOException { return r.getDrawableInflater().inflateFromXmlForDensity(parser.getName(), parser, attrs, @@ -1403,8 +1398,7 @@ public abstract class Drawable { /** * Create a drawable from file path name. */ - @Nullable - public static Drawable createFromPath(String pathName) { + public static @Nullable Drawable createFromPath(String pathName) { if (pathName == null) { return null; } diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java index 66752a2536d3..8debe26b8c6a 100644 --- a/graphics/java/android/graphics/drawable/DrawableInflater.java +++ b/graphics/java/android/graphics/drawable/DrawableInflater.java @@ -61,8 +61,7 @@ public final class DrawableInflater { * @param id the identifier of the drawable resource * @return a drawable, or {@code null} if the drawable failed to load */ - @Nullable - public static Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) { + public static @Nullable Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) { return loadDrawable(context.getResources(), context.getTheme(), id); } @@ -74,9 +73,8 @@ public final class DrawableInflater { * @param id the identifier of the drawable resource * @return a drawable, or {@code null} if the drawable failed to load */ - @Nullable - public static Drawable loadDrawable( - @NonNull Resources resources, @Nullable Theme theme, @DrawableRes int id) { + public static @Nullable Drawable loadDrawable(@NonNull Resources resources, + @Nullable Theme theme, @DrawableRes int id) { return resources.getDrawable(id, theme); } @@ -111,8 +109,7 @@ public final class DrawableInflater { * @throws XmlPullParserException * @throws IOException */ - @NonNull - public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser, + public @NonNull Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme) throws XmlPullParserException, IOException { return inflateFromXmlForDensity(name, parser, attrs, 0, theme); @@ -122,8 +119,7 @@ public final class DrawableInflater { * Version of {@link #inflateFromXml(String, XmlPullParser, AttributeSet, Theme)} that accepts * an override density. */ - @NonNull - Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser, + @NonNull Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, int density, @Nullable Theme theme) throws XmlPullParserException, IOException { // Inner classes must be referenced as Outer$Inner, but XML tag names @@ -146,9 +142,8 @@ public final class DrawableInflater { return drawable; } - @NonNull @SuppressWarnings("deprecation") - private Drawable inflateFromTag(@NonNull String name) { + private @Nullable Drawable inflateFromTag(@NonNull String name) { switch (name) { case "selector": return new StateListDrawable(); @@ -195,8 +190,7 @@ public final class DrawableInflater { } } - @NonNull - private Drawable inflateFromClass(@NonNull String className) { + private @NonNull Drawable inflateFromClass(@NonNull String className) { try { Constructor<? extends Drawable> constructor; synchronized (CONSTRUCTOR_MAP) { diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java index ebde75775e84..a63d7f660801 100644 --- a/graphics/java/android/graphics/drawable/DrawableWrapper.java +++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java @@ -352,7 +352,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb } @Override - protected boolean onStateChange(int[] state) { + protected boolean onStateChange(@NonNull int[] state) { if (mDrawable != null && mDrawable.isStateful()) { final boolean changed = mDrawable.setState(state); if (changed) { diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index a03931bfab24..b04b82629b92 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -302,7 +302,7 @@ public final class Icon implements Parcelable { * is available. The {@link android.os.Message#obj obj} * property is populated with the Drawable. */ - public void loadDrawableAsync(Context context, Message andThen) { + public void loadDrawableAsync(@NonNull Context context, @NonNull Message andThen) { if (andThen.getTarget() == null) { throw new IllegalArgumentException("callback message must have a target handler"); } @@ -320,7 +320,7 @@ public final class Icon implements Parcelable { * {@link #loadDrawable(Context)} finished * @param handler {@link Handler} on which to notify the {@code listener} */ - public void loadDrawableAsync(Context context, final OnDrawableLoadedListener listener, + public void loadDrawableAsync(@NonNull Context context, final OnDrawableLoadedListener listener, Handler handler) { new LoadDrawableTask(context, handler, listener).runAsync(); } @@ -335,7 +335,7 @@ public final class Icon implements Parcelable { * to access {@link android.content.res.Resources Resources}, for example. * @return A fresh instance of a drawable for this image, yours to keep. */ - public Drawable loadDrawable(Context context) { + public @Nullable Drawable loadDrawable(Context context) { final Drawable result = loadDrawableInner(context); if (result != null && hasTint()) { result.mutate(); @@ -415,7 +415,7 @@ public final class Icon implements Parcelable { return null; } - private InputStream getUriInputStream(Context context) { + private @Nullable InputStream getUriInputStream(Context context) { final Uri uri = getUri(); final String scheme = uri.getScheme(); if (ContentResolver.SCHEME_CONTENT.equals(scheme) @@ -496,7 +496,7 @@ public final class Icon implements Parcelable { * @param stream The stream on which to serialize the Icon. * @hide */ - public void writeToStream(OutputStream stream) throws IOException { + public void writeToStream(@NonNull OutputStream stream) throws IOException { DataOutputStream dataStream = new DataOutputStream(stream); dataStream.writeInt(VERSION_STREAM_SERIALIZER); @@ -532,7 +532,7 @@ public final class Icon implements Parcelable { * @param stream The input stream from which to reconstruct the Icon. * @hide */ - public static Icon createFromStream(InputStream stream) throws IOException { + public static @Nullable Icon createFromStream(@NonNull InputStream stream) throws IOException { DataInputStream inputStream = new DataInputStream(stream); final int version = inputStream.readInt(); @@ -571,7 +571,7 @@ public final class Icon implements Parcelable { * @return whether this icon is the same as the another one * @hide */ - public boolean sameAs(Icon otherIcon) { + public boolean sameAs(@NonNull Icon otherIcon) { if (otherIcon == this) { return true; } @@ -602,7 +602,7 @@ public final class Icon implements Parcelable { * given resource ID. * @param resId ID of the drawable resource */ - public static Icon createWithResource(Context context, @DrawableRes int resId) { + public static @NonNull Icon createWithResource(Context context, @DrawableRes int resId) { if (context == null) { throw new IllegalArgumentException("Context must not be null."); } @@ -617,7 +617,7 @@ public final class Icon implements Parcelable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static Icon createWithResource(Resources res, @DrawableRes int resId) { + public static @NonNull Icon createWithResource(Resources res, @DrawableRes int resId) { if (res == null) { throw new IllegalArgumentException("Resource must not be null."); } @@ -632,7 +632,7 @@ public final class Icon implements Parcelable { * @param resPackage Name of the package containing the resource in question * @param resId ID of the drawable resource */ - public static Icon createWithResource(String resPackage, @DrawableRes int resId) { + public static @NonNull Icon createWithResource(String resPackage, @DrawableRes int resId) { if (resPackage == null) { throw new IllegalArgumentException("Resource package name must not be null."); } @@ -646,7 +646,7 @@ public final class Icon implements Parcelable { * Create an Icon pointing to a bitmap in memory. * @param bits A valid {@link android.graphics.Bitmap} object */ - public static Icon createWithBitmap(Bitmap bits) { + public static @NonNull Icon createWithBitmap(Bitmap bits) { if (bits == null) { throw new IllegalArgumentException("Bitmap must not be null."); } @@ -660,7 +660,7 @@ public final class Icon implements Parcelable { * by {@link AdaptiveIconDrawable}. * @param bits A valid {@link android.graphics.Bitmap} object */ - public static Icon createWithAdaptiveBitmap(Bitmap bits) { + public static @NonNull Icon createWithAdaptiveBitmap(Bitmap bits) { if (bits == null) { throw new IllegalArgumentException("Bitmap must not be null."); } @@ -677,7 +677,7 @@ public final class Icon implements Parcelable { * @param offset Offset into <code>data</code> at which the bitmap data starts * @param length Length of the bitmap data */ - public static Icon createWithData(byte[] data, int offset, int length) { + public static @NonNull Icon createWithData(byte[] data, int offset, int length) { if (data == null) { throw new IllegalArgumentException("Data must not be null."); } @@ -693,7 +693,7 @@ public final class Icon implements Parcelable { * * @param uri A uri referring to local content:// or file:// image data. */ - public static Icon createWithContentUri(String uri) { + public static @NonNull Icon createWithContentUri(String uri) { if (uri == null) { throw new IllegalArgumentException("Uri must not be null."); } @@ -707,7 +707,7 @@ public final class Icon implements Parcelable { * * @param uri A uri referring to local content:// or file:// image data. */ - public static Icon createWithContentUri(Uri uri) { + public static @NonNull Icon createWithContentUri(Uri uri) { if (uri == null) { throw new IllegalArgumentException("Uri must not be null."); } @@ -720,8 +720,7 @@ public final class Icon implements Parcelable { * * @param uri A uri referring to local content:// or file:// image data. */ - @NonNull - public static Icon createWithAdaptiveBitmapContentUri(@NonNull String uri) { + public static @NonNull Icon createWithAdaptiveBitmapContentUri(@NonNull String uri) { if (uri == null) { throw new IllegalArgumentException("Uri must not be null."); } @@ -750,7 +749,7 @@ public final class Icon implements Parcelable { * @param tint a color, as in {@link Drawable#setTint(int)} * @return this same object, for use in chained construction */ - public Icon setTint(@ColorInt int tint) { + public @NonNull Icon setTint(@ColorInt int tint) { return setTintList(ColorStateList.valueOf(tint)); } @@ -760,7 +759,7 @@ public final class Icon implements Parcelable { * @param tintList as in {@link Drawable#setTintList(ColorStateList)}, null to remove tint * @return this same object, for use in chained construction */ - public Icon setTintList(ColorStateList tintList) { + public @NonNull Icon setTintList(ColorStateList tintList) { mTintList = tintList; return this; } @@ -809,7 +808,7 @@ public final class Icon implements Parcelable { * @param path A path to a file that contains compressed bitmap data of * a type that {@link android.graphics.BitmapFactory} can decode. */ - public static Icon createWithFilePath(String path) { + public static @NonNull Icon createWithFilePath(String path) { if (path == null) { throw new IllegalArgumentException("Path must not be null."); } diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index 53a673122040..4461f39fd006 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -126,7 +126,7 @@ final class RippleShader extends RuntimeShader { private static final double PI_ROTATE_LEFT = Math.PI * -0.0078125; RippleShader() { - super(SHADER, false); + super(SHADER); } public void setShader(Shader shader) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index b310dd638e6c..9a6df23ca971 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -18,9 +18,12 @@ package com.android.wm.shell.back; import android.view.MotionEvent; +import com.android.wm.shell.common.annotations.ExternalThread; + /** - * Interface for SysUI to get access to the Back animation related methods. + * Interface for external process to get access to the Back animation related methods. */ +@ExternalThread public interface BackAnimation { /** @@ -32,4 +35,11 @@ public interface BackAnimation { * Sets whether the back gesture is past the trigger threshold or not. */ void setTriggerBack(boolean triggerBack); + + /** + * Returns a binder that can be passed to an external process to update back animations. + */ + default IBackAnimation createExternalInterface() { + return null; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 229e8ee04982..a5140c3aafff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.back; +import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import android.animation.Animator; @@ -26,6 +27,7 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; +import android.content.Context; import android.graphics.Point; import android.graphics.PointF; import android.hardware.HardwareBuffer; @@ -35,16 +37,18 @@ import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceControl; import android.window.BackNavigationInfo; +import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ShellMainThread; /** * Controls the window animation run when a user initiates a back gesture. */ -public class BackAnimationController { +public class BackAnimationController implements RemoteCallable<BackAnimationController> { private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; public static final boolean IS_ENABLED = SystemProperties @@ -72,18 +76,26 @@ public class BackAnimationController { private BackNavigationInfo mBackNavigationInfo; private final SurfaceControl.Transaction mTransaction; private final IActivityTaskManager mActivityTaskManager; + private final Context mContext; + @Nullable + private IOnBackInvokedCallback mBackToLauncherCallback; - public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) { - this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService()); + public BackAnimationController( + @ShellMainThread ShellExecutor shellExecutor, + Context context) { + this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService(), + context); } @VisibleForTesting BackAnimationController(@NonNull ShellExecutor shellExecutor, @NonNull SurfaceControl.Transaction transaction, - @NonNull IActivityTaskManager activityTaskManager) { + @NonNull IActivityTaskManager activityTaskManager, + Context context) { mShellExecutor = shellExecutor; mTransaction = transaction; mActivityTaskManager = activityTaskManager; + mContext = context; } public BackAnimation getBackAnimationImpl() { @@ -92,7 +104,27 @@ public class BackAnimationController { private final BackAnimation mBackAnimation = new BackAnimationImpl(); + @Override + public Context getContext() { + return mContext; + } + + @Override + public ShellExecutor getRemoteCallExecutor() { + return mShellExecutor; + } + private class BackAnimationImpl implements BackAnimation { + private IBackAnimationImpl mBackAnimation; + + @Override + public IBackAnimation createExternalInterface() { + if (mBackAnimation != null) { + mBackAnimation.invalidate(); + } + mBackAnimation = new IBackAnimationImpl(BackAnimationController.this); + return mBackAnimation; + } @Override public void onBackMotion(MotionEvent event) { @@ -105,6 +137,48 @@ public class BackAnimationController { } } + private static class IBackAnimationImpl extends IBackAnimation.Stub { + private BackAnimationController mController; + + IBackAnimationImpl(BackAnimationController controller) { + mController = controller; + } + + @Override + public void setBackToLauncherCallback(IOnBackInvokedCallback callback) { + executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback", + (controller) -> mController.setBackToLauncherCallback(callback)); + } + + @Override + public void clearBackToLauncherCallback() { + executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback", + (controller) -> mController.clearBackToLauncherCallback()); + } + + @Override + public void onBackToLauncherAnimationFinished() { + executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished", + (controller) -> mController.onBackToLauncherAnimationFinished()); + } + + void invalidate() { + mController = null; + } + } + + private void setBackToLauncherCallback(IOnBackInvokedCallback callback) { + mBackToLauncherCallback = callback; + } + + private void clearBackToLauncherCallback() { + mBackToLauncherCallback = null; + } + + private void onBackToLauncherAnimationFinished() { + finishAnimation(); + } + /** * Called when a new motion event needs to be transferred to this * {@link BackAnimationController} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl new file mode 100644 index 000000000000..6311f879fd45 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl @@ -0,0 +1,45 @@ +/* + * 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.back; + +import android.window.IOnBackInvokedCallback; + +/** + * Interface for Launcher process to register back invocation callbacks. + */ +interface IBackAnimation { + + /** + * Sets a {@link IOnBackInvokedCallback} to be invoked when + * back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}. + */ + void setBackToLauncherCallback(in IOnBackInvokedCallback callback); + + /** + * Clears the previously registered {@link IOnBackInvokedCallback}. + */ + void clearBackToLauncherCallback(); + + /** + * Notifies Shell that the back to launcher animation has fully finished + * (including the transition animation that runs after the finger is lifted). + * + * At this point the top window leash (if one was created) should be ready to be released. + * //TODO: Remove once we play the transition animation through shell transitions. + */ + void onBackToLauncherAnimationFinished(); +} 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/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 8f4cfb0a49a4..4d279bc4e927 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -49,7 +49,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; /** - * Controls to show/update restart-activity buttons on Tasks based on whether the foreground + * Controller to show/update compat UI components on Tasks based on whether the foreground * activities are in compatibility mode. */ public class CompatUIController implements OnDisplaysChangedListener, @@ -228,8 +228,7 @@ public class CompatUIController implements OnDisplaysChangedListener, final CompatUIWindowManager compatUIWindowManager = createLayout(context, taskInfo, taskListener); mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager); - compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), - taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState); + compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), taskInfo); } @VisibleForTesting @@ -254,9 +253,7 @@ public class CompatUIController implements OnDisplaysChangedListener, if (layout == null) { return; } - layout.updateCompatInfo(taskInfo.configuration, taskListener, - showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat, - taskInfo.cameraCompatControlState); + layout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(layout.getDisplayId())); } private void removeLayout(int taskId) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index 44526b00bf0d..9c001a37e4b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -20,28 +20,16 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED; import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN; import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED; import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; +import android.app.TaskInfo; import android.app.TaskInfo.CameraCompatControlState; import android.content.Context; import android.content.res.Configuration; -import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.Binder; import android.util.Log; -import android.view.IWindow; import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.View; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; @@ -50,26 +38,19 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; /** - * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat - * controls. + * Window manager for the Size Compat restart button and Camera Compat control. */ -class CompatUIWindowManager extends WindowlessWindowManager { +class CompatUIWindowManager extends CompatUIWindowManagerAbstract { - private static final String TAG = "CompatUIWindowManager"; + /** + * The Compat UI should be the topmost child of the Task in case there can be more than one + * child. + */ + private static final int Z_ORDER = Integer.MAX_VALUE; - private final SyncTransactionQueue mSyncQueue; private final CompatUIController.CompatUICallback mCallback; - private final int mDisplayId; - private final int mTaskId; - private final Rect mStableBounds; - private Context mContext; - private Configuration mTaskConfig; - private ShellTaskOrganizer.TaskListener mTaskListener; - private DisplayLayout mDisplayLayout; - - // Remember the last reported states in case visibility changes due to keyguard or - // IME updates. + // Remember the last reported states in case visibility changes due to keyguard or IME updates. @VisibleForTesting boolean mHasSizeCompat; @CameraCompatControlState @@ -82,147 +63,83 @@ class CompatUIWindowManager extends WindowlessWindowManager { @Nullable @VisibleForTesting - CompatUILayout mCompatUILayout; - - @Nullable - private SurfaceControlViewHost mViewHost; - @Nullable - private SurfaceControl mLeash; + CompatUILayout mLayout; CompatUIWindowManager(Context context, Configuration taskConfig, SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback, int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, - boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) { - super(taskConfig, null /* rootSurface */, null /* hostInputToken */); - mContext = context; - mSyncQueue = syncQueue; + boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) { + super(context, taskConfig, syncQueue, taskId, taskListener, displayLayout); mCallback = callback; - mTaskConfig = taskConfig; - mDisplayId = mContext.getDisplayId(); - mTaskId = taskId; - mTaskListener = taskListener; - mDisplayLayout = displayLayout; mShouldShowSizeCompatHint = !hasShownSizeCompatHint; mShouldShowCameraCompatHint = !hasShownCameraCompatHint; - mStableBounds = new Rect(); - mDisplayLayout.getStableBounds(mStableBounds); } @Override - public void setConfiguration(Configuration configuration) { - super.setConfiguration(configuration); - mContext = mContext.createConfigurationContext(configuration); + protected int getZOrder() { + return Z_ORDER; + } + + + @Override + protected @Nullable View getLayout() { + return mLayout; } @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("CompatUILeash") - .setHidden(false) - .setCallsite("CompatUIWindowManager#attachToParentSurface"); - attachToParentSurface(builder); - mLeash = builder.build(); - b.setParent(mLeash); + protected void removeLayout() { + mLayout = null; } - /** Creates the layout for compat controls. */ - void createLayout(boolean show, boolean hasSizeCompat, - @CameraCompatControlState int cameraCompatControlState) { - mHasSizeCompat = hasSizeCompat; - mCameraCompatControlState = cameraCompatControlState; - if (!show || mCompatUILayout != null) { - // Wait until compat controls should be visible. - return; - } + @Override + protected boolean eligibleToShowLayout() { + return mHasSizeCompat || shouldShowCameraControl(); + } + + /** + * Updates the internal state with respect to {@code taskInfo} and calls {@link + * #createLayout(boolean)}. + */ + void createLayout(boolean canShow, TaskInfo taskInfo) { + mHasSizeCompat = taskInfo.topActivityInSizeCompat; + mCameraCompatControlState = taskInfo.cameraCompatControlState; + createLayout(canShow); + } + + @Override + protected View createLayout() { + mLayout = inflateLayout(); + mLayout.inject(this); - initCompatUi(); - updateSurfacePosition(); + updateVisibilityOfViews(); - if (hasSizeCompat) { + if (mHasSizeCompat) { mCallback.onSizeCompatRestartButtonAppeared(mTaskId); } + + return mLayout; } - private void createLayout(boolean show) { - createLayout(show, mHasSizeCompat, mCameraCompatControlState); + @VisibleForTesting + CompatUILayout inflateLayout() { + return (CompatUILayout) LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, + null); } - /** Called when compat info changed. */ - void updateCompatInfo(Configuration taskConfig, - ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat, - @CameraCompatControlState int cameraCompatControlState) { - final Configuration prevTaskConfig = mTaskConfig; - final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; - mTaskConfig = taskConfig; - mTaskListener = taskListener; + @Override + public void updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener, + boolean canShow) { final boolean prevHasSizeCompat = mHasSizeCompat; final int prevCameraCompatControlState = mCameraCompatControlState; - mHasSizeCompat = hasSizeCompat; - mCameraCompatControlState = cameraCompatControlState; + mHasSizeCompat = taskInfo.topActivityInSizeCompat; + mCameraCompatControlState = taskInfo.cameraCompatControlState; - // Update configuration. - mContext = mContext.createConfigurationContext(taskConfig); - setConfiguration(taskConfig); - - if (mCompatUILayout == null || prevTaskListener != taskListener) { - // TaskListener changed, recreate the layout for new surface parent. - release(); - createLayout(show); - return; - } + super.updateCompatInfo(taskInfo, taskListener, canShow); if (prevHasSizeCompat != mHasSizeCompat || prevCameraCompatControlState != mCameraCompatControlState) { updateVisibilityOfViews(); } - - if (!taskConfig.windowConfiguration.getBounds() - .equals(prevTaskConfig.windowConfiguration.getBounds())) { - // Reposition the UI surfaces. - updateSurfacePosition(); - } - - if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { - // Update layout for RTL. - mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection()); - updateSurfacePosition(); - } - - } - - /** Called when the visibility of the UI should change. */ - void updateVisibility(boolean show) { - if (mCompatUILayout == null) { - // Layout may not have been created because it was hidden previously. - createLayout(show); - return; - } - - // Hide compat UIs when IME is showing. - final int newVisibility = show ? View.VISIBLE : View.GONE; - if (mCompatUILayout.getVisibility() != newVisibility) { - mCompatUILayout.setVisibility(newVisibility); - } - } - - /** Called when display layout changed. */ - void updateDisplayLayout(DisplayLayout displayLayout) { - final Rect prevStableBounds = mStableBounds; - final Rect curStableBounds = new Rect(); - displayLayout.getStableBounds(curStableBounds); - mDisplayLayout = displayLayout; - if (!prevStableBounds.equals(curStableBounds)) { - // Stable bounds changed, update UI surface positions. - updateSurfacePosition(); - mStableBounds.set(curStableBounds); - } - } - - /** Called when it is ready to be placed compat UI surface. */ - void attachToParentSurface(SurfaceControl.Builder b) { - mTaskListener.attachChildSurfaceToTask(mTaskId, b); } /** Called when the restart button is clicked. */ @@ -233,7 +150,7 @@ class CompatUIWindowManager extends WindowlessWindowManager { /** Called when the camera treatment button is clicked. */ void onCameraTreatmentButtonClicked() { if (!shouldShowCameraControl()) { - Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state."); + Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state."); return; } // When a camera control is shown, only two states are allowed: "treament applied" and @@ -244,141 +161,72 @@ class CompatUIWindowManager extends WindowlessWindowManager { ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState); - mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState); + mLayout.updateCameraTreatmentButton(mCameraCompatControlState); } /** Called when the camera dismiss button is clicked. */ void onCameraDismissButtonClicked() { if (!shouldShowCameraControl()) { - Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state."); + Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state."); return; } mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED; mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED); - mCompatUILayout.setCameraControlVisibility(/* show= */ false); + mLayout.setCameraControlVisibility(/* show= */ false); } /** Called when the restart button is long clicked. */ void onRestartButtonLongClicked() { - if (mCompatUILayout == null) { + if (mLayout == null) { return; } - mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true); + mLayout.setSizeCompatHintVisibility(/* show= */ true); } /** Called when either dismiss or treatment camera buttons is long clicked. */ void onCameraButtonLongClicked() { - if (mCompatUILayout == null) { + if (mLayout == null) { return; } - mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true); + mLayout.setCameraCompatHintVisibility(/* show= */ true); } - int getDisplayId() { - return mDisplayId; - } - - int getTaskId() { - return mTaskId; - } - - /** Releases the surface control and tears down the view hierarchy. */ - void release() { - // Hiding before releasing to avoid flickering when transitioning to the Home screen. - mCompatUILayout.setVisibility(View.GONE); - mCompatUILayout = null; - - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - - if (mLeash != null) { - final SurfaceControl leash = mLeash; - mSyncQueue.runInSync(t -> t.remove(leash)); - mLeash = null; - } - } - - void relayout() { - mViewHost.relayout(getWindowLayoutParams()); - updateSurfacePosition(); - } - - @VisibleForTesting - void updateSurfacePosition() { - if (mCompatUILayout == null || mLeash == null) { + @Override + protected void updateSurfacePosition(Rect taskBounds, Rect stableBounds) { + if (mLayout == null) { return; } - - // Use stable bounds to prevent controls from overlapping with system bars. - final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); - final Rect stableBounds = new Rect(); - mDisplayLayout.getStableBounds(stableBounds); - stableBounds.intersect(taskBounds); - // Position of the button in the container coordinate. final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? stableBounds.left - taskBounds.left - : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth(); + : stableBounds.right - taskBounds.left - mLayout.getMeasuredWidth(); final int positionY = stableBounds.bottom - taskBounds.top - - mCompatUILayout.getMeasuredHeight(); + - mLayout.getMeasuredHeight(); updateSurfacePosition(positionX, positionY); } - private int getLayoutDirection() { - return mContext.getResources().getConfiguration().getLayoutDirection(); - } - - private void updateSurfacePosition(int positionX, int positionY) { - mSyncQueue.runInSync(t -> { - if (mLeash == null || !mLeash.isValid()) { - Log.w(TAG, "The leash has been released."); - return; - } - t.setPosition(mLeash, positionX, positionY); - // The compat UI should be the topmost child of the Task in case there can be more - // than one children. - t.setLayer(mLeash, Integer.MAX_VALUE); - }); - } - - /** Inflates {@link CompatUILayout} on to the root surface. */ - private void initCompatUi() { - if (mViewHost != null) { - throw new IllegalStateException( - "A UI has already been created with this window manager."); - } - - // Construction extracted into the separate methods to allow injection for tests. - mViewHost = createSurfaceViewHost(); - mCompatUILayout = inflateCompatUILayout(); - mCompatUILayout.inject(this); - - updateVisibilityOfViews(); - - mViewHost.setView(mCompatUILayout, getWindowLayoutParams()); - } - private void updateVisibilityOfViews() { + if (mLayout == null) { + return; + } // Size Compat mode restart button. - mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat); + mLayout.setRestartButtonVisibility(mHasSizeCompat); if (mHasSizeCompat && mShouldShowSizeCompatHint) { - mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true); + mLayout.setSizeCompatHintVisibility(/* show= */ true); // Only show by default for the first time. mShouldShowSizeCompatHint = false; } // Camera control for stretched issues. - mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl()); + mLayout.setCameraControlVisibility(shouldShowCameraControl()); if (shouldShowCameraControl() && mShouldShowCameraCompatHint) { - mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true); + mLayout.setCameraCompatHintVisibility(/* show= */ true); // Only show by default for the first time. mShouldShowCameraCompatHint = false; } if (shouldShowCameraControl()) { - mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState); + mLayout.updateCameraTreatmentButton(mCameraCompatControlState); } } @@ -386,32 +234,4 @@ class CompatUIWindowManager extends WindowlessWindowManager { return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED; } - - @VisibleForTesting - CompatUILayout inflateCompatUILayout() { - return (CompatUILayout) LayoutInflater.from(mContext) - .inflate(R.layout.compat_ui_layout, null); - } - - @VisibleForTesting - SurfaceControlViewHost createSurfaceViewHost() { - return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - } - - /** Gets the layout params. */ - private WindowManager.LayoutParams getWindowLayoutParams() { - // Measure how big the hint is since its size depends on the text size. - mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( - // Cannot be wrap_content as this determines the actual window size - mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(), - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - winParams.token = new Binder(); - winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId); - winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - return winParams; - } - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java new file mode 100644 index 000000000000..b9a9db1ee800 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -0,0 +1,364 @@ +/* + * 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; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.annotation.Nullable; +import android.app.TaskInfo; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Binder; +import android.util.Log; +import android.view.IWindow; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +/** + * A superclass for all Compat UI {@link WindowlessWindowManager}s that holds shared logic and + * exposes general API for {@link CompatUIController}. + * + * <p>Holds view hierarchy of a root surface and helps to inflate and manage layout. + */ +abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager { + + protected final SyncTransactionQueue mSyncQueue; + protected final int mDisplayId; + protected final int mTaskId; + + protected Context mContext; + protected Configuration mTaskConfig; + protected ShellTaskOrganizer.TaskListener mTaskListener; + protected DisplayLayout mDisplayLayout; + protected final Rect mStableBounds; + + /** + * Utility class for adding and releasing a View hierarchy for this {@link + * WindowlessWindowManager} to {@code mLeash}. + */ + @Nullable + protected SurfaceControlViewHost mViewHost; + + /** + * A surface leash to position the layout relative to the task, since we can't set position for + * the {@code mViewHost} directly. + */ + @Nullable + protected SurfaceControl mLeash; + + protected CompatUIWindowManagerAbstract(Context context, Configuration taskConfig, + SyncTransactionQueue syncQueue, int taskId, + ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) { + super(taskConfig, null /* rootSurface */, null /* hostInputToken */); + mContext = context; + mSyncQueue = syncQueue; + mTaskConfig = taskConfig; + mDisplayId = mContext.getDisplayId(); + mTaskId = taskId; + mTaskListener = taskListener; + mDisplayLayout = displayLayout; + mStableBounds = new Rect(); + mDisplayLayout.getStableBounds(mStableBounds); + } + + /** + * Returns the z-order of this window which will be passed to the {@link SurfaceControl} once + * {@link #attachToParentSurface} is called. + * + * <p>See {@link SurfaceControl.Transaction#setLayer}. + */ + protected abstract int getZOrder(); + + /** Returns the layout of this window manager. */ + protected abstract @Nullable View getLayout(); + + /** + * Inflates and inits the layout of this window manager on to the root surface if both {@code + * canShow} and {@link #eligibleToShowLayout} are true. + * + * @param canShow whether the layout is allowed to be shown by the parent controller. + */ + void createLayout(boolean canShow) { + if (!canShow || !eligibleToShowLayout() || getLayout() != null) { + // Wait until layout should be visible. + return; + } + + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); + } + + // Construction extracted into separate methods to allow injection for tests. + mViewHost = createSurfaceViewHost(); + mViewHost.setView(createLayout(), getWindowLayoutParams()); + + updateSurfacePosition(); + } + + /** Inflates and inits the layout of this window manager. */ + protected abstract View createLayout(); + + protected abstract void removeLayout(); + + /** + * Whether the layout is eligible to be shown according to the internal state of the subclass. + * Returns true by default if subclass doesn't override this method. + */ + protected boolean eligibleToShowLayout() { + return true; + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + String className = getClass().getSimpleName(); + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName(className + "Leash") + .setHidden(false) + .setCallsite(className + "#attachToParentSurface"); + attachToParentSurface(builder); + mLeash = builder.build(); + b.setParent(mLeash); + + initSurface(mLeash); + } + + /** Inits the z-order of the surface. */ + private void initSurface(SurfaceControl leash) { + final int z = getZOrder(); + mSyncQueue.runInSync(t -> { + if (leash == null || !leash.isValid()) { + Log.w(getTag(), "The leash has been released."); + return; + } + t.setLayer(leash, z); + }); + } + + /** + * Called when compat info changed. + * + * @param canShow whether the layout is allowed to be shown by the parent controller. + */ + void updateCompatInfo(TaskInfo taskInfo, + ShellTaskOrganizer.TaskListener taskListener, boolean canShow) { + final Configuration prevTaskConfig = mTaskConfig; + final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; + mTaskConfig = taskInfo.configuration; + mTaskListener = taskListener; + + // Update configuration. + setConfiguration(mTaskConfig); + + View layout = getLayout(); + if (layout == null || prevTaskListener != taskListener) { + // TaskListener changed, recreate the layout for new surface parent. + release(); + createLayout(canShow); + return; + } + + boolean boundsUpdated = !mTaskConfig.windowConfiguration.getBounds().equals( + prevTaskConfig.windowConfiguration.getBounds()); + boolean layoutDirectionUpdated = + mTaskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection(); + if (boundsUpdated || layoutDirectionUpdated) { + // Reposition the UI surfaces. + updateSurfacePosition(); + } + + if (layout != null && layoutDirectionUpdated) { + // Update layout for RTL. + layout.setLayoutDirection(mTaskConfig.getLayoutDirection()); + } + } + + + /** + * Updates the visibility of the layout. + * + * @param canShow whether the layout is allowed to be shown by the parent controller. + */ + void updateVisibility(boolean canShow) { + View layout = getLayout(); + if (layout == null) { + // Layout may not have been created because it was hidden previously. + createLayout(canShow); + return; + } + + final int newVisibility = canShow && eligibleToShowLayout() ? View.VISIBLE : View.GONE; + if (layout.getVisibility() != newVisibility) { + layout.setVisibility(newVisibility); + } + } + + /** Called when display layout changed. */ + void updateDisplayLayout(DisplayLayout displayLayout) { + final Rect prevStableBounds = mStableBounds; + final Rect curStableBounds = new Rect(); + displayLayout.getStableBounds(curStableBounds); + mDisplayLayout = displayLayout; + if (!prevStableBounds.equals(curStableBounds)) { + // Stable bounds changed, update UI surface positions. + updateSurfacePosition(); + mStableBounds.set(curStableBounds); + } + } + + /** Called when the surface is ready to be placed under the task surface. */ + @VisibleForTesting + void attachToParentSurface(SurfaceControl.Builder b) { + mTaskListener.attachChildSurfaceToTask(mTaskId, b); + } + + int getDisplayId() { + return mDisplayId; + } + + int getTaskId() { + return mTaskId; + } + + /** Releases the surface control and tears down the view hierarchy. */ + void release() { + // Hiding before releasing to avoid flickering when transitioning to the Home screen. + View layout = getLayout(); + if (layout != null) { + layout.setVisibility(View.GONE); + } + removeLayout(); + + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + final SurfaceControl leash = mLeash; + mSyncQueue.runInSync(t -> t.remove(leash)); + mLeash = null; + } + } + + /** Re-layouts the view host and updates the surface position. */ + void relayout() { + if (mViewHost == null) { + return; + } + mViewHost.relayout(getWindowLayoutParams()); + updateSurfacePosition(); + } + + /** + * Updates the position of the surface with respect to the task bounds and display layout + * stable bounds. + */ + @VisibleForTesting + void updateSurfacePosition() { + if (mLeash == null) { + return; + } + // Use stable bounds to prevent controls from overlapping with system bars. + final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); + final Rect stableBounds = new Rect(); + mDisplayLayout.getStableBounds(stableBounds); + stableBounds.intersect(taskBounds); + + updateSurfacePosition(taskBounds, stableBounds); + } + + /** + * Updates the position of the surface with respect to the given {@code taskBounds} and {@code + * stableBounds}. + */ + protected abstract void updateSurfacePosition(Rect taskBounds, Rect stableBounds); + + /** + * Updates the position of the surface with respect to the given {@code positionX} and {@code + * positionY}. + */ + protected void updateSurfacePosition(int positionX, int positionY) { + mSyncQueue.runInSync(t -> { + if (mLeash == null || !mLeash.isValid()) { + Log.w(getTag(), "The leash has been released."); + return; + } + t.setPosition(mLeash, positionX, positionY); + }); + } + + protected int getLayoutDirection() { + return mContext.getResources().getConfiguration().getLayoutDirection(); + } + + @VisibleForTesting + SurfaceControlViewHost createSurfaceViewHost() { + return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + + /** Gets the layout params. */ + private WindowManager.LayoutParams getWindowLayoutParams() { + View layout = getLayout(); + if (layout == null) { + return new WindowManager.LayoutParams(); + } + // Measure how big the hint is since its size depends on the text size. + layout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + return getWindowLayoutParams(layout.getMeasuredWidth(), layout.getMeasuredHeight()); + } + + /** Gets the layout params given the width and height of the layout. */ + private WindowManager.LayoutParams getWindowLayoutParams(int width, int height) { + final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size + width, height, + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT); + winParams.token = new Binder(); + winParams.setTitle(getClass().getSimpleName() + mTaskId); + winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + return winParams; + } + + protected final String getTag() { + return getClass().getSimpleName(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 9f4ff7c8dc06..2e54c792ed57 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -696,11 +696,12 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<BackAnimationController> provideBackAnimationController( + Context context, @ShellMainThread ShellExecutor shellExecutor ) { if (BackAnimationController.IS_ENABLED) { return Optional.of( - new BackAnimationController(shellExecutor)); + new BackAnimationController(shellExecutor, context)); } return Optional.empty(); } 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/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..21ced0dc5981 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 @@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; +import android.content.Context; import android.hardware.HardwareBuffer; import android.os.RemoteCallback; import android.os.RemoteException; @@ -52,6 +53,9 @@ public class BackAnimationControllerTest { private final ShellExecutor mShellExecutor = new TestShellExecutor(); @Mock + private Context mContext; + + @Mock private SurfaceControl.Transaction mTransaction; @Mock @@ -63,7 +67,7 @@ public class BackAnimationControllerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mController = new BackAnimationController( - mShellExecutor, mTransaction, mActivityTaskManager); + mShellExecutor, mTransaction, mActivityTaskManager, mContext); } private void createNavigationInfo(SurfaceControl topWindowLeash, @@ -75,7 +79,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/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 4352fd3d2c27..741da3fe9f58 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -116,35 +116,17 @@ public class CompatUIControllerTest extends ShellTestCase { TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); - // Verify that the restart button is added with non-null size compat info. + // Verify that the compat controls are added with non-null size compat info. mController.onCompatInfoChanged(taskInfo, mMockTaskListener); verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener)); - // Verify that the restart button is updated with non-null new size compat info. - mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), - mMockTaskListener); - - verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener, - true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); - - // Verify that the restart button is updated with new camera state. - mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED), - mMockTaskListener); - - verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener, - true /* show */, true /* hasSizeCompat */, + // Verify that the compat controls are updated with non-null new size compat info. + taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); - mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED), - mMockTaskListener); - - verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener, - true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, true /* canShow */); // Verify that compat controls are removed with null compat info. mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, @@ -155,7 +137,7 @@ public class CompatUIControllerTest extends ShellTestCase { clearInvocations(mMockLayout); clearInvocations(mController); - // Verify that compat controls are removed with dismissed camera state. + // Verify that compat controls are removed with no size compat and dismissed camera state. taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); @@ -245,11 +227,11 @@ public class CompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while IME is showing. - mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener); + TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); - verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener, - false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */); // Verify button is shown after IME is hidden. mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */); @@ -268,11 +250,11 @@ public class CompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while keyguard is occluded. - mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener); + TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); - verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener, - false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */); // Verify button is shown after keyguard becomes not occluded. mController.onKeyguardOccludedChanged(false); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java index 353d8fe8bc52..211781798af7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java @@ -27,6 +27,9 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.app.TaskInfo.CameraCompatControlState; import android.content.res.Configuration; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; @@ -83,7 +86,7 @@ public class CompatUILayoutTest extends ShellTestCase { spyOn(mWindowManager); spyOn(mCompatUILayout); doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); - doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); + doReturn(mCompatUILayout).when(mWindowManager).inflateLayout(); } @Test @@ -107,8 +110,8 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testOnClickForSizeCompatHint() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint); sizeCompatHint.performClick(); @@ -117,8 +120,8 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED); + mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED)); final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_treatment_button); button.performClick(); @@ -135,8 +138,8 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_treatment_button); button.performClick(); @@ -153,8 +156,8 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testOnCameraDismissButtonClicked() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button); button.performClick(); @@ -188,11 +191,19 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testOnClickForCameraCompatHint() { - mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* show */, createTaskInfo(false /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint); hint.performClick(); verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false); } + + private static TaskInfo createTaskInfo(boolean hasSizeCompat, + @CameraCompatControlState int cameraCompatControlState) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.topActivityInSizeCompat = hasSizeCompat; + taskInfo.cameraCompatControlState = cameraCompatControlState; + return taskInfo; + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java index 11c797363819..de882eae1503 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -26,8 +26,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -35,6 +35,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.app.ActivityManager; +import android.app.TaskInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -75,47 +77,45 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private CompatUILayout mCompatUILayout; @Mock private SurfaceControlViewHost mViewHost; - private Configuration mTaskConfig; private CompatUIWindowManager mWindowManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTaskConfig = new Configuration(); mWindowManager = new CompatUIWindowManager(mContext, new Configuration(), mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(), false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */); spyOn(mWindowManager); - doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); + doReturn(mCompatUILayout).when(mWindowManager).inflateLayout(); doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); } @Test public void testCreateSizeCompatButton() { // Not create layout if show is false. - mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(false /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); - verify(mWindowManager, never()).inflateCompatUILayout(); + verify(mWindowManager, never()).inflateLayout(); // Not create hint popup. mWindowManager.mShouldShowSizeCompatHint = false; - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); - verify(mWindowManager).inflateCompatUILayout(); + verify(mWindowManager).inflateLayout(); verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */); // Create hint popup. mWindowManager.release(); mWindowManager.mShouldShowSizeCompatHint = true; - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); - verify(mWindowManager, times(2)).inflateCompatUILayout(); + verify(mWindowManager, times(2)).inflateLayout(); assertNotNull(mCompatUILayout); verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */); assertFalse(mWindowManager.mShouldShowSizeCompatHint); @@ -123,10 +123,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testRelease() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); - verify(mWindowManager).inflateCompatUILayout(); + verify(mWindowManager).inflateLayout(); mWindowManager.release(); @@ -135,65 +135,104 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testUpdateCompatInfo() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* canShow */, taskInfo); // No diff clearInvocations(mWindowManager); - mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */); verify(mWindowManager, never()).updateSurfacePosition(); verify(mWindowManager, never()).release(); - verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt()); + verify(mWindowManager, never()).createLayout(anyBoolean()); // Change task listener, recreate button. clearInvocations(mWindowManager); final ShellTaskOrganizer.TaskListener newTaskListener = mock( ShellTaskOrganizer.TaskListener.class); - mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, - true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); verify(mWindowManager).release(); - verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt()); + verify(mWindowManager).createLayout(true); + + // Change in Size Compat to false, hides restart button. + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); + + verify(mCompatUILayout).setRestartButtonVisibility(/* show */ false); + + // Change in Size Compat to true, shows restart button. + clearInvocations(mWindowManager); + clearInvocations(mCompatUILayout); + taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); + + verify(mCompatUILayout).setRestartButtonVisibility(/* show */ true); // Change Camera Compat state, show a control. - mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED); + clearInvocations(mWindowManager); + clearInvocations(mCompatUILayout); + taskInfo = createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); verify(mCompatUILayout).setCameraControlVisibility(/* show */ true); verify(mCompatUILayout).updateCameraTreatmentButton( CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED); + // Change Camera Compat state, update a control. clearInvocations(mWindowManager); clearInvocations(mCompatUILayout); - // Change Camera Compat state, update a control. - mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */, - true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + taskInfo = createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); verify(mCompatUILayout).setCameraControlVisibility(/* show */ true); verify(mCompatUILayout).updateCameraTreatmentButton( CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + // Change Camera Compat state to hidden, hide a control. clearInvocations(mWindowManager); clearInvocations(mCompatUILayout); - // Change Camera Compat state to hidden, hide a control. - mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, - true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); verify(mCompatUILayout).setCameraControlVisibility(/* show */ false); // Change task bounds, update position. clearInvocations(mWindowManager); - final Configuration newTaskConfiguration = new Configuration(); - newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); - mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener, - true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */); verify(mWindowManager).updateSurfacePosition(); } @Test + public void testUpdateCompatInfoLayoutNotInflatedYet() { + TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(false /* canShow */, taskInfo); + + verify(mWindowManager, never()).inflateLayout(); + + // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be + // inflated + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */); + + verify(mWindowManager, never()).inflateLayout(); + + // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated. + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */); + + verify(mWindowManager).inflateLayout(); + } + + @Test public void testUpdateDisplayLayout() { final DisplayInfo displayInfo = new DisplayInfo(); displayInfo.logicalWidth = 1000; @@ -237,26 +276,25 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testUpdateVisibility() { // Create button if it is not created. - mWindowManager.mCompatUILayout = null; + mWindowManager.mLayout = null; mWindowManager.mHasSizeCompat = true; - mWindowManager.updateVisibility(true /* show */); + mWindowManager.updateVisibility(true /* canShow */); - verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + verify(mWindowManager).createLayout(true /* canShow */); // Hide button. clearInvocations(mWindowManager); doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility(); - mWindowManager.updateVisibility(false /* show */); + mWindowManager.updateVisibility(false /* canShow */); - verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt()); + verify(mWindowManager, never()).createLayout(anyBoolean(), any()); verify(mCompatUILayout).setVisibility(View.GONE); // Show button. doReturn(View.GONE).when(mCompatUILayout).getVisibility(); - mWindowManager.updateVisibility(true /* show */); + mWindowManager.updateVisibility(true /* canShow */); - verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt()); + verify(mWindowManager, never()).createLayout(anyBoolean(), any()); verify(mCompatUILayout).setVisibility(View.VISIBLE); } @@ -270,8 +308,8 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testOnCameraDismissButtonClicked() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); clearInvocations(mCompatUILayout); mWindowManager.onCameraDismissButtonClicked(); @@ -281,8 +319,8 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testOnCameraTreatmentButtonClicked() { - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); clearInvocations(mCompatUILayout); mWindowManager.onCameraTreatmentButtonClicked(); @@ -310,10 +348,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase { public void testOnRestartButtonLongClicked_showHint() { // Not create hint popup. mWindowManager.mShouldShowSizeCompatHint = false; - mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_HIDDEN); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_HIDDEN)); - verify(mWindowManager).inflateCompatUILayout(); + verify(mWindowManager).inflateLayout(); verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */); mWindowManager.onRestartButtonLongClicked(); @@ -325,10 +363,10 @@ public class CompatUIWindowManagerTest extends ShellTestCase { public void testOnCamerControlLongClicked_showHint() { // Not create hint popup. mWindowManager.mShouldShowCameraCompatHint = false; - mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); - verify(mWindowManager).inflateCompatUILayout(); + verify(mWindowManager).inflateLayout(); verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */); mWindowManager.onCameraButtonLongClicked(); @@ -339,30 +377,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testCreateCameraCompatControl() { // Not create layout if show is false. - mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(false /* canShow */, createTaskInfo(false /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); - verify(mWindowManager, never()).inflateCompatUILayout(); + verify(mWindowManager, never()).inflateLayout(); // Not create hint popup. mWindowManager.mShouldShowCameraCompatHint = false; - mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); - verify(mWindowManager).inflateCompatUILayout(); + verify(mWindowManager).inflateLayout(); verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */); verify(mCompatUILayout).setCameraControlVisibility(true /* show */); // Create hint popup. mWindowManager.release(); mWindowManager.mShouldShowCameraCompatHint = true; - mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */, - CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED); + mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */, + CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED)); - verify(mWindowManager, times(2)).inflateCompatUILayout(); + verify(mWindowManager, times(2)).inflateLayout(); assertNotNull(mCompatUILayout); verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */); assertFalse(mWindowManager.mShouldShowCameraCompatHint); } + private static TaskInfo createTaskInfo(boolean hasSizeCompat, + @TaskInfo.CameraCompatControlState int cameraCompatControlState) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + taskInfo.topActivityInSizeCompat = hasSizeCompat; + taskInfo.cameraCompatControlState = cameraCompatControlState; + return taskInfo; + } } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index dc31bdd6f8ff..f6ad4c2f3aa5 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -345,6 +345,7 @@ cc_defaults { "jni/PathEffect.cpp", "jni/PathMeasure.cpp", "jni/Picture.cpp", + "jni/Region.cpp", "jni/Shader.cpp", "jni/RenderEffect.cpp", "jni/Typeface.cpp", @@ -394,7 +395,6 @@ cc_defaults { "jni/GraphicsStatsService.cpp", "jni/Movie.cpp", "jni/MovieImpl.cpp", - "jni/Region.cpp", // requires libbinder_ndk "jni/pdf/PdfDocument.cpp", "jni/pdf/PdfEditor.cpp", "jni/pdf/PdfRenderer.cpp", diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp index dca10e29cbb8..942c0506321c 100644 --- a/libs/hwui/apex/LayoutlibLoader.cpp +++ b/libs/hwui/apex/LayoutlibLoader.cpp @@ -14,31 +14,22 @@ * limitations under the License. */ -#include "graphics_jni_helpers.h" - #include <GraphicsJNI.h> #include <SkGraphics.h> -#include <sstream> -#include <iostream> -#include <unicode/putil.h> #include <unordered_map> #include <vector> -using namespace std; - -/* - * This is responsible for setting up the JNI environment for communication between - * the Java and native parts of layoutlib, including registering native methods. - * This is mostly achieved by copying the way it is done in the platform - * (see AndroidRuntime.cpp). - */ +#include "Properties.h" +#include "android/graphics/jni_runtime.h" +#include "graphics_jni_helpers.h" -static JavaVM* javaVM; +using namespace std; extern int register_android_graphics_Bitmap(JNIEnv*); extern int register_android_graphics_BitmapFactory(JNIEnv*); extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env); +extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); @@ -49,10 +40,12 @@ extern int register_android_graphics_PathEffect(JNIEnv* env); extern int register_android_graphics_Shader(JNIEnv* env); extern int register_android_graphics_RenderEffect(JNIEnv* env); extern int register_android_graphics_Typeface(JNIEnv* env); +extern int register_android_graphics_YuvImage(JNIEnv* env); namespace android { extern int register_android_graphics_Canvas(JNIEnv* env); +extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); extern int register_android_graphics_ColorSpace(JNIEnv* env); extern int register_android_graphics_DrawFilter(JNIEnv* env); @@ -62,7 +55,7 @@ extern int register_android_graphics_Paint(JNIEnv* env); extern int register_android_graphics_Path(JNIEnv* env); extern int register_android_graphics_PathMeasure(JNIEnv* env); extern int register_android_graphics_Picture(JNIEnv* env); -//extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_Region(JNIEnv* env); extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); @@ -71,9 +64,11 @@ extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); extern int register_android_graphics_text_LineBreaker(JNIEnv* env); extern int register_android_graphics_text_MeasuredText(JNIEnv* env); +extern int register_android_graphics_text_TextShaper(JNIEnv* env); + extern int register_android_util_PathParser(JNIEnv* env); -extern int register_android_view_RenderNode(JNIEnv* env); extern int register_android_view_DisplayListCanvas(JNIEnv* env); +extern int register_android_view_RenderNode(JNIEnv* env); #define REG_JNI(name) { name } struct RegJNIRec { @@ -87,8 +82,9 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)}, {"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)}, + {"android.graphics.Camera", REG_JNI(register_android_graphics_Camera)}, {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)}, - {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, + {"android.graphics.CanvasProperty", REG_JNI(register_android_graphics_CanvasProperty)}, {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)}, {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)}, {"android.graphics.CreateJavaOutputStreamAdaptor", @@ -107,10 +103,12 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)}, {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)}, {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)}, -// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, + {"android.graphics.Region", REG_JNI(register_android_graphics_Region)}, + {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)}, {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)}, {"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)}, {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)}, + {"android.graphics.YuvImage", REG_JNI(register_android_graphics_YuvImage)}, {"android.graphics.animation.NativeInterpolatorFactory", REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)}, {"android.graphics.animation.RenderNodeAnimator", @@ -124,6 +122,7 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)}, {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)}, + {"android.graphics.text.TextRunShaper", REG_JNI(register_android_graphics_text_TextShaper)}, {"android.util.PathParser", REG_JNI(register_android_util_PathParser)}, }; @@ -177,10 +176,9 @@ int register_android_graphics_classes(JNIEnv *env) { "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); // Get the names of classes that need to register their native methods - auto nativesClassesJString = - (jstring) env->CallStaticObjectMethod(system, - getPropertyMethod, env->NewStringUTF("native_classes"), - env->NewStringUTF("")); + auto nativesClassesJString = (jstring)env->CallStaticObjectMethod( + system, getPropertyMethod, env->NewStringUTF("graphics_native_classes"), + env->NewStringUTF("")); vector<string> classesToRegister = parseCsv(env, nativesClassesJString); if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) { diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index c505b53b2df1..899c7d4d75e2 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -261,11 +261,10 @@ static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete)); } -static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr, - jboolean isOpaque) { +static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr) { SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE); + sk_sp<SkShader> shader = builder->makeShader(matrix, false); ThrowIAE_IfNull(env, shader); return reinterpret_cast<jlong>(shader.release()); } @@ -419,7 +418,7 @@ static const JNINativeMethod gComposeShaderMethods[] = { static const JNINativeMethod gRuntimeShaderMethods[] = { {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer}, - {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create}, + {"nativeCreateShader", "(JJ)J", (void*)RuntimeShader_create}, {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder}, {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)RuntimeShader_updateFloatArrayUniforms}, 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 b8333fb57848..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(); 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/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 4451c64b6548..fec14def618c 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -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); diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 004501812ff6..e609226c3cb8 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -67,7 +67,7 @@ public final class RouteDiscoveryPreference implements Parcelable { @NonNull private final List<String> mRequiredFeatures; @NonNull - private final List<String> mPackagesOrder; + private final List<String> mPackageOrder; @NonNull private final List<String> mAllowedPackages; @@ -86,7 +86,7 @@ public final class RouteDiscoveryPreference implements Parcelable { RouteDiscoveryPreference(@NonNull Builder builder) { mPreferredFeatures = builder.mPreferredFeatures; mRequiredFeatures = builder.mRequiredFeatures; - mPackagesOrder = builder.mPackageOrder; + mPackageOrder = builder.mPackageOrder; mAllowedPackages = builder.mAllowedPackages; mShouldPerformActiveScan = builder.mActiveScan; mExtras = builder.mExtras; @@ -95,7 +95,7 @@ public final class RouteDiscoveryPreference implements Parcelable { RouteDiscoveryPreference(@NonNull Parcel in) { mPreferredFeatures = in.createStringArrayList(); mRequiredFeatures = in.createStringArrayList(); - mPackagesOrder = in.createStringArrayList(); + mPackageOrder = in.createStringArrayList(); mAllowedPackages = in.createStringArrayList(); mShouldPerformActiveScan = in.readBoolean(); mExtras = in.readBundle(); @@ -144,7 +144,7 @@ public final class RouteDiscoveryPreference implements Parcelable { */ @NonNull public List<String> getDeduplicationPackageOrder() { - return mPackagesOrder; + return mPackageOrder; } /** @@ -175,7 +175,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * @see #getDeduplicationPackageOrder() */ public boolean shouldRemoveDuplicates() { - return !mPackagesOrder.isEmpty(); + return !mPackageOrder.isEmpty(); } /** @@ -194,7 +194,7 @@ public final class RouteDiscoveryPreference implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStringList(mPreferredFeatures); dest.writeStringList(mRequiredFeatures); - dest.writeStringList(mPackagesOrder); + dest.writeStringList(mPackageOrder); dest.writeStringList(mAllowedPackages); dest.writeBoolean(mShouldPerformActiveScan); dest.writeBundle(mExtras); @@ -225,14 +225,14 @@ public final class RouteDiscoveryPreference implements Parcelable { RouteDiscoveryPreference other = (RouteDiscoveryPreference) o; return Objects.equals(mPreferredFeatures, other.mPreferredFeatures) && Objects.equals(mRequiredFeatures, other.mRequiredFeatures) - && Objects.equals(mPackagesOrder, other.mPackagesOrder) + && Objects.equals(mPackageOrder, other.mPackageOrder) && Objects.equals(mAllowedPackages, other.mAllowedPackages) && mShouldPerformActiveScan == other.mShouldPerformActiveScan; } @Override public int hashCode() { - return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackagesOrder, mAllowedPackages, + return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackageOrder, mAllowedPackages, mShouldPerformActiveScan); } @@ -271,21 +271,31 @@ public final class RouteDiscoveryPreference implements Parcelable { } /** - * A constructor to combine all the preferences into a single preference. - * It ignores extras of preferences. + * A constructor to combine multiple preferences into a single preference. The combined + * preference will discover a superset of the union of the routes discoverable by each of + * the individual preferences. + * <p> + * When routes need to be discovered for multiple preferences, the combined preference can + * be used to query route providers once and obtain all routes of interest. The obtained + * routes can then be filtered for each of the individual preferences. This is typically + * more efficient than querying route providers with each of the individual preferences. * * @hide */ public Builder(@NonNull Collection<RouteDiscoveryPreference> preferences) { Objects.requireNonNull(preferences, "preferences must not be null"); - Set<String> routeFeatureSet = new HashSet<>(); - mActiveScan = false; + Set<String> preferredFeatures = new HashSet<>(); + boolean activeScan = false; for (RouteDiscoveryPreference preference : preferences) { - routeFeatureSet.addAll(preference.mPreferredFeatures); - mActiveScan |= preference.mShouldPerformActiveScan; + preferredFeatures.addAll(preference.mPreferredFeatures); + activeScan |= preference.mShouldPerformActiveScan; } - mPreferredFeatures = new ArrayList<>(routeFeatureSet); + mPreferredFeatures = new ArrayList<>(preferredFeatures); + mRequiredFeatures = List.of(); + mPackageOrder = List.of(); + mAllowedPackages = List.of(); + mActiveScan = activeScan; } /** 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/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java index f7a52f22e93a..cbd8c1fd53cd 100644 --- a/media/java/android/media/tv/BroadcastInfoRequest.java +++ b/media/java/android/media/tv/BroadcastInfoRequest.java @@ -56,6 +56,8 @@ public abstract class BroadcastInfoRequest implements Parcelable { switch (type) { case TvInputManager.BROADCAST_INFO_TYPE_TS: return TsRequest.createFromParcelBody(source); + case TvInputManager.BROADCAST_INFO_TYPE_TABLE: + return TableRequest.createFromParcelBody(source); case TvInputManager.BROADCAST_INFO_TYPE_SECTION: return SectionRequest.createFromParcelBody(source); case TvInputManager.BROADCAST_INFO_TYPE_PES: diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java index ff4ec15bca5b..4ba2b2c2ff7b 100644 --- a/media/java/android/media/tv/BroadcastInfoResponse.java +++ b/media/java/android/media/tv/BroadcastInfoResponse.java @@ -57,6 +57,8 @@ public abstract class BroadcastInfoResponse implements Parcelable { switch (type) { case TvInputManager.BROADCAST_INFO_TYPE_TS: return TsResponse.createFromParcelBody(source); + case TvInputManager.BROADCAST_INFO_TYPE_TABLE: + return TableResponse.createFromParcelBody(source); case TvInputManager.BROADCAST_INFO_TYPE_SECTION: return SectionResponse.createFromParcelBody(source); case TvInputManager.BROADCAST_INFO_TYPE_PES: diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 14accaafe189..a3e731b0ffa1 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -623,21 +623,21 @@ public class Filter implements AutoCloseable { * <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information. * - * @param delayInMs specifies the duration of the delay in milliseconds. + * @param durationInMs specifies the duration of the delay in milliseconds. * @return one of the following results: {@link Tuner#RESULT_SUCCESS}, * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED}, * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT}, * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}. */ - public int delayCallbackUntilMillisElapsed(long delayInMs) { + public int delayCallbackForDurationMillis(long durationInMs) { if (!TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) { return Tuner.RESULT_UNAVAILABLE; } - if (delayInMs >= 0 && delayInMs <= Integer.MAX_VALUE) { + if (durationInMs >= 0 && durationInMs <= Integer.MAX_VALUE) { synchronized (mLock) { - return nativeSetTimeDelayHint((int) delayInMs); + return nativeSetTimeDelayHint((int) durationInMs); } } return Tuner.RESULT_INVALID_ARGUMENT; @@ -653,20 +653,20 @@ public class Filter implements AutoCloseable { * <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information. * - * @param delayInBytes specifies the duration of the delay in bytes. + * @param bytesAccumulated specifies the delay condition in bytes. * @return one of the following results: {@link Tuner#RESULT_SUCCESS}, * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED}, * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT}, * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}. */ - public int delayCallbackUntilBytesAccumulated(int delayInBytes) { + public int delayCallbackUntilBytesAccumulated(int bytesAccumulated) { if (!TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) { return Tuner.RESULT_UNAVAILABLE; } synchronized (mLock) { - return nativeSetDataSizeDelayHint(delayInBytes); + return nativeSetDataSizeDelayHint(bytesAccumulated); } } } 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/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 6329565ca567..66527802a0e4 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-netstats-sources", ], visibility: ["//frameworks/base"], } @@ -167,6 +166,7 @@ filegroup { name: "framework-connectivity-tiramisu-updatable-sources", srcs: [ ":framework-connectivity-ipsec-sources", + ":framework-connectivity-netstats-sources", ":framework-connectivity-nsd-sources", ":framework-connectivity-tiramisu-internal-sources", ], @@ -194,15 +194,12 @@ cc_library_shared { "jni/onload.cpp", ], shared_libs: [ + "libandroid", "liblog", - ], - static_libs: [ - "libnativehelper_compat_libc++", + "libnativehelper", ], stl: "none", apex_available: [ "com.android.tethering", - // TODO: remove when ConnectivityT moves to APEX. - "//apex_available:platform", ], } diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java index 5ce7e59b38ff..0414bb79f83b 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.WorkerThread; import android.app.usage.NetworkStats.Bucket; import android.compat.annotation.UnsupportedAppUsage; @@ -192,9 +191,13 @@ public class NetworkStatsManager { } } - /** @hide */ + /** + * Set poll force flag to indicate that calling any subsequent query method will force a stats + * poll. + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @TestApi + @SystemApi(client = MODULE_LIBRARIES) public void setPollForce(boolean pollForce) { if (pollForce) { mFlags |= FLAG_POLL_FORCE; diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java index 577ac5466692..9bffbfb27a8d 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java +++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.SystemApi; import android.app.SystemServiceRegistry; +import android.app.usage.NetworkStatsManager; import android.content.Context; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; @@ -57,5 +58,15 @@ public final class ConnectivityFrameworkInitializerTiramisu { return new IpSecManager(context, service); } ); + + SystemServiceRegistry.registerContextAwareService( + Context.NETWORK_STATS_SERVICE, + NetworkStatsManager.class, + (context, serviceBinder) -> { + INetworkStatsService service = + INetworkStatsService.Stub.asInterface(serviceBinder); + return new NetworkStatsManager(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/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java index 56faa52e82df..4ebaf2b8cdb8 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java @@ -184,14 +184,14 @@ public class NetworkIdentity { public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); - proto.write(NetworkIdentityProto.TYPE, mType); + proto.write(NetworkIdentityProto.TYPE_FIELD_NUMBER, mType); // TODO: dump mRatType as well. - proto.write(NetworkIdentityProto.ROAMING, mRoaming); - proto.write(NetworkIdentityProto.METERED, mMetered); - proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork); - proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged); + proto.write(NetworkIdentityProto.ROAMING_FIELD_NUMBER, mRoaming); + proto.write(NetworkIdentityProto.METERED_FIELD_NUMBER, mMetered); + proto.write(NetworkIdentityProto.DEFAULT_NETWORK_FIELD_NUMBER, mDefaultNetwork); + proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK_FIELD_NUMBER, mOemManaged); proto.end(start); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java index dfa347f6f12b..2236d70c3502 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java @@ -212,7 +212,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { final long start = proto.start(tag); for (NetworkIdentity ident : this) { - ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES); + ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES_FIELD_NUMBER); } proto.end(start); diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java index 735c44d5c87e..67d48f0000d5 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java @@ -732,19 +732,19 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W final long start = proto.start(tag); for (Key key : getSortedKeys()) { - final long startStats = proto.start(NetworkStatsCollectionProto.STATS); + final long startStats = proto.start(NetworkStatsCollectionProto.STATS_FIELD_NUMBER); // Key - final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY); - key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY); - proto.write(NetworkStatsCollectionKeyProto.UID, key.uid); - proto.write(NetworkStatsCollectionKeyProto.SET, key.set); - proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag); + final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY_FIELD_NUMBER); + key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY_FIELD_NUMBER); + proto.write(NetworkStatsCollectionKeyProto.UID_FIELD_NUMBER, key.uid); + proto.write(NetworkStatsCollectionKeyProto.SET_FIELD_NUMBER, key.set); + proto.write(NetworkStatsCollectionKeyProto.TAG_FIELD_NUMBER, key.tag); proto.end(startKey); // Value final NetworkStatsHistory history = mStats.get(key); - history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY); + history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY_FIELD_NUMBER); proto.end(startStats); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java index 78c137073aaa..822a16e0bb41 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java @@ -915,17 +915,18 @@ public final class NetworkStatsHistory implements Parcelable { public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); - proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration); + proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS_FIELD_NUMBER, bucketDuration); for (int i = 0; i < bucketCount; i++) { - final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS); - - proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]); - dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i); + final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS_FIELD_NUMBER); + + proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS_FIELD_NUMBER, + bucketStart[i]); + dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES_FIELD_NUMBER, rxBytes, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS_FIELD_NUMBER, rxPackets, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES_FIELD_NUMBER, txBytes, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS_FIELD_NUMBER, txPackets, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS_FIELD_NUMBER, operations, i); proto.end(startBucket); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java index 9b58b016bbf3..7b5afd720016 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java @@ -79,7 +79,8 @@ public final class NetworkTemplate implements Parcelable { MATCH_WIFI, MATCH_ETHERNET, MATCH_BLUETOOTH, - MATCH_CARRIER + MATCH_PROXY, + MATCH_CARRIER, }) public @interface TemplateMatchRule{} @@ -104,9 +105,8 @@ public final class NetworkTemplate implements Parcelable { /** Match rule to match bluetooth networks. */ public static final int MATCH_BLUETOOTH = 8; /** - * Match rule to match networks with {@link Connectivity#TYPE_PROXY} as the legacy network type. - * - * @hide + * Match rule to match networks with {@link ConnectivityManager#TYPE_PROXY} as the legacy + * network type. */ public static final int MATCH_PROXY = 9; /** 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 d2d6ef0cbbb1..5100e7c5b9a4 100644 --- a/packages/ConnectivityT/service/Android.bp +++ b/packages/ConnectivityT/service/Android.bp @@ -28,6 +28,11 @@ filegroup { "src/com/android/server/net/NetworkStats*.java", "src/com/android/server/net/BpfInterfaceMapUpdater.java", "src/com/android/server/net/InterfaceMapValue.java", + "src/com/android/server/net/CookieTagMapKey.java", + "src/com/android/server/net/CookieTagMapValue.java", + "src/com/android/server/net/StatsMapKey.java", + "src/com/android/server/net/StatsMapValue.java", + "src/com/android/server/net/UidStatsMapKey.java", ], path: "src", visibility: [ @@ -35,6 +40,30 @@ filegroup { ], } +// For test code only. +filegroup { + name: "lib_networkStatsFactory_native", + srcs: [ + "jni/com_android_server_net_NetworkStatsFactory.cpp", + ], + path: "jni", + visibility: [ + "//packages/modules/Connectivity:__subpackages__", + ], +} + +filegroup { + name: "services.connectivity-netstats-jni-sources", + srcs: [ + "jni/com_android_server_net_NetworkStatsFactory.cpp", + "jni/com_android_server_net_NetworkStatsService.cpp", + ], + path: "jni", + visibility: [ + "//packages/modules/Connectivity:__subpackages__", + ], +} + // Nsd related libraries. filegroup { @@ -83,7 +112,6 @@ filegroup { name: "services.connectivity-tiramisu-sources", srcs: [ ":services.connectivity-ethernet-sources", - ":services.connectivity-netstats-sources", ], path: "src", visibility: ["//frameworks/base/services/core"], @@ -93,6 +121,7 @@ filegroup { name: "services.connectivity-tiramisu-updatable-sources", srcs: [ ":services.connectivity-ipsec-sources", + ":services.connectivity-netstats-sources", ":services.connectivity-nsd-sources", ], path: "src", diff --git a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp index 8b6526ff49f0..8b6526ff49f0 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp +++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp index f8a81682bdcf..f8a81682bdcf 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp 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/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java new file mode 100644 index 000000000000..443e5b38475e --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java @@ -0,0 +1,33 @@ +/* + * 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.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * Key for cookie tag map. + */ +public class CookieTagMapKey extends Struct { + @Field(order = 0, type = Type.S64) + public final long socketCookie; + + public CookieTagMapKey(final long socketCookie) { + this.socketCookie = socketCookie; + } +} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java new file mode 100644 index 000000000000..93b9195f92da --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java @@ -0,0 +1,37 @@ +/* + * 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.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * Value for cookie tag map. + */ +public class CookieTagMapValue extends Struct { + @Field(order = 0, type = Type.U32) + public final long uid; + + @Field(order = 1, type = Type.U32) + public final long tag; + + public CookieTagMapValue(final long uid, final long tag) { + this.uid = uid; + this.tag = tag; + } +} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java index 668d1cba921b..151c90dd4155 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java @@ -54,6 +54,10 @@ import java.util.concurrent.ConcurrentHashMap; * @hide */ public class NetworkStatsFactory { + static { + System.loadLibrary("service-connectivity"); + } + private static final String TAG = "NetworkStatsFactory"; private static final boolean USE_NATIVE_PARSING = true; diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java index c371f0859aa1..a006cd597568 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java @@ -471,9 +471,11 @@ public class NetworkStatsRecorder { public void dumpDebugLocked(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); if (mPending != null) { - proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes()); + proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES_FIELD_NUMBER, + mPending.getTotalBytes()); } - getOrLoadCompleteLocked().dumpDebug(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY); + getOrLoadCompleteLocked().dumpDebug(proto, + NetworkStatsRecorderProto.COMPLETE_HISTORY_FIELD_NUMBER); proto.end(start); } diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java index 9f3371b724cf..7a5ba09f27f9 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -62,6 +62,7 @@ import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.usage.NetworkStatsManager; @@ -102,6 +103,7 @@ import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; import android.os.Binder; +import android.os.Build; import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; @@ -169,7 +171,12 @@ import java.util.concurrent.TimeUnit; * Collect and persist detailed network statistics, and provide this data to * other system services. */ +@TargetApi(Build.VERSION_CODES.TIRAMISU) public class NetworkStatsService extends INetworkStatsService.Stub { + static { + System.loadLibrary("service-connectivity"); + } + static final String TAG = "NetworkStats"; static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); @@ -2004,12 +2011,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // TODO Right now it writes all history. Should it limit to the "since-boot" log? - dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces); - dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces); - mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS); - mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS); - mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS); - mUidTagRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS); + dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES_FIELD_NUMBER, + mActiveIfaces); + dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES_FIELD_NUMBER, + mActiveUidIfaces); + mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS_FIELD_NUMBER); + mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS_FIELD_NUMBER); + mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS_FIELD_NUMBER); + mUidTagRecorder.dumpDebugLocked(proto, + NetworkStatsServiceDumpProto.UID_TAG_STATS_FIELD_NUMBER); proto.flush(); } @@ -2019,8 +2029,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { for (int i = 0; i < ifaces.size(); i++) { final long start = proto.start(tag); - proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i)); - ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES); + proto.write(NetworkInterfaceProto.INTERFACE_FIELD_NUMBER, ifaces.keyAt(i)); + ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES_FIELD_NUMBER); proto.end(start); } diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index 5bba0b17aa42..65ccd2007299 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -23,7 +23,9 @@ import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NS import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; import android.annotation.NonNull; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyDisplayInfo; @@ -43,6 +45,7 @@ import java.util.concurrent.Executor; /** * Helper class that watches for events that are triggered per subscription. */ +@TargetApi(Build.VERSION_CODES.TIRAMISU) public class NetworkStatsSubscriptionsMonitor extends SubscriptionManager.OnSubscriptionsChangedListener { diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java new file mode 100644 index 000000000000..ea8d83638347 --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java @@ -0,0 +1,46 @@ +/* + * 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.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * Key for both stats maps. + */ +public class StatsMapKey extends Struct { + @Field(order = 0, type = Type.U32) + public final long uid; + + @Field(order = 1, type = Type.U32) + public final long tag; + + @Field(order = 2, type = Type.U32) + public final long counterSet; + + @Field(order = 3, type = Type.U32) + public final long ifaceIndex; + + public StatsMapKey(final long uid, final long tag, final long counterSet, + final long ifaceIndex) { + this.uid = uid; + this.tag = tag; + this.counterSet = counterSet; + this.ifaceIndex = ifaceIndex; + } +} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java new file mode 100644 index 000000000000..48f26ce686e2 --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java @@ -0,0 +1,46 @@ +/* + * 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.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * Value used for both stats maps and uid stats map. + */ +public class StatsMapValue extends Struct { + @Field(order = 0, type = Type.U63) + public final long rxPackets; + + @Field(order = 1, type = Type.U63) + public final long rxBytes; + + @Field(order = 2, type = Type.U63) + public final long txPackets; + + @Field(order = 3, type = Type.U63) + public final long txBytes; + + public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets, + final long txBytes) { + this.rxPackets = rxPackets; + this.rxBytes = rxBytes; + this.txPackets = txPackets; + this.txBytes = txBytes; + } +} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java new file mode 100644 index 000000000000..2849f94c9383 --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java @@ -0,0 +1,33 @@ +/* + * 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.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * Key for uid stats map. + */ +public class UidStatsMapKey extends Struct { + @Field(order = 0, type = Type.U32) + public final long uid; + + public UidStatsMapKey(final long uid) { + this.uid = uid; + } +} diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml index c8ddcc870e46..6940c39d6617 100644 --- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml +++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml @@ -27,14 +27,14 @@ android:layout_gravity="center"> <ImageView android:id="@+id/user_photo" - android:layout_width="@dimen/user_photo_size_in_profile_info_dialog" - android:layout_height="@dimen/user_photo_size_in_profile_info_dialog" + android:layout_width="@dimen/user_photo_size_in_user_info_dialog" + android:layout_height="@dimen/user_photo_size_in_user_info_dialog" android:contentDescription="@string/user_image_photo_selector" android:scaleType="fitCenter"/> <ImageView android:id="@+id/add_a_photo_icon" - android:layout_width="@dimen/add_a_photo_icon_size_in_profile_info_dialog" - android:layout_height="@dimen/add_a_photo_icon_size_in_profile_info_dialog" + android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog" + android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog" android:src="@drawable/add_a_photo_circled" android:layout_gravity="bottom|right" /> </FrameLayout> @@ -42,7 +42,7 @@ <EditText android:id="@+id/user_name" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/user_name_height_in_user_info_dialog" android:layout_gravity="center" android:minWidth="200dp" android:layout_marginStart="6dp" diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 120df76218b3..e1bd9f705577 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -99,8 +99,9 @@ <dimen name="update_user_photo_popup_min_width">300dp</dimen> <dimen name="add_a_photo_circled_padding">6dp</dimen> - <dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen> - <dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen> + <dimen name="user_photo_size_in_user_info_dialog">112dp</dimen> + <dimen name="add_a_photo_icon_size_in_user_info_dialog">32dp</dimen> + <dimen name="user_name_height_in_user_info_dialog">48sp</dimen> <integer name="avatar_picker_columns">3</integer> <dimen name="avatar_size_in_picker">96dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 15ca8cdf75ac..df19c67f00e5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -223,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; } @@ -236,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; } @@ -262,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; } @@ -288,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/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java index db6d41ef692d..e203cbaa3da5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java @@ -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) { diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java index a781a629b7a1..035fafd5bb58 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java @@ -16,6 +16,10 @@ package com.android.settingslib.drawable; +import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON; +import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_USER_ICON; + import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.NonNull; @@ -38,9 +42,12 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.UserHandle; +import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; +import androidx.core.os.BuildCompat; import com.android.settingslib.R; @@ -82,8 +89,23 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback { * @return drawable containing just the badge */ public static Drawable getManagedUserDrawable(Context context) { - return getDrawableForDisplayDensity - (context, com.android.internal.R.drawable.ic_corp_user_badge); + if (BuildCompat.isAtLeastT()) { + return getUpdatableManagedUserDrawable(context); + } else { + return getDrawableForDisplayDensity( + context, com.android.internal.R.drawable.ic_corp_user_badge); + } + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private static Drawable getUpdatableManagedUserDrawable(Context context) { + DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); + return dpm.getDrawableForDensity( + WORK_PROFILE_USER_ICON, + SOLID_COLORED, + context.getResources().getDisplayMetrics().densityDpi, + /* default= */ () -> getDrawableForDisplayDensity( + context, com.android.internal.R.drawable.ic_corp_user_badge)); } private static Drawable getDrawableForDisplayDensity( @@ -181,8 +203,7 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback { && dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(UserHandle.of(userId)) == null; // and has no supervisor if (isCorp) { - badge = getDrawableForDisplayDensity( - context, com.android.internal.R.drawable.ic_corp_badge_case); + badge = getManagementBadge(context); } } return setBadge(badge); @@ -192,16 +213,35 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback { * Sets the managed badge to this user icon if the device has a device owner. */ public UserIconDrawable setBadgeIfManagedDevice(Context context) { + DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); Drawable badge = null; - boolean deviceOwnerExists = context.getSystemService(DevicePolicyManager.class) - .getDeviceOwnerComponentOnAnyUser() != null; + boolean deviceOwnerExists = dpm.getDeviceOwnerComponentOnAnyUser() != null; if (deviceOwnerExists) { - badge = getDrawableForDisplayDensity( - context, com.android.internal.R.drawable.ic_corp_badge_case); + badge = getManagementBadge(context); } return setBadge(badge); } + private static Drawable getManagementBadge(Context context) { + if (BuildCompat.isAtLeastT()) { + return getUpdatableManagementBadge(context); + } else { + return getDrawableForDisplayDensity( + context, com.android.internal.R.drawable.ic_corp_user_badge); + } + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private static Drawable getUpdatableManagementBadge(Context context) { + DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); + return dpm.getDrawableForDensity( + WORK_PROFILE_ICON, + SOLID_COLORED, + context.getResources().getDisplayMetrics().densityDpi, + /* default= */ () -> getDrawableForDisplayDensity( + context, com.android.internal.R.drawable.ic_corp_badge_case)); + } + public void setBadgeRadius(float radius) { mBadgeRadius = radius; onBoundsChange(getBounds()); 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/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java index b0a647e0dd2f..95f7ef41b10b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java @@ -81,8 +81,8 @@ public class DataUsageUtilsTest { when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false); final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse(); } @Test @@ -94,8 +94,8 @@ public class DataUsageUtilsTest { .thenReturn(new String[] {SUBSCRIBER_ID}); final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse(); } @Test @@ -107,7 +107,7 @@ public class DataUsageUtilsTest { .thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID_2}); final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue(); - assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue(); + assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isTrue(); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 00b5f5019485..a6bfc408be7e 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -84,6 +84,7 @@ public class SystemSettings { Settings.System.RING_VIBRATION_INTENSITY, Settings.System.HAPTIC_FEEDBACK_INTENSITY, Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, + Settings.System.HAPTIC_FEEDBACK_ENABLED, Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE Settings.System.DISPLAY_COLOR_MODE, Settings.System.ALARM_ALERT, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 246466e16c74..bbfab0bfa792 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -314,6 +314,7 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_HEIGHT, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 6bcb7695cd22..06712cc68b89 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -124,6 +124,7 @@ public class SystemSettingsValidators { VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); + VALIDATORS.put(System.HAPTIC_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.RINGTONE, URI_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR); VALIDATORS.put(System.ALARM_ALERT, URI_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 13ae87015fa4..be25b4717976 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -594,6 +594,7 @@ public class SettingsBackupTest { Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, Settings.Global.CLOCKWORK_HOME_READY, + Settings.Global.WATCHDOG_TIMEOUT_MILLIS, Settings.Global.Wearable.BATTERY_SAVER_MODE, Settings.Global.Wearable.COMBINED_LOCATION_ENABLED, Settings.Global.Wearable.HAS_PAY_TOKENS, @@ -659,7 +660,8 @@ public class SettingsBackupTest { Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY, Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED, Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER, - Settings.Global.Wearable.WET_MODE_ON); + Settings.Global.Wearable.WET_MODE_ON, + Settings.Global.Wearable.COOLDOWN_MODE_ON); private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS = newHashSet( diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 6f4cd4a0f6ee..ef5849c73b72 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -562,12 +562,14 @@ <!-- Permissions required for CTS test - TrustTestCases --> <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" /> <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <uses-permission android:name="android.permission.TRUST_LISTENER" /> <!-- Permission required for CTS test - CtsGameManagerTestCases --> <uses-permission android:name="android.permission.MANAGE_GAME_MODE" /> <!-- Permission required for CTS test - CtsGameServiceTestCases --> <uses-permission android:name="android.permission.SET_GAME_SERVICE" /> + <uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY" /> <!-- Permission required for CTS test - ClipboardManagerTest --> <uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 776a5117cb2e..c9bd3710ca79 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -39,6 +39,7 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> + <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" /> <uses-permission android:name="android.permission.DUMP" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> 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-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml new file mode 100644 index 000000000000..989115697d6f --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml @@ -0,0 +1,20 @@ +<!-- + ~ 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/system_neutral1_800" /> + <corners android:radius="@dimen/ongoing_call_chip_corner_radius" /> +</shape> 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/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index 850b01717308..e47eed9ea04a 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -30,14 +30,39 @@ android:id="@+id/status_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_marginStart="@dimen/system_icons_super_container_margin_start" android:paddingTop="@dimen/status_bar_padding_top" android:layout_alignParentEnd="true" android:gravity="center_vertical|end" > + <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer + android:id="@+id/user_switcher_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="horizontal" + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:background="@drawable/status_bar_user_chip_bg" + android:visibility="visible" > + <ImageView android:id="@+id/current_user_avatar" + android:layout_width="@dimen/multi_user_avatar_keyguard_size" + android:layout_height="@dimen/multi_user_avatar_keyguard_size" + android:scaleType="centerInside" + android:paddingEnd="4dp" /> + + <TextView android:id="@+id/current_user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + /> + </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer> + <FrameLayout android:id="@+id/system_icons_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" - android:layout_marginStart="@dimen/system_icons_super_container_margin_start" android:layout_marginEnd="@dimen/status_bar_padding_end" android:gravity="center_vertical|end"> <include layout="@layout/system_icons" /> diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml index 51d160892a01..978998d6ca2c 100644 --- a/packages/SystemUI/res/layout/media_session_view.xml +++ b/packages/SystemUI/res/layout/media_session_view.xml @@ -29,14 +29,14 @@ android:theme="@style/MediaPlayer"> <ImageView + android:id="@+id/album_art" android:layout_width="match_parent" - android:layout_height="184dp" + android:layout_height="@dimen/qs_media_session_height_expanded" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:translationZ="0dp" - android:id="@+id/album_art" android:scaleType="centerCrop" android:adjustViewBounds="true" android:clipToOutline="true" @@ -154,10 +154,7 @@ android:layout_marginStart="@dimen/qs_media_padding" android:layout_marginEnd="@dimen/qs_media_padding" android:layout_marginTop="0dp" - android:layout_marginBottom="0dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/media_seamless" - app:layout_constraintBottom_toTopOf="@id/actionEnd" /> + android:layout_marginBottom="0dp" /> <ImageButton android:id="@+id/actionPrev" @@ -168,11 +165,8 @@ android:layout_marginEnd="0dp" android:layout_marginBottom="0dp" android:layout_marginTop="0dp" - app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/media_progress_bar" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + app:layout_constraintHorizontal_bias="1" + app:layout_constraintHorizontal_chainStyle="packed" /> <!-- Seek Bar --> <!-- As per Material Design on Bidirectionality, this is forced to LTR in code --> @@ -188,11 +182,7 @@ android:layout_marginBottom="0dp" android:layout_marginTop="0dp" android:layout_marginStart="0dp" - android:layout_marginEnd="0dp" - app:layout_constraintStart_toEndOf="@id/actionPrev" - app:layout_constraintEnd_toStartOf="@id/actionNext" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + android:layout_marginEnd="0dp" /> <ImageButton android:id="@+id/actionNext" @@ -202,11 +192,7 @@ android:layout_marginStart="0dp" android:layout_marginEnd="@dimen/qs_media_action_spacing" android:layout_marginBottom="0dp" - android:layout_marginTop="0dp" - app:layout_constraintStart_toEndOf="@id/media_progress_bar" - app:layout_constraintEnd_toStartOf="@id/actionStart" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + android:layout_marginTop="0dp" /> <ImageButton android:id="@+id/actionStart" @@ -216,11 +202,7 @@ android:layout_marginStart="@dimen/qs_media_action_spacing" android:layout_marginEnd="@dimen/qs_media_action_spacing" android:layout_marginBottom="0dp" - android:layout_marginTop="0dp" - app:layout_constraintStart_toEndOf="@id/actionNext" - app:layout_constraintEnd_toStartOf="@id/actionEnd" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + android:layout_marginTop="0dp" /> <ImageButton android:id="@+id/actionEnd" @@ -231,11 +213,7 @@ android:layout_marginEnd="4dp" android:layout_marginBottom="0dp" android:layout_marginTop="0dp" - app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintStart_toEndOf="@id/actionStart" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + app:layout_constraintHorizontal_chainStyle="packed" /> <!-- Long press menu --> <TextView diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 536b0423ce16..02c58e468c30 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -26,9 +26,9 @@ android:layout_marginEnd="0dp" android:layout_gravity="center_vertical | start"> - <TextView + <com.android.systemui.util.SafeMarqueeTextView android:id="@+id/tile_label" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="start" android:textDirection="locale" @@ -37,9 +37,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.QS.TileLabel"/> - <TextView + <com.android.systemui.util.SafeMarqueeTextView android:id="@+id/app_label" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="start" android:textDirection="locale" diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 8b244c757649..af9801936207 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -119,6 +119,32 @@ android:gravity="center_vertical|end" > + <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer + android:id="@+id/user_switcher_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="horizontal" + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:layout_marginEnd="16dp" + android:background="@drawable/status_bar_user_chip_bg" + android:visibility="visible" > + <ImageView android:id="@+id/current_user_avatar" + android:layout_width="@dimen/multi_user_avatar_keyguard_size" + android:layout_height="@dimen/multi_user_avatar_keyguard_size" + android:scaleType="centerInside" + android:paddingEnd="4dp" /> + + <TextView android:id="@+id/current_user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + /> + </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer> + <include layout="@layout/system_icons" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index 6d5c7d40a5f8..4f4bae49b275 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -17,7 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/system_icons" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical"> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index 1b8453ae824d..abc69b080440 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -23,6 +23,9 @@ <!-- The maximum number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">4</integer> + <!-- Use collapsed layout for media player in landscape QQS --> + <bool name="config_quickSettingsMediaLandscapeCollapsed">false</bool> + <!-- Nav bar button default ordering/layout --> <string name="config_navBarLayout" translatable="false">left;back,home,recent;right</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 079f5d0a2805..47822b77a93f 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -102,6 +102,9 @@ <item>one_handed_mode_enabled:onehanded</item> </string-array> + <!-- Use collapsed layout for media player in landscape QQS --> + <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool> + <!-- Show indicator for Wifi on but not connected. --> <bool name="config_showWifiIndicatorWhenEnabled">false</bool> @@ -683,7 +686,7 @@ <integer name="config_connectionMinDuration">1000</integer> <!-- Flag to activate notification to contents feature --> - <bool name="config_notificationToContents">false</bool> + <bool name="config_notificationToContents">true</bool> <!-- Respect drawable/rounded_secondary.xml intrinsic size for multiple radius corner path customization for secondary display--> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index dba7290dba09..fdb56314bcaa 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -990,6 +990,8 @@ <!-- Sizes for alternate session-based layout --> <dimen name="qs_media_session_enabled_seekbar_vertical_padding">15dp</dimen> <dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen> + <dimen name="qs_media_session_height_expanded">184dp</dimen> + <dimen name="qs_media_session_height_collapsed">128dp</dimen> <!-- Size of Smartspace media recommendations cards in the QSPanel carousel --> <dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 2a70645e49ec..49dd574af829 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -32,4 +32,8 @@ <bool name="flag_smartspace">false</bool> + <!-- Whether the user switcher chip shows in the status bar. When true, the multi user + avatar will no longer show on the lockscreen --> + <bool name="flag_user_switcher_chip">false</bool> + </resources> 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/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml new file mode 100644 index 000000000000..c6e18a6f8740 --- /dev/null +++ b/packages/SystemUI/res/xml/media_session_collapsed.xml @@ -0,0 +1,90 @@ +<?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 + --> +<ConstraintSet + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <Constraint + android:id="@+id/album_art" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_collapsed" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> + + <Constraint + android:id="@+id/actionPlayPause" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginEnd="@dimen/qs_media_padding" + app:layout_constraintStart_toEndOf="@id/actionEnd" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" + app:layout_constraintBottom_toBottomOf="parent" /> + + <Constraint + android:id="@+id/actionPrev" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintHorizontal_bias="1" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toEndOf="@id/header_artist" + app:layout_constraintEnd_toStartOf="@id/media_progress_bar" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> + + <Constraint + android:id="@+id/media_progress_bar" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:visibility="gone" + app:layout_constraintStart_toEndOf="@id/actionPrev" + app:layout_constraintEnd_toStartOf="@id/actionNext" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> + + <Constraint + android:id="@+id/actionNext" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintStart_toEndOf="@id/media_progress_bar" + app:layout_constraintEnd_toStartOf="@id/actionStart" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> + + <Constraint + android:id="@+id/actionStart" + android:layout_width="48dp" + android:layout_height="48dp" + android:visibility="gone" + app:layout_constraintStart_toEndOf="@id/actionNext" + app:layout_constraintEnd_toStartOf="@id/actionEnd" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> + + <Constraint + android:id="@+id/actionEnd" + android:layout_width="48dp" + android:layout_height="48dp" + android:visibility="gone" + app:layout_constraintStart_toEndOf="@id/actionStart" + app:layout_constraintEnd_toStartOf="@id/actionPlayPause" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> + +</ConstraintSet>
\ No newline at end of file diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml new file mode 100644 index 000000000000..18ec7aa4cab4 --- /dev/null +++ b/packages/SystemUI/res/xml/media_session_expanded.xml @@ -0,0 +1,84 @@ +<?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 + --> +<ConstraintSet + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <Constraint + android:id="@+id/album_art" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_media_session_height_expanded" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> + + <Constraint + android:id="@+id/actionPlayPause" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginEnd="@dimen/qs_media_padding" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" + app:layout_constraintBottom_toTopOf="@id/actionEnd" /> + + <Constraint + android:id="@+id/actionPrev" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/media_progress_bar" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + + <Constraint + android:id="@+id/media_progress_bar" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintStart_toEndOf="@id/actionPrev" + app:layout_constraintEnd_toStartOf="@id/actionNext" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + + <Constraint + android:id="@+id/actionNext" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintStart_toEndOf="@id/media_progress_bar" + app:layout_constraintEnd_toStartOf="@id/actionStart" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + + <Constraint + android:id="@+id/actionStart" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintStart_toEndOf="@id/actionNext" + app:layout_constraintEnd_toStartOf="@id/actionEnd" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + + <Constraint + android:id="@+id/actionEnd" + android:layout_width="48dp" + android:layout_height="48dp" + app:layout_constraintStart_toEndOf="@id/actionStart" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/actionPlayPause" /> + +</ConstraintSet>
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 8d98a7540a05..56326e36ff5e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -18,6 +18,7 @@ package com.android.systemui.shared.recents.utilities; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import android.annotation.TargetApi; import android.content.Context; @@ -110,11 +111,16 @@ public class Utilities { hints &= ~NAVIGATION_HINT_BACK_ALT; break; } - if (showImeSwitcher) { + if (imeShown) { hints |= NAVIGATION_HINT_IME_SHOWN; } else { hints &= ~NAVIGATION_HINT_IME_SHOWN; } + if (showImeSwitcher) { + hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN; + } else { + hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN; + } return hints; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index eebc7918c72c..08b4d3f68a87 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -59,6 +59,7 @@ public class QuickStepContract { public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; // See IRecentTasks.aidl public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks"; + public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; public static final String NAV_BAR_MODE_3BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java index fc14b6a99008..8fc86004c400 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java @@ -20,7 +20,11 @@ import com.android.keyguard.CarrierText; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -39,4 +43,17 @@ public abstract class KeyguardStatusBarViewModule { static BatteryMeterView getBatteryMeterView(KeyguardStatusBarView view) { return view.findViewById(R.id.battery); } + + /** */ + @Provides + @KeyguardStatusBarViewScope + static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) { + return view.findViewById(R.id.user_switcher_container); + } + + /** */ + @Binds + @KeyguardStatusBarViewScope + abstract StatusBarUserSwitcherController bindStatusBarUserSwitcherController( + StatusBarUserSwitcherControllerImpl controller); } diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 4a0c30ccd798..3d0c08bb5237 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -39,6 +39,8 @@ import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import com.android.systemui.animation.Interpolators; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -82,6 +84,7 @@ public class SwipeHelper implements Gefingerpoken { private final int mSwipeDirection; private final VelocityTracker mVelocityTracker; private final FalsingManager mFalsingManager; + private final FeatureFlags mFeatureFlags; private float mInitialTouchPos; private float mPerpendicularInitialTouchPos; @@ -128,7 +131,8 @@ public class SwipeHelper implements Gefingerpoken { public SwipeHelper( int swipeDirection, Callback callback, Resources resources, - ViewConfiguration viewConfiguration, FalsingManager falsingManager) { + ViewConfiguration viewConfiguration, FalsingManager falsingManager, + FeatureFlags featureFlags) { mCallback = callback; mHandler = new Handler(); mSwipeDirection = swipeDirection; @@ -146,6 +150,7 @@ public class SwipeHelper implements Gefingerpoken { mFadeDependingOnAmountSwiped = resources.getBoolean( R.bool.config_fadeDependingOnAmountSwiped); mFalsingManager = falsingManager; + mFeatureFlags = featureFlags; mFlingAnimationUtils = new FlingAnimationUtils(resources.getDisplayMetrics(), getMaxEscapeAnimDuration() / 1000f); } @@ -795,7 +800,7 @@ public class SwipeHelper implements Gefingerpoken { } private boolean isAvailableToDragAndDrop(View v) { - if (v.getResources().getBoolean(R.bool.config_notificationToContents)) { + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) { if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow enr = (ExpandableNotificationRow) v; boolean canBubble = enr.getEntry().canBubble(); 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/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 20cd5b987c1c..68b74bdd7add 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -41,6 +41,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; @@ -96,6 +97,7 @@ public class DozeTriggers implements DozeMachine.Part { private final AuthController mAuthController; private final DelayableExecutor mMainExecutor; private final KeyguardStateController mKeyguardStateController; + private final BatteryController mBatteryController; private final UiEventLogger mUiEventLogger; private final DevicePostureController mDevicePostureController; @@ -187,7 +189,8 @@ public class DozeTriggers implements DozeMachine.Part { @Main DelayableExecutor mainExecutor, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - DevicePostureController devicePostureController) { + DevicePostureController devicePostureController, + BatteryController batteryController) { mContext = context; mDozeHost = dozeHost; mConfig = config; @@ -210,6 +213,7 @@ public class DozeTriggers implements DozeMachine.Part { mMainExecutor = mainExecutor; mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; + mBatteryController = batteryController; } private final DevicePostureController.Callback mDevicePostureCallback = posture -> { @@ -320,7 +324,12 @@ public class DozeTriggers implements DozeMachine.Part { gentleWakeUp(pulseReason); } else if (isPickup) { if (shouldDropPickupEvent()) { - mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded"); + mDozeLog.traceSensorEventDropped( + pulseReason, + "keyguardOccluded=" + + mKeyguardStateController.isOccluded() + + " pluggedInWireless=" + + mBatteryController.isPluggedInWireless()); return; } gentleWakeUp(pulseReason); @@ -351,7 +360,7 @@ public class DozeTriggers implements DozeMachine.Part { } private boolean shouldDropPickupEvent() { - return mKeyguardStateController.isOccluded(); + return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless(); } private void gentleWakeUp(int reason) { 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/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 357a68fc6502..12c6e003bb92 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -46,9 +46,6 @@ public class Flags { public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING = new BooleanFlag(103, false); - public static final ResourceBooleanFlag NOTIFICATION_SHADE_DRAG = - new ResourceBooleanFlag(104, R.bool.config_enableNotificationShadeDrag); - public static final BooleanFlag NSSL_DEBUG_LINES = new BooleanFlag(105, false); @@ -58,6 +55,9 @@ public class Flags { public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE = new BooleanFlag(107, false); + public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS = + new ResourceBooleanFlag(108, R.bool.config_notificationToContents); + /***************************************/ // 200 - keyguard/lockscreen @@ -114,8 +114,8 @@ public class Flags { public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS = new BooleanFlag(601, false); - public static final BooleanFlag STATUS_BAR_USER_SWITCHER = - new BooleanFlag(602, false); + public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER = + new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip); /***************************************/ // 700 - dialer/calls diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 30429fbf8cfa..2a737970907a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -49,7 +49,6 @@ import androidx.slice.builders.SliceAction; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -139,6 +138,8 @@ public class KeyguardSliceProvider extends SliceProvider implements public StatusBarStateController mStatusBarStateController; @Inject public KeyguardBypassController mKeyguardBypassController; + @Inject + public KeyguardUpdateMonitor mKeyguardUpdateMonitor; private CharSequence mMediaTitle; private CharSequence mMediaArtist; protected boolean mDozing; @@ -333,7 +334,7 @@ public class KeyguardSliceProvider extends SliceProvider implements mAlarmManager.cancel(mUpdateNextAlarm); if (mRegistered) { mRegistered = false; - getKeyguardUpdateMonitor().removeCallback(mKeyguardUpdateMonitorCallback); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); getContext().unregisterReceiver(mIntentReceiver); } KeyguardSliceProvider.sInstance = null; @@ -389,7 +390,7 @@ public class KeyguardSliceProvider extends SliceProvider implements filter.addAction(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/, null /* scheduler */); - getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback); + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); mRegistered = true; } } @@ -441,10 +442,6 @@ public class KeyguardSliceProvider extends SliceProvider implements updateNextAlarm(); } - private KeyguardUpdateMonitor getKeyguardUpdateMonitor() { - return Dependency.get(KeyguardUpdateMonitor.class); - } - /** * Called whenever new media metadata is available. * @param metadata New metadata. diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index 48f48266fda5..c3f4ce986596 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -45,7 +45,8 @@ class KeyguardMediaController @Inject constructor( private val statusBarStateController: SysuiStatusBarStateController, private val notifLockscreenUserManager: NotificationLockscreenUserManager, private val context: Context, - configurationController: ConfigurationController + configurationController: ConfigurationController, + private val mediaFlags: MediaFlags ) { init { @@ -61,7 +62,11 @@ class KeyguardMediaController @Inject constructor( }) // First let's set the desired state that we want for this host - mediaHost.expansion = MediaHostState.COLLAPSED + mediaHost.expansion = if (mediaFlags.useMediaSessionLayout()) { + MediaHostState.EXPANDED + } else { + MediaHostState.COLLAPSED + } mediaHost.showsOnlyActiveMedia = true mediaHost.falsingProtectionNeeded = true diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 0223c6042499..240ca3678765 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -657,7 +657,10 @@ class MediaDataManager( } val runnable = if (action.actionIntent != null) { Runnable { - if (action.isAuthenticationRequired()) { + if (action.actionIntent.isActivity) { + activityStarter.startPendingIntentDismissingKeyguard( + action.actionIntent) + } else if (action.isAuthenticationRequired()) { activityStarter.dismissKeyguardThenExecute({ var result = sendPendingIntent(action.actionIntent) result diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index 791a312f341e..591aad1014bd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -471,14 +471,16 @@ class MediaViewController @Inject constructor( private fun updateMediaViewControllerType(type: TYPE) { this.type = type + + // These XML resources contain ConstraintSets that will apply to this player type's layout when (type) { TYPE.PLAYER -> { collapsedLayout.load(context, R.xml.media_collapsed) expandedLayout.load(context, R.xml.media_expanded) } TYPE.PLAYER_SESSION -> { - collapsedLayout.clone(context, R.layout.media_session_view) - expandedLayout.clone(context, R.layout.media_session_view) + collapsedLayout.load(context, R.xml.media_session_collapsed) + expandedLayout.load(context, R.xml.media_session_expanded) } TYPE.RECOMMENDATION -> { collapsedLayout.load(context, R.xml.media_recommendation_collapsed) 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..f8b34f9769e4 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)); } /** */ @@ -113,12 +115,14 @@ public interface MediaModule { @SysUISingleton static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver( MediaTttFlags mediaTttFlags, + CommandQueue commandQueue, Context context, WindowManager windowManager) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerReceiver(context, windowManager)); + return Optional.of( + new MediaTttChipControllerReceiver(commandQueue, context, windowManager)); } /** */ @@ -128,15 +132,12 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, - MediaTttChipControllerReceiver mediaTttChipControllerReceiver) { + @Main Executor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of( - new MediaTttCommandLineHelper( - commandRegistry, - context, - mediaTttChipControllerReceiver)); + new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor)); } /** Inject into NearbyMediaDevicesService. */ 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..26f31cd11704 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -18,16 +18,13 @@ package com.android.systemui.media.taptotransfer import android.app.StatusBarManager 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.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.dagger.qualifiers.Main +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 +33,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,19 +44,33 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, private val context: Context, - private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver, + @Main private val mainExecutor: Executor ) { - 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( - ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() } - commandRegistry.registerCommand( - REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() } + commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() } } /** All commands for the sender device. */ @@ -68,41 +80,81 @@ class MediaTttCommandLineHelper @Inject constructor( .addFeature("feature") .build() + val commandName = args[1] + @StatusBarManager.MediaTransferSenderState + val displayState = stateStringToStateInt[commandName] + if (displayState == null) { + pw.println("Invalid command name $commandName") + 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. } - override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>") + private fun getUndoExecutor( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Executor? { + return if (isSucceededState(displayState)) { + mainExecutor + } else { + null + } } - } - /** A command to DISPLAY the media ttt chip on the RECEIVER device. */ - inner class AddChipCommandReceiver : Command { - override fun execute(pw: PrintWriter, args: List<String>) { - mediaTttChipControllerReceiver.displayChip( - ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION) - ) + 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 $ADD_CHIP_COMMAND_RECEIVER_TAG") + pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>") } } - /** A command to REMOVE the media ttt chip on the RECEIVER device. */ - inner class RemoveChipCommandReceiver : Command { + /** All commands for the receiver device. */ + inner class ReceiverCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { - mediaTttChipControllerReceiver.removeChip() + val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) + as StatusBarManager + when(val commandName = args[0]) { + CLOSE_TO_SENDER_STATE -> + statusBarManager.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + FAR_FROM_SENDER_STATE -> + statusBarManager.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + else -> + pw.println("Invalid command name $commandName") + } } + override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG") + pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND <chipState>") } } } @@ -110,29 +162,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!! +const val RECEIVER_COMMAND = "media-ttt-chip-receiver" @VisibleForTesting -val TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME = - TransferToThisDeviceSucceeded::class.simpleName!! +const val FAR_FROM_RECEIVER_STATE = "FarFromReceiver" @VisibleForTesting -val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!! +const val CLOSE_TO_SENDER_STATE = "CloseToSender" @VisibleForTesting -val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver" +const val FAR_FROM_SENDER_STATE = "FarFromSender" +private const val CLI_TAG = "MediaTransferCli" -private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon" -private const val TAG = "MediaTapToTransferCli" +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/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 17809548d1fd..2d3ca5fdb6b8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -16,12 +16,18 @@ package com.android.systemui.media.taptotransfer.receiver +import android.app.StatusBarManager import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Icon +import android.media.MediaRoute2Info +import android.util.Log import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon +import com.android.systemui.statusbar.CommandQueue import javax.inject.Inject /** @@ -31,13 +37,49 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipControllerReceiver @Inject constructor( + commandQueue: CommandQueue, context: Context, windowManager: WindowManager, ) : MediaTttChipControllerCommon<ChipStateReceiver>( context, windowManager, R.layout.media_ttt_chip_receiver ) { + // TODO(b/216141279): Use app icon from media route info instead of this fake one. + private val fakeAppIconDrawable = + Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { + it.setTint(Color.YELLOW) + } + + private val commandQueueCallbacks = object : CommandQueue.Callbacks { + override fun updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState displayState: Int, + routeInfo: MediaRoute2Info + ) { + this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay( + displayState, routeInfo + ) + } + } + + init { + commandQueue.addCallback(commandQueueCallbacks) + } + + private fun updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState displayState: Int, + routeInfo: MediaRoute2Info + ) { + when(displayState) { + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> + displayChip(ChipStateReceiver(fakeAppIconDrawable, routeInfo.name.toString())) + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> removeChip() + else -> + Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState") + } + } override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) { setIcon(chipState, currentChipView) } } + +private const val RECEIVER_TAG = "MediaTapToTransferReceiver" 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/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 1bef32ad8caf..4b550f2fb875 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -17,7 +17,7 @@ package com.android.systemui.navigationbar; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; -import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; @@ -1410,7 +1410,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, - (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) + (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0) .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, allowSystemGestureIgnoringBarVisibility()) .commitUpdate(mDisplayId); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 2dd89f3c4dcd..593b278c8278 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -774,11 +774,12 @@ public class NavigationBarView extends FrameLayout implements updateRecentsIcon(); boolean isImeRenderingNavButtons = isGesturalMode(mNavBarMode) - && mImeCanRenderGesturalNavButtons; + && mImeCanRenderGesturalNavButtons + && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0; // Update IME button visibility, a11y and rotate button always overrides the appearance boolean disableImeSwitcher = - (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0 + (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0 || isImeRenderingNavButtons; mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index ec15b2469358..75a3df7cad0c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -17,7 +17,7 @@ package com.android.systemui.navigationbar; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; -import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; import static android.view.InsetsState.containsType; @@ -293,7 +293,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, - (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) + (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0) .setFlag(SYSUI_STATE_OVERVIEW_DISABLED, (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0) .setFlag(SYSUI_STATE_HOME_DISABLED, 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..58ebe89f199a --- /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, true) + + 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/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 2d2fa1f08452..a3af0e58c43d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -17,12 +17,16 @@ package com.android.systemui.qs; import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL; +import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; +import androidx.annotation.VisibleForTesting; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; +import com.android.systemui.media.MediaFlags; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.QSTile; @@ -31,6 +35,7 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.brightness.BrightnessMirrorHandler; import com.android.systemui.statusbar.policy.BrightnessMirrorController; +import com.android.systemui.util.leak.RotationUtils; import java.util.ArrayList; import java.util.List; @@ -54,11 +59,16 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> private final QuickQSBrightnessController mBrightnessController; private final BrightnessMirrorHandler mBrightnessMirrorHandler; + private final MediaFlags mMediaFlags; + private final boolean mUsingCollapsedLandscapeMedia; + @Inject QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost, QSCustomizerController qsCustomizerController, @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, @Named(QUICK_QS_PANEL) MediaHost mediaHost, + @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia, + MediaFlags mediaFlags, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, DumpManager dumpManager, QuickQSBrightnessController quickQSBrightnessController @@ -67,17 +77,36 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> uiEventLogger, qsLogger, dumpManager); mBrightnessController = quickQSBrightnessController; mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); + mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia; + mMediaFlags = mediaFlags; } @Override protected void onInit() { super.onInit(); - mMediaHost.setExpansion(0.0f); + updateMediaExpansion(); mMediaHost.setShowsOnlyActiveMedia(true); mMediaHost.init(MediaHierarchyManager.LOCATION_QQS); mBrightnessController.init(mShouldUseSplitNotificationShade); } + private void updateMediaExpansion() { + int rotation = getRotation(); + boolean isLandscape = rotation == RotationUtils.ROTATION_LANDSCAPE + || rotation == RotationUtils.ROTATION_SEASCAPE; + if (mMediaFlags.useMediaSessionLayout() + && (!mUsingCollapsedLandscapeMedia || !isLandscape)) { + mMediaHost.setExpansion(MediaHost.EXPANDED); + } else { + mMediaHost.setExpansion(MediaHost.COLLAPSED); + } + } + + @VisibleForTesting + protected int getRotation() { + return RotationUtils.getRotation(getContext()); + } + @Override protected void onViewAttached() { super.onViewAttached(); @@ -116,6 +145,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Override protected void onConfigurationChanged() { mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade); + updateMediaExpansion(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index 776ee1021db2..fdf9ae0d4b63 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.dagger; +import static com.android.systemui.util.Utils.useCollapsedMediaInLandscape; import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.content.Context; @@ -56,6 +57,7 @@ public interface QSFragmentModule { String QS_FGS_MANAGER_FOOTER_VIEW = "qs_fgs_manager_footer"; String QS_SECURITY_FOOTER_VIEW = "qs_security_footer"; String QS_USING_MEDIA_PLAYER = "qs_using_media_player"; + String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media"; /** * Provide a context themed using the QS theme @@ -173,6 +175,13 @@ public interface QSFragmentModule { /** */ @Provides + @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) + static boolean providesQSUsingCollapsedLandscapeMedia(Context context) { + return useCollapsedMediaInLandscape(context.getResources()); + } + + /** */ + @Provides @QSScope static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) { return qsHeader.findViewById(R.id.privacy_chip); 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/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 821dfa5fc902..a712ce2e6394 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -25,6 +25,7 @@ import android.content.res.Configuration import android.content.res.Resources.ID_NULL import android.graphics.drawable.Drawable import android.graphics.drawable.RippleDrawable +import android.os.Trace import android.service.quicksettings.Tile import android.text.TextUtils import android.util.Log @@ -163,6 +164,12 @@ open class QSTileViewImpl @JvmOverloads constructor( updateResources() } + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + Trace.traceBegin(Trace.TRACE_TAG_APP, "QSTileViewImpl#onMeasure") + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + Trace.endSection() + } + override fun resetOverride() { heightOverride = HeightOverrideable.NO_OVERRIDE updateHeight() diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 00a314943f7a..1218fd307931 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -26,6 +26,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; @@ -104,6 +105,7 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; +import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; @@ -163,6 +165,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final Optional<StartingSurface> mStartingSurface; private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController; private final Optional<RecentTasks> mRecentTasks; + private final Optional<BackAnimation> mBackAnimation; private final UiEventLogger mUiEventLogger; private Region mActiveNavBarRegion; @@ -508,6 +511,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mRecentTasks.ifPresent(recentTasks -> params.putBinder( KEY_EXTRA_RECENT_TASKS, recentTasks.createExternalInterface().asBinder())); + mBackAnimation.ifPresent((backAnimation) -> params.putBinder( + KEY_EXTRA_SHELL_BACK_ANIMATION, + backAnimation.createExternalInterface().asBinder())); try { mOverviewProxy.onInitialize(params); @@ -566,6 +572,7 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, Optional<RecentTasks> recentTasks, + Optional<BackAnimation> backAnimation, Optional<StartingSurface> startingSurface, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, @@ -593,6 +600,7 @@ public class OverviewProxyService extends CurrentUserTracker implements mOneHandedOptional = oneHandedOptional; mShellTransitions = shellTransitions; mRecentTasks = recentTasks; + mBackAnimation = backAnimation; mUiEventLogger = uiEventLogger; // Assumes device always starts with back button until launcher tells it that it does not 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/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt index 8dc01f014192..236129f8eb50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt @@ -33,7 +33,7 @@ import android.util.MathUtils * * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java. */ -class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { +class DwellRippleShader internal constructor() : RuntimeShader(SHADER) { companion object { private const val SHADER_UNIFORMS = """uniform vec2 in_origin; uniform float in_time; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt index 22fbf9139f1d..bdad36c58480 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt @@ -28,7 +28,7 @@ import android.util.MathUtils * * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java. */ -class RippleShader internal constructor() : RuntimeShader(SHADER, false) { +class RippleShader internal constructor() : RuntimeShader(SHADER) { companion object { private const val SHADER_UNIFORMS = """uniform vec2 in_origin; uniform float in_progress; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt new file mode 100644 index 000000000000..6f8e5da70497 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt @@ -0,0 +1,140 @@ +/* + * 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.statusbar.notification.collection + +import android.view.Choreographer +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.util.ListenerSet +import com.android.systemui.util.concurrency.DelayableExecutor +import dagger.Module +import dagger.Provides + +/** + * Choreographs evaluation resulting from multiple asynchronous sources. Specifically, it exposes + * [schedule], and [addOnEvalListener]; the former will "schedule" an asynchronous invocation of the + * latter. Multiple invocations of [schedule] before any added listeners are invoked have no effect. + */ +interface NotifPipelineChoreographer { + /** + * Schedules all listeners registered with [addOnEvalListener] to be asynchronously executed at + * some point in the future. The exact timing is up to the implementation. + */ + fun schedule() + + /** Cancels a pending evaluation triggered by any recent calls to [schedule]. */ + fun cancel() + + /** Adds a listener [Runnable] that will be invoked when the scheduled evaluation occurs. */ + fun addOnEvalListener(onEvalListener: Runnable) + + /** Removes a listener previously registered with [addOnEvalListener]. */ + fun removeOnEvalListener(onEvalListener: Runnable) +} + +@Module +object NotifPipelineChoreographerModule { + @Provides + @JvmStatic + @SysUISingleton + fun provideChoreographer( + choreographer: Choreographer, + @Main mainExecutor: DelayableExecutor + ): NotifPipelineChoreographer = NotifPipelineChoreographerImpl(choreographer, mainExecutor) +} + +private const val TIMEOUT_MS: Long = 100 + +private class NotifPipelineChoreographerImpl( + private val viewChoreographer: Choreographer, + private val executor: DelayableExecutor +) : NotifPipelineChoreographer { + + private val listeners = ListenerSet<Runnable>() + private var timeoutSubscription: Runnable? = null + private var isScheduled = false + + private val frameCallback = Choreographer.FrameCallback { + if (isScheduled) { + isScheduled = false + timeoutSubscription?.run() + listeners.forEach { it.run() } + } + } + + override fun schedule() { + if (isScheduled) return + isScheduled = true + viewChoreographer.postFrameCallback(frameCallback) + if (!isScheduled) { + // Guard against synchronous evaluation of the frame callback. + return + } + timeoutSubscription = executor.executeDelayed(::onTimeout, TIMEOUT_MS) + } + + override fun cancel() { + if (!isScheduled) return + timeoutSubscription?.run() + viewChoreographer.removeFrameCallback(frameCallback) + } + + override fun addOnEvalListener(onEvalListener: Runnable) { + listeners.addIfAbsent(onEvalListener) + } + + override fun removeOnEvalListener(onEvalListener: Runnable) { + listeners.remove(onEvalListener) + } + + private fun onTimeout() { + if (isScheduled) { + isScheduled = false + viewChoreographer.removeFrameCallback(frameCallback) + listeners.forEach { it.run() } + } + } +} + +class FakeNotifPipelineChoreographer : NotifPipelineChoreographer { + + var isScheduled = false + val listeners = ListenerSet<Runnable>() + + fun runIfScheduled() { + if (isScheduled) { + isScheduled = false + listeners.forEach { it.run() } + } + } + + override fun schedule() { + isScheduled = true + } + + override fun cancel() { + isScheduled = false + } + + override fun addOnEvalListener(onEvalListener: Runnable) { + listeners.addIfAbsent(onEvalListener) + } + + override fun removeOnEvalListener(onEvalListener: Runnable) { + listeners.remove(onEvalListener) + } +} 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 b328ae8cd0bb..e0c2fd563226 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 @@ -122,20 +122,23 @@ public class ShadeListBuilder implements Dumpable { private List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList); private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList); + private final NotifPipelineChoreographer mChoreographer; @Inject public ShadeListBuilder( - SystemClock systemClock, + DumpManager dumpManager, + NotifPipelineChoreographer pipelineChoreographer, NotifPipelineFlags flags, + NotificationInteractionTracker interactionTracker, ShadeListBuilderLogger logger, - DumpManager dumpManager, - NotificationInteractionTracker interactionTracker + SystemClock systemClock ) { Assert.isMainThread(); mSystemClock = systemClock; mLogger = logger; mAlwaysLogList = flags.isDevLoggingEnabled(); mInteractionTracker = interactionTracker; + mChoreographer = pipelineChoreographer; dumpManager.registerDumpable(TAG, this); setSectioners(Collections.emptyList()); @@ -148,6 +151,7 @@ public class ShadeListBuilder implements Dumpable { public void attach(NotifCollection collection) { Assert.isMainThread(); collection.setBuildListener(mReadyForBuildListener); + mChoreographer.addOnEvalListener(this::buildList); } /** @@ -290,7 +294,7 @@ public class ShadeListBuilder implements Dumpable { mLogger.logOnBuildList(); mAllEntries = entries; - buildList(); + mChoreographer.schedule(); } }; @@ -1281,7 +1285,7 @@ public class ShadeListBuilder implements Dumpable { private void rebuildListIfBefore(@PipelineState.StateName int state) { mPipelineState.requireIsBefore(state); if (mPipelineState.is(STATE_IDLE)) { - buildList(); + mChoreographer.schedule(); } } 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/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 45a9092068e4..35d4582d9dee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotifPipelineChoreographerModule; import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator; import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule; @@ -103,6 +104,7 @@ import dagger.Provides; */ @Module(includes = { CoordinatorsModule.class, + NotifPipelineChoreographerModule.class, NotifPanelEventSourceModule.class, NotificationSectionHeadersModule.class, }) 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..c4beb5bf4d7b 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 @@ -27,8 +27,9 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -89,6 +90,7 @@ public class ExpandableNotificationRowController implements NotifViewController private final OnUserInteractionCallback mOnUserInteractionCallback; private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; + private final FeatureFlags mFeatureFlags; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional<BubblesManager> mBubblesManagerOptional; @@ -119,6 +121,7 @@ public class ExpandableNotificationRowController implements NotifViewController OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, FalsingCollector falsingCollector, + FeatureFlags featureFlags, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional, ExpandableNotificationRowDragController dragController) { @@ -145,6 +148,7 @@ public class ExpandableNotificationRowController implements NotifViewController mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingCollector = falsingCollector; + mFeatureFlags = featureFlags; mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; mDragController = dragController; @@ -179,7 +183,7 @@ public class ExpandableNotificationRowController implements NotifViewController ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { - if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) { + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) { mView.setDragController(mDragController); } @@ -248,19 +252,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/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 0b6d7594ce50..7d035a78450a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -35,6 +35,10 @@ public class FooterView extends StackScrollerDecorView { private FooterViewButton mClearAllButton; private FooterViewButton mManageButton; private boolean mShowHistory; + // String cache, for performance reasons. + // Reading them from a Resources object can be quite slow sometimes. + private String mManageNotificationText; + private String mManageNotificationHistoryText; public FooterView(Context context, AttributeSet attrs) { super(context, attrs); @@ -68,6 +72,8 @@ public class FooterView extends StackScrollerDecorView { super.onFinishInflate(); mClearAllButton = (FooterViewButton) findSecondaryView(); mManageButton = findViewById(R.id.manage_text); + updateResources(); + updateText(); } public void setManageButtonClickListener(OnClickListener listener) { @@ -86,15 +92,20 @@ public class FooterView extends StackScrollerDecorView { } public void showHistory(boolean showHistory) { + if (mShowHistory == showHistory) { + return; + } mShowHistory = showHistory; + updateText(); + } + + private void updateText() { if (mShowHistory) { - mManageButton.setText(R.string.manage_notifications_history_text); - mManageButton.setContentDescription( - mContext.getString(R.string.manage_notifications_history_text)); + mManageButton.setText(mManageNotificationHistoryText); + mManageButton.setContentDescription(mManageNotificationHistoryText); } else { - mManageButton.setText(R.string.manage_notifications_text); - mManageButton.setContentDescription( - mContext.getString(R.string.manage_notifications_text)); + mManageButton.setText(mManageNotificationText); + mManageButton.setContentDescription(mManageNotificationText); } } @@ -109,7 +120,8 @@ public class FooterView extends StackScrollerDecorView { mClearAllButton.setText(R.string.clear_all_notifications_text); mClearAllButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); - showHistory(mShowHistory); + updateResources(); + updateText(); } /** @@ -124,6 +136,12 @@ public class FooterView extends StackScrollerDecorView { mManageButton.setTextColor(textColor); } + private void updateResources() { + mManageNotificationText = getContext().getString(R.string.manage_notifications_text); + mManageNotificationHistoryText = getContext() + .getString(R.string.manage_notifications_history_text); + } + @Override public ExpandableViewState createExpandableViewState() { return new FooterViewState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index ce3e27c55f3e..f40a3c7186e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -76,7 +76,10 @@ public class AmbientState { private float mHideAmount; private boolean mAppearing; private float mPulseHeight = MAX_PULSE_HEIGHT; + + /** How we much we are sleeping. 1f fully dozing (AOD), 0f fully awake (for all other states) */ private float mDozeAmount = 0.0f; + private Runnable mOnPulseHeightChangedListener; private ExpandableNotificationRow mTrackedHeadsUpRow; private float mAppearFraction; @@ -96,6 +99,9 @@ public class AmbientState { /** Height of the notifications panel without top padding when expansion completes. */ private float mStackEndHeight; + /** Whether we are swiping up. */ + private boolean mIsSwipingUp; + /** * @return Height of the notifications panel without top padding when expansion completes. */ @@ -133,6 +139,20 @@ public class AmbientState { } /** + * @param isSwipingUp Whether we are swiping up. + */ + public void setSwipingUp(boolean isSwipingUp) { + mIsSwipingUp = isSwipingUp; + } + + /** + * @return Whether we are swiping up. + */ + public boolean isSwipingUp() { + return mIsSwipingUp; + } + + /** * @return Fraction of shade expansion. */ public float getExpansionFraction() { 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 25b8a6545638..2c4db7745fd4 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 @@ -203,6 +203,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private float mQsExpansionFraction; private final int mSplitShadeMinContentHeight; + /** Whether we are flinging the shade open or closed. */ + private boolean mIsFlinging; + /** * The algorithm which calculates the properties for our children */ @@ -1270,6 +1273,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * @return Whether we should skip stack height update for lockscreen swipe-up or unlock hint. + */ + private boolean shouldSkipHeightUpdate() { + // After the user swipes up on lockscreen and lets go, + // {@link PanelViewController) flings the shade back down. + return mAmbientState.isOnKeyguard() && ( + mAmbientState.isUnlockHintRunning() || mAmbientState.isSwipingUp() || mIsFlinging); + } + + /** * Apply expansion fraction to the y position and height of the notifications panel. * @param listenerNeedsAnimation does the listener need to animate? */ @@ -1283,7 +1296,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mOnStackYChanged != null) { mOnStackYChanged.accept(listenerNeedsAnimation); } - if (mQsExpansionFraction <= 0) { + if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) { final float endHeight = updateStackEndHeight( getHeight(), getEmptyBottomMargin(), mTopPadding); updateStackHeight(endHeight, fraction); @@ -1325,22 +1338,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.COORDINATOR) public void setExpandedHeight(float height) { final float shadeBottom = getHeight() - getEmptyBottomMargin(); - final float expansionFraction = MathUtils.saturate(height / shadeBottom); - mAmbientState.setExpansionFraction(expansionFraction); + final boolean skipHeightUpdate = shouldSkipHeightUpdate(); + if (!skipHeightUpdate) { + final float expansionFraction = MathUtils.saturate(height / shadeBottom); + mAmbientState.setExpansionFraction(expansionFraction); + } updateStackPosition(); - mExpandedHeight = height; - setIsExpanded(height > 0); - int minExpansionHeight = getMinExpansionHeight(); - if (height < minExpansionHeight) { - mClipRect.left = 0; - mClipRect.right = getWidth(); - mClipRect.top = 0; - mClipRect.bottom = (int) height; - height = minExpansionHeight; - setRequestedClipBounds(mClipRect); - } else { - setRequestedClipBounds(null); + if (!skipHeightUpdate) { + mExpandedHeight = height; + setIsExpanded(height > 0); + int minExpansionHeight = getMinExpansionHeight(); + if (height < minExpansionHeight) { + mClipRect.left = 0; + mClipRect.right = getWidth(); + mClipRect.top = 0; + mClipRect.bottom = (int) height; + height = minExpansionHeight; + setRequestedClipBounds(mClipRect); + } else { + setRequestedClipBounds(null); + } } int stackHeight; float translationY; @@ -1368,7 +1386,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } } else { - stackHeight = (int) height; + stackHeight = (int) (skipHeightUpdate ? mExpandedHeight : height); } } else { appearFraction = calculateAppearFraction(height); @@ -1386,7 +1404,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } mAmbientState.setAppearFraction(appearFraction); - if (stackHeight != mCurrentStackHeight) { + if (stackHeight != mCurrentStackHeight && !skipHeightUpdate) { mCurrentStackHeight = stackHeight; updateAlgorithmHeightAndPadding(); requestChildrenUpdate(); @@ -5001,6 +5019,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAmbientState.setUnlockHintRunning(running); } + /** + * @param isFlinging Whether we are flinging the shade open or closed. + */ + public void setIsFlinging(boolean isFlinging) { + mIsFlinging = isFlinging; + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) { mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed; 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 a2929f01f715..e1116f84c15d 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 @@ -1195,6 +1195,13 @@ public class NotificationStackScrollLayoutController { mView.setUnlockHintRunning(running); } + /** + * @param isFlinging Whether we are flinging the shade open or close. + */ + public void setIsFlinging(boolean isFlinging) { + mView.setIsFlinging(isFlinging); + } + public boolean isFooterViewNotGone() { return mView.isFooterViewNotGone(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index 1038e76234ae..2d2fbe588728 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.SwipeHelper; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; @@ -66,9 +67,10 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc NotificationSwipeHelper( Resources resources, ViewConfiguration viewConfiguration, - FalsingManager falsingManager, int swipeDirection, NotificationCallback callback, + FalsingManager falsingManager, FeatureFlags featureFlags, + int swipeDirection, NotificationCallback callback, NotificationMenuRowPlugin.OnMenuEventListener menuListener) { - super(swipeDirection, callback, resources, viewConfiguration, falsingManager); + super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags); mMenuListener = menuListener; mCallback = callback; mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */); @@ -508,16 +510,18 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc private final Resources mResources; private final ViewConfiguration mViewConfiguration; private final FalsingManager mFalsingManager; + private final FeatureFlags mFeatureFlags; private int mSwipeDirection; private NotificationCallback mNotificationCallback; private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener; @Inject Builder(@Main Resources resources, ViewConfiguration viewConfiguration, - FalsingManager falsingManager) { + FalsingManager falsingManager, FeatureFlags featureFlags) { mResources = resources; mViewConfiguration = viewConfiguration; mFalsingManager = falsingManager; + mFeatureFlags = featureFlags; } Builder setSwipeDirection(int swipeDirection) { @@ -538,7 +542,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc NotificationSwipeHelper build() { return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager, - mSwipeDirection, mNotificationCallback, mOnMenuEventListener); + mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 2ec5f250eb48..b8e9875be7e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -40,6 +40,8 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.VisibleForTesting; + import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; @@ -67,8 +69,10 @@ public class KeyguardStatusBarView extends RelativeLayout { private ImageView mMultiUserAvatar; private BatteryMeterView mBatteryView; private StatusIconContainer mStatusIconContainer; + private ViewGroup mUserSwitcherContainer; private boolean mKeyguardUserSwitcherEnabled; + private boolean mKeyguardUserAvatarEnabled; private boolean mIsPrivacyDotEnabled; private int mSystemIconsSwitcherHiddenExpandedMargin; @@ -111,10 +115,15 @@ public class KeyguardStatusBarView extends RelativeLayout { mCutoutSpace = findViewById(R.id.cutout_space_view); mStatusIconArea = findViewById(R.id.status_icon_area); mStatusIconContainer = findViewById(R.id.statusIcons); + mUserSwitcherContainer = findViewById(R.id.user_switcher_container); mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot); loadDimens(); } + public ViewGroup getUserSwitcherContainer() { + return mUserSwitcherContainer; + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -186,6 +195,17 @@ public class KeyguardStatusBarView extends RelativeLayout { } private void updateVisibilities() { + // Multi user avatar is disabled in favor of the user switcher chip + if (!mKeyguardUserAvatarEnabled) { + if (mMultiUserAvatar.getParent() == mStatusIconArea) { + mStatusIconArea.removeView(mMultiUserAvatar); + } else if (mMultiUserAvatar.getParent() != null) { + getOverlay().remove(mMultiUserAvatar); + } + + return; + } + if (mMultiUserAvatar.getParent() != mStatusIconArea && !mKeyguardUserSwitcherEnabled) { if (mMultiUserAvatar.getParent() != null) { @@ -346,6 +366,16 @@ public class KeyguardStatusBarView extends RelativeLayout { mKeyguardUserSwitcherEnabled = enabled; } + void setKeyguardUserAvatarEnabled(boolean enabled) { + mKeyguardUserAvatarEnabled = enabled; + updateVisibilities(); + } + + @VisibleForTesting + boolean isKeyguardUserAvatarEnabled() { + return mKeyguardUserAvatarEnabled; + } + private void animateNextLayoutChange() { final int systemIconsCurrentX = mSystemIconsContainer.getLeft(); final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea; @@ -416,9 +446,14 @@ public class KeyguardStatusBarView extends RelativeLayout { /** Should only be called from {@link KeyguardStatusBarViewController}. */ void onOverlayChanged() { - mCarrierLabel.setTextAppearance( - Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall)); + int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall); + mCarrierLabel.setTextAppearance(theme); mBatteryView.updatePercentView(); + + TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name); + if (userSwitcherName != null) { + userSwitcherName.setTextAppearance(theme); + } } private void updateIconsAndTextColors(StatusBarIconController.TintedIconManager iconManager) { @@ -429,6 +464,14 @@ public class KeyguardStatusBarView extends RelativeLayout { R.color.light_mode_icon_color_single_tone); float intensity = textColor == Color.WHITE ? 0 : 1; mCarrierLabel.setTextColor(iconColor); + + TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name); + if (userSwitcherName != null) { + userSwitcherName.setTextColor(Utils.getColorStateListDefaultColor( + mContext, + R.color.light_mode_icon_color_single_tone)); + } + if (iconManager != null) { iconManager.setTint(iconColor); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index ee97fd631818..1df1aff38593 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -47,6 +47,9 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -95,6 +98,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final SysuiStatusBarStateController mStatusBarStateController; private final StatusBarContentInsetsProvider mInsetsProvider; private final UserManager mUserManager; + private final StatusBarUserSwitcherFeatureController mFeatureController; + private final StatusBarUserSwitcherController mUserSwitcherController; + private final StatusBarUserInfoTracker mStatusBarUserInfoTracker; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -246,7 +252,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat BiometricUnlockController biometricUnlockController, SysuiStatusBarStateController statusBarStateController, StatusBarContentInsetsProvider statusBarContentInsetsProvider, - UserManager userManager + UserManager userManager, + StatusBarUserSwitcherFeatureController featureController, + StatusBarUserSwitcherController userSwitcherController, + StatusBarUserInfoTracker statusBarUserInfoTracker ) { super(view); mCarrierTextController = carrierTextController; @@ -265,6 +274,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mStatusBarStateController = statusBarStateController; mInsetsProvider = statusBarContentInsetsProvider; mUserManager = userManager; + mFeatureController = featureController; + mUserSwitcherController = userSwitcherController; + mStatusBarUserInfoTracker = statusBarUserInfoTracker; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mKeyguardStateController.addCallback( @@ -286,6 +298,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat r.getString(com.android.internal.R.string.status_bar_call_strength))); mNotificationsHeaderCollideDistance = r.getDimensionPixelSize( R.dimen.header_notifications_collide_distance); + + mView.setKeyguardUserAvatarEnabled( + !mFeatureController.isStatusBarUserSwitcherFeatureEnabled()); + mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled)); } @Override @@ -293,6 +309,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat super.onInit(); mCarrierTextController.init(); mBatteryMeterViewController.init(); + mUserSwitcherController.init(); } @Override @@ -334,6 +351,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Sets whether user switcher is enabled. */ public void setKeyguardUserSwitcherEnabled(boolean enabled) { mView.setKeyguardUserSwitcherEnabled(enabled); + // We don't have a listener for when the user switcher setting changes, so this is + // where we re-check the state + mStatusBarUserInfoTracker.checkEnabled(); } /** Sets whether this controller should listen to battery updates. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 3d3a1da1cf82..278b4ec5f53a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -806,7 +806,6 @@ public class NotificationPanelViewController extends PanelViewController QsFrameTranslateController qsFrameTranslateController, KeyguardUnlockAnimationController keyguardUnlockAnimationController) { super(view, - featureFlags, falsingManager, dozeLog, keyguardStateController, @@ -1885,9 +1884,16 @@ public class NotificationPanelViewController extends PanelViewController mHeadsUpTouchHelper.notifyFling(!expand); mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */); setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); + mNotificationStackScrollLayoutController.setIsFlinging(true); super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); } + @Override + protected void onFlingEnd(boolean cancelled) { + super.onFlingEnd(cancelled); + mNotificationStackScrollLayoutController.setIsFlinging(false); + } + private boolean onQsIntercept(MotionEvent event) { int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 474653b9728c..9f9e7d9a276e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -310,10 +310,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW if (onKeyguard && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) { mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate; - mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate; } else { mLpChanged.preferredMaxDisplayRefreshRate = 0; - mLpChanged.preferredMinDisplayRefreshRate = 0; } Trace.setCounter("display_set_preferred_refresh_rate", (long) mKeyguardPreferredRefreshRate); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index c466a8ce6d3f..85e804233ed9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -53,8 +53,6 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.Classifier; import com.android.systemui.doze.DozeLog; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -223,7 +221,6 @@ public abstract class PanelViewController { public PanelViewController( PanelView view, - FeatureFlags featureFlags, FalsingManager falsingManager, DozeLog dozeLog, KeyguardStateController keyguardStateController, @@ -292,7 +289,8 @@ public abstract class PanelViewController { mBounceInterpolator = new BounceInterpolator(); mFalsingManager = falsingManager; mDozeLog = dozeLog; - mNotificationsDragEnabled = featureFlags.isEnabled(Flags.NOTIFICATION_SHADE_DRAG); + mNotificationsDragEnabled = mResources.getBoolean( + R.bool.config_enableNotificationShadeDrag); mVibratorHelper = vibratorHelper; mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation); mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; @@ -461,7 +459,7 @@ public abstract class PanelViewController { boolean expands = onEmptySpaceClick(mInitialTouchX); onTrackingStopped(expands); } - + mAmbientState.setSwipingUp(false); mVelocityTracker.clear(); } @@ -708,7 +706,7 @@ public abstract class PanelViewController { animator.start(); } - private void onFlingEnd(boolean cancelled) { + void onFlingEnd(boolean cancelled) { mIsFlinging = false; // No overshoot when the animation ends setOverExpansionInternal(0, false /* isFromGesture */); @@ -1393,6 +1391,10 @@ public abstract class PanelViewController { mUpwardsWhenThresholdReached = isDirectionUpwards(x, y); } if ((!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) { + // Count h==0 as part of swipe-up, + // otherwise {@link NotificationStackScrollLayout} + // wrongly enables stack height updates at the start of lockscreen swipe-up + mAmbientState.setSwipingUp(h <= 0); setExpandedHeightInternal(newHeight); } break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index d6bf5f21c834..224b2e45bfe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -21,15 +21,19 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver + import com.android.systemui.R import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UNFOLD_STATUS_BAR import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import com.android.systemui.util.ViewController import com.android.systemui.util.kotlin.getOrNull + import java.util.Optional + import javax.inject.Inject import javax.inject.Named @@ -38,6 +42,7 @@ class PhoneStatusBarViewController private constructor( view: PhoneStatusBarView, @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?, private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?, + private val userSwitcherController: StatusBarUserSwitcherController, touchEventHandler: PhoneStatusBarView.TouchEventHandler, private val configurationController: ConfigurationController ) : ViewController<PhoneStatusBarView>(view) { @@ -89,6 +94,10 @@ class PhoneStatusBarViewController private constructor( mView.setTouchEventHandler(touchEventHandler) } + override fun onInit() { + userSwitcherController.init() + } + fun setImportantForAccessibility(mode: Int) { mView.importantForAccessibility = mode } @@ -153,6 +162,7 @@ class PhoneStatusBarViewController private constructor( private val unfoldComponent: Optional<SysUIUnfoldComponent>, @Named(UNFOLD_STATUS_BAR) private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>, + private val userSwitcherController: StatusBarUserSwitcherController, private val configurationController: ConfigurationController ) { fun create( @@ -163,6 +173,7 @@ class PhoneStatusBarViewController private constructor( view, progressProvider.getOrNull(), unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(), + userSwitcherController, touchEventHandler, configurationController ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 329293409dc2..d464acb7fe76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -217,8 +217,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * frameworks/base/core/res/res/values/config.xml */ public void addIgnoredSlot(String slotName) { - addIgnoredSlotInternal(slotName); - requestLayout(); + boolean added = addIgnoredSlotInternal(slotName); + if (added) { + requestLayout(); + } } /** @@ -226,17 +228,27 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * @param slots names of the icons to ignore */ public void addIgnoredSlots(List<String> slots) { + boolean willAddAny = false; for (String slot : slots) { - addIgnoredSlotInternal(slot); + willAddAny |= addIgnoredSlotInternal(slot); } - requestLayout(); + if (willAddAny) { + requestLayout(); + } } - private void addIgnoredSlotInternal(String slotName) { - if (!mIgnoredSlots.contains(slotName)) { - mIgnoredSlots.add(slotName); + /** + * + * @param slotName + * @return + */ + private boolean addIgnoredSlotInternal(String slotName) { + if (mIgnoredSlots.contains(slotName)) { + return false; } + mIgnoredSlots.add(slotName); + return true; } /** @@ -245,9 +257,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * @param slotName name of the icon slot to remove from the ignored list */ public void removeIgnoredSlot(String slotName) { - mIgnoredSlots.remove(slotName); - - requestLayout(); + boolean removed = mIgnoredSlots.remove(slotName); + if (removed) { + requestLayout(); + } } /** @@ -256,11 +269,14 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * @param slots name of the icon slots to remove from the ignored list */ public void removeIgnoredSlots(List<String> slots) { + boolean removedAny = false; for (String slot : slots) { - mIgnoredSlots.remove(slot); + removedAny |= mIgnoredSlots.remove(slot); } - requestLayout(); + if (removedAny) { + requestLayout(); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index dea1b43f579f..e2dc9057e49d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -26,11 +26,15 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; import javax.inject.Named; +import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -83,6 +87,20 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + static StatusBarUserSwitcherContainer provideStatusBarUserSwitcherContainer( + @RootView PhoneStatusBarView view) { + return view.findViewById(R.id.user_switcher_container); + } + + /** */ + @Binds + @StatusBarFragmentScope + StatusBarUserSwitcherController bindStatusBarUserSwitcherController( + StatusBarUserSwitcherControllerImpl controller); + + /** */ + @Provides + @StatusBarFragmentScope static PhoneStatusBarViewController providePhoneStatusBarViewController( PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory, @RootView PhoneStatusBarView phoneStatusBarView, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt new file mode 100644 index 000000000000..2dbc19c653f7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt @@ -0,0 +1,122 @@ +/* + * 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.statusbar.phone.userswitcher + +import android.graphics.drawable.Drawable +import android.os.UserManager + +import com.android.systemui.DejankUtils.whitelistIpcs +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.CallbackController +import com.android.systemui.statusbar.policy.UserInfoController +import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener + +import javax.inject.Inject + +/** + * Since every user switcher chip will user the exact same information and logic on whether or not + * to show, and what data to show, it makes sense to create a single tracker here + */ +@SysUISingleton +class StatusBarUserInfoTracker @Inject constructor( + private val userInfoController: UserInfoController, + private val userManager: UserManager +) : CallbackController<CurrentUserChipInfoUpdatedListener> { + var currentUserName: String? = null + private set + var currentUserAvatar: Drawable? = null + private set + var userSwitcherEnabled = false + private set + private var listening = false + + private val listeners = mutableListOf<CurrentUserChipInfoUpdatedListener>() + + private val userInfoChangedListener = OnUserInfoChangedListener { name, picture, _ -> + currentUserAvatar = picture + currentUserName = name + notifyListenersUserInfoChanged() + } + + init { + startListening() + } + + override fun addCallback(listener: CurrentUserChipInfoUpdatedListener) { + if (listeners.isEmpty()) { + startListening() + } + + if (!listeners.contains(listener)) { + listeners.add(listener) + } + } + + override fun removeCallback(listener: CurrentUserChipInfoUpdatedListener) { + listeners.remove(listener) + + if (listeners.isEmpty()) { + stopListening() + } + } + + private fun notifyListenersUserInfoChanged() { + listeners.forEach { + it.onCurrentUserChipInfoUpdated() + } + } + + private fun notifyListenersSettingChanged() { + listeners.forEach { + it.onStatusBarUserSwitcherSettingChanged(userSwitcherEnabled) + } + } + + private fun startListening() { + listening = true + userInfoController.addCallback(userInfoChangedListener) + } + + private fun stopListening() { + listening = false + userInfoController.removeCallback(userInfoChangedListener) + } + + private fun checkUserSwitcherEnabled() { + whitelistIpcs { + userSwitcherEnabled = userManager.isUserSwitcherEnabled + } + } + + /** + * Force a check to [UserManager.isUserSwitcherEnabled], and update listeners if the value has + * changed + */ + fun checkEnabled() { + val wasEnabled = userSwitcherEnabled + checkUserSwitcherEnabled() + + if (wasEnabled != userSwitcherEnabled) { + notifyListenersSettingChanged() + } + } +} + +interface CurrentUserChipInfoUpdatedListener { + fun onCurrentUserChipInfoUpdated() + fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt new file mode 100644 index 000000000000..2c8677dee4d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt @@ -0,0 +1,40 @@ +/* + * 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.statusbar.phone.userswitcher + +import android.content.Context +import android.util.AttributeSet +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.android.systemui.R + +class StatusBarUserSwitcherContainer( + context: Context?, + attrs: AttributeSet? +) : LinearLayout(context, attrs) { + lateinit var text: TextView + private set + lateinit var avatar: ImageView + private set + + override fun onFinishInflate() { + super.onFinishInflate() + text = findViewById(R.id.current_user_name) + avatar = findViewById(R.id.current_user_avatar) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt new file mode 100644 index 000000000000..a1247539c660 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt @@ -0,0 +1,88 @@ +/* + * 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.statusbar.phone.userswitcher + +import android.view.View + +import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.util.ViewController + +import javax.inject.Inject + +/** + * ViewController for [StatusBarUserSwitcherContainer] + */ +class StatusBarUserSwitcherControllerImpl @Inject constructor( + view: StatusBarUserSwitcherContainer, + private val tracker: StatusBarUserInfoTracker, + private val featureController: StatusBarUserSwitcherFeatureController, + private val userSwitcherDialogController: UserSwitchDialogController +) : ViewController<StatusBarUserSwitcherContainer>(view), + StatusBarUserSwitcherController { + private val listener = object : CurrentUserChipInfoUpdatedListener { + override fun onCurrentUserChipInfoUpdated() { + updateChip() + } + + override fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) { + updateEnabled() + } + } + + private val featureFlagListener = object : OnUserSwitcherPreferenceChangeListener { + override fun onUserSwitcherPreferenceChange(enabled: Boolean) { + updateEnabled() + } + } + + override fun onViewAttached() { + tracker.addCallback(listener) + featureController.addCallback(featureFlagListener) + mView.setOnClickListener { + userSwitcherDialogController.showDialog(it) + } + + updateEnabled() + } + + override fun onViewDetached() { + tracker.removeCallback(listener) + featureController.removeCallback(featureFlagListener) + mView.setOnClickListener(null) + } + + private fun updateChip() { + mView.text.text = tracker.currentUserName + mView.avatar.setImageDrawable(tracker.currentUserAvatar) + } + + private fun updateEnabled() { + if (featureController.isStatusBarUserSwitcherFeatureEnabled() && + tracker.userSwitcherEnabled) { + mView.visibility = View.VISIBLE + updateChip() + } else { + mView.visibility = View.GONE + } + } +} + +interface StatusBarUserSwitcherController { + fun init() +} + +private const val TAG = "SbUserSwitcherController" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt new file mode 100644 index 000000000000..7bae9ff72760 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt @@ -0,0 +1,63 @@ +/* + * 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.statusbar.phone.userswitcher + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.policy.CallbackController + +import javax.inject.Inject + +@SysUISingleton +class StatusBarUserSwitcherFeatureController @Inject constructor( + private val flags: FeatureFlags +) : CallbackController<OnUserSwitcherPreferenceChangeListener> { + private val listeners = mutableListOf<OnUserSwitcherPreferenceChangeListener>() + + init { + flags.addListener(Flags.STATUS_BAR_USER_SWITCHER) { + it.requestNoRestart() + notifyListeners() + } + } + + fun isStatusBarUserSwitcherFeatureEnabled(): Boolean { + return flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER) + } + + override fun addCallback(listener: OnUserSwitcherPreferenceChangeListener) { + if (!listeners.contains(listener)) { + listeners.add(listener) + } + } + + override fun removeCallback(listener: OnUserSwitcherPreferenceChangeListener) { + listeners.remove(listener) + } + + private fun notifyListeners() { + val enabled = flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER) + listeners.forEach { + it.onUserSwitcherPreferenceChange(enabled) + } + } +} + +interface OnUserSwitcherPreferenceChangeListener { + fun onUserSwitcherPreferenceChange(enabled: Boolean) +} 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/AutoMarqueeTextView.java b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java index 09dbfee5e0a4..fa4f314ff0a2 100644 --- a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java +++ b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java @@ -19,7 +19,6 @@ package com.android.systemui.util; import android.content.Context; import android.text.TextUtils; import android.util.AttributeSet; -import android.widget.TextView; /** * TextView that changes its ellipsize value with its visibility. @@ -27,7 +26,7 @@ import android.widget.TextView; * The View responds to changes in user-visibility to change its ellipsize from MARQUEE to END * and back. Useful for TextView that need to marquee forever. */ -public class AutoMarqueeTextView extends TextView { +public class AutoMarqueeTextView extends SafeMarqueeTextView { private boolean mAggregatedVisible = false; diff --git a/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt new file mode 100644 index 000000000000..1c1a990e6018 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt @@ -0,0 +1,44 @@ +package com.android.systemui.util + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import android.widget.TextView + +/** + * A TextField that doesn't relayout when changing from marquee to ellipsis. + */ +@SuppressLint("AppCompatCustomView") +open class SafeMarqueeTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : TextView(context, attrs, defStyleAttr, defStyleRes) { + + private var safelyIgnoreLayout = false + private val hasStableWidth + get() = layoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT + + override fun requestLayout() { + if (safelyIgnoreLayout) { + return + } + super.requestLayout() + } + + override fun startMarquee() { + val wasIgnoring = safelyIgnoreLayout + safelyIgnoreLayout = hasStableWidth + super.startMarquee() + safelyIgnoreLayout = wasIgnoring + } + + override fun stopMarquee() { + val wasIgnoring = safelyIgnoreLayout + safelyIgnoreLayout = hasStableWidth + super.stopMarquee() + safelyIgnoreLayout = wasIgnoring + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 407dc5e2787a..71d8e3344937 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -162,6 +162,14 @@ public class Utils { } /** + * Returns true if the device should use the collapsed layout for the media player when in + * landscape (or seascape) orientation + */ + public static boolean useCollapsedMediaInLandscape(Resources resources) { + return resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed); + } + + /** * Returns true if the device should use the split notification shade, based on orientation and * screen width. */ 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/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 35dca7ef5fce..7fc354f8106f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -45,6 +45,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -90,6 +91,8 @@ public class DozeTriggersTest extends SysuiTestCase { private KeyguardStateController mKeyguardStateController; @Mock private DevicePostureController mDevicePostureController; + @Mock + private BatteryController mBatteryController; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -122,7 +125,7 @@ public class DozeTriggersTest extends SysuiTestCase { asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController, - mDevicePostureController); + mDevicePostureController, mBatteryController); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -230,7 +233,9 @@ public class DozeTriggersTest extends SysuiTestCase { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); // WHEN the pick up gesture is triggered and keyguard isn't occluded + // and device isn't on a wireless charger when(mKeyguardStateController.isOccluded()).thenReturn(false); + when(mBatteryController.isPluggedInWireless()).thenReturn(false); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN wakeup @@ -244,6 +249,22 @@ public class DozeTriggersTest extends SysuiTestCase { // WHEN the pick up gesture is triggered and keyguard IS occluded when(mKeyguardStateController.isOccluded()).thenReturn(true); + when(mBatteryController.isPluggedInWireless()).thenReturn(false); + mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); + + // THEN never wakeup + verify(mMachine, never()).wakeUp(); + } + + @Test + public void testPickupGestureWirelessCharger() { + // GIVEN device is in doze (screen blank, but running doze sensors) + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN the pick up gesture is triggered + // and device IS on a wireless charger + when(mKeyguardStateController.isOccluded()).thenReturn(false); + when(mBatteryController.isPluggedInWireless()).thenReturn(true); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN never wakeup diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 1a8326fd5bd1..cad98f4dc713 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -32,6 +32,7 @@ import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -117,6 +118,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { /** * Ensures expansion only happens when touch down happens in valid part of the screen. */ + @FlakyTest @Test public void testSessionStart() { mTouchHandler.onSessionStart(mTouchSession); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index d1f505baa9e3..51c258055465 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -90,6 +90,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { private DozeParameters mDozeParameters; @Mock private NextAlarmController mNextAlarmController; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private TestableKeyguardSliceProvider mProvider; private boolean mIsZenMode; @@ -97,7 +98,6 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); - mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class); mIsZenMode = false; mProvider = new TestableKeyguardSliceProvider(); mProvider.setContextAvailableCallback(context -> { }); @@ -265,6 +265,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController; mKeyguardBypassController = KeyguardSliceProviderTest.this.mKeyguardBypassController; mMediaManager = KeyguardSliceProviderTest.this.mNotificationMediaManager; + mKeyguardUpdateMonitor = KeyguardSliceProviderTest.this.mKeyguardUpdateMonitor; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt index dc7026da2194..1484c9d11ba6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt @@ -36,6 +36,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever @@ -51,6 +52,8 @@ class KeyguardMediaControllerTest : SysuiTestCase() { private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var configurationController: ConfigurationController + @Mock + private lateinit var mediaFlags: MediaFlags @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager @@ -70,13 +73,15 @@ class KeyguardMediaControllerTest : SysuiTestCase() { .thenReturn(true) whenever(mediaHost.hostView).thenReturn(hostView) hostView.layoutParams = FrameLayout.LayoutParams(100, 100) + whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) keyguardMediaController = KeyguardMediaController( mediaHost, bypassController, statusBarStateController, notificationLockscreenUserManager, context, - configurationController + configurationController, + mediaFlags ) keyguardMediaController.attachSinglePaneContainer(mediaContainerView) keyguardMediaController.useSplitShade = false @@ -150,4 +155,24 @@ class KeyguardMediaControllerTest : SysuiTestCase() { assertTrue("HostView wasn't attached to the single pane container", mediaContainerView.childCount == 1) } + + @Test + fun testNotificationLayout_collapsedPlayer() { + verify(mediaHost).expansion = MediaHostState.COLLAPSED + } + + @Test + fun testSessionLayout_expandedPlayer() { + whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) + keyguardMediaController = KeyguardMediaController( + mediaHost, + bypassController, + statusBarStateController, + notificationLockscreenUserManager, + context, + configurationController, + mediaFlags + ) + verify(mediaHost).expansion = MediaHostState.EXPANDED + } } 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..cb05d0302698 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,33 @@ 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,16 +59,17 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper @Mock - private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver + private lateinit var statusBarManager: StatusBarManager @Before fun setUp() { MockitoAnnotations.initMocks(this) + context.addMockSystemService(Context.STATUS_BAR_SERVICE, statusBarManager) mediaTttCommandLineHelper = MediaTttCommandLineHelper( commandRegistry, context, - mediaTttChipControllerReceiver, + FakeExecutor(FakeSystemClock()), ) } @@ -71,177 +81,147 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { } @Test(expected = IllegalStateException::class) - fun constructor_addReceiverCommandAlreadyRegistered() { - // Since creating the chip controller should automatically register the add command, it + fun constructor_receiverCommandAlreadyRegistered() { + // Since creating the chip controller should automatically register the receiver command, it // should throw when registering it again. - commandRegistry.registerCommand( - ADD_CHIP_COMMAND_RECEIVER_TAG - ) { EmptyCommand() } + commandRegistry.registerCommand(RECEIVER_COMMAND) { EmptyCommand() } } - @Test(expected = IllegalStateException::class) - fun constructor_removeReceiverCommandAlreadyRegistered() { - // Since creating the chip controller should automatically register the remove command, it - // should throw when registering it again. - commandRegistry.registerCommand( - REMOVE_CHIP_COMMAND_RECEIVER_TAG - ) { 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)) + fun receiver_closeToSender_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE)) - verify(mediaTttChipControllerReceiver).displayChip(any(ChipStateReceiver::class.java)) + verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER), + any() + ) } @Test - fun receiver_removeCommand_chipRemoved() { - commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_RECEIVER_TAG)) - - 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 - ) + fun receiver_farFromSender_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE)) - private fun getTransferToThisDeviceSucceededCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME + verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER), + any() ) + } - private fun getTransferFailedCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_FAILED_COMMAND_NAME - ) + private fun getSenderCommand(displayState: String): Array<String> = + arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState) - private fun getNoLongerCloseToReceiverCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME - ) + private fun getReceiverCommand(displayState: String): Array<String> = + arrayOf(RECEIVER_COMMAND, displayState) class EmptyCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 1d1265b16e63..fce495470ab0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.media.taptotransfer.receiver +import android.app.StatusBarManager import android.graphics.drawable.Icon +import android.media.MediaRoute2Info import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -24,6 +26,7 @@ import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -31,7 +34,8 @@ import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -41,11 +45,55 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager + @Mock + private lateinit var commandQueue: CommandQueue + private lateinit var commandQueueCallback: CommandQueue.Callbacks @Before fun setUp() { MockitoAnnotations.initMocks(this) - controllerReceiver = MediaTttChipControllerReceiver(context, windowManager) + controllerReceiver = MediaTttChipControllerReceiver(commandQueue, context, windowManager) + + val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) + verify(commandQueue).addCallback(callbackCaptor.capture()) + commandQueueCallback = callbackCaptor.value!! + } + + @Test + fun commandQueueCallback_closeToSender_triggersChip() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + + assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(ROUTE_NAME) + } + + @Test + fun commandQueueCallback_farFromSender_noChipShown() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + + verify(windowManager, never()).addView(any(), any()) + } + + @Test + fun commandQueueCallback_closeThenFar_chipShownThenHidden() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).removeView(viewCaptor.value) } @Test @@ -61,9 +109,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { private fun getChipView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) - Mockito.verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as ViewGroup } private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon) } + +private const val ROUTE_NAME = "Test name" +private val routeInfo = MediaRoute2Info.Builder("id", ROUTE_NAME) + .addFeature("feature") + .build() 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/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 9ca898b9dea9..612bad8483d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -18,6 +18,7 @@ package com.android.systemui.navigationbar; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.inputmethodservice.InputMethodService.IME_VISIBLE; @@ -285,20 +286,26 @@ public class NavigationBarTest extends SysuiTestCase { BACK_DISPOSITION_DEFAULT, true); // Verify IME window state will be updated in default NavBar & external NavBar state reset. - assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN, + assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN + | NAVIGATION_HINT_IME_SWITCHER_SHOWN, defaultNavBar.getNavigationIconHints()); assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0); assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); + assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN) + != 0); externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true); defaultNavBar.setImeWindowStatus( DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false); // Verify IME window state will be updated in external NavBar & default NavBar state reset. - assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN, + assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN + | NAVIGATION_HINT_IME_SWITCHER_SHOWN, externalNavBar.getNavigationIconHints()); assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0); assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); + assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN) + != 0); } @Test @@ -316,6 +323,8 @@ public class NavigationBarTest extends SysuiTestCase { BACK_DISPOSITION_DEFAULT, true); assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0); assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); + assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN) + != 0); // Verify navbar didn't alter and showing back icon when the keyguard is showing without // requesting IME insets visible. @@ -324,6 +333,8 @@ public class NavigationBarTest extends SysuiTestCase { BACK_DISPOSITION_DEFAULT, true); assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0); assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); + assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN) + != 0); // Verify navbar altered and showing back icon when the keyguard is showing and // requesting IME insets visible. @@ -333,6 +344,8 @@ public class NavigationBarTest extends SysuiTestCase { BACK_DISPOSITION_DEFAULT, true); assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0); assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); + assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN) + != 0); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 09c6d9e86a44..1eb16fd64b85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -23,11 +23,14 @@ import com.android.internal.logging.MetricsLogger import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.media.MediaFlags import com.android.systemui.media.MediaHost +import com.android.systemui.media.MediaHostState import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTileView import com.android.systemui.qs.customize.QSCustomizerController import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.util.leak.RotationUtils import org.junit.After import org.junit.Before import org.junit.Test @@ -55,6 +58,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost @Mock + private lateinit var mediaFlags: MediaFlags + @Mock private lateinit var metricsLogger: MetricsLogger private val uiEventLogger = UiEventLoggerFake() @Mock @@ -71,7 +76,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener> - private lateinit var controller: QuickQSPanelController + private lateinit var controller: TestQuickQSPanelController @Before fun setUp() { @@ -82,13 +87,16 @@ class QuickQSPanelControllerTest : SysuiTestCase() { `when`(quickQSPanel.dumpableTag).thenReturn("") `when`(quickQSPanel.resources).thenReturn(mContext.resources) `when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView) + `when`(mediaFlags.useMediaSessionLayout()).thenReturn(false) - controller = QuickQSPanelController( + controller = TestQuickQSPanelController( quickQSPanel, qsTileHost, qsCustomizerController, false, mediaHost, + true, + mediaFlags, metricsLogger, uiEventLogger, qsLogger, @@ -133,4 +141,49 @@ class QuickQSPanelControllerTest : SysuiTestCase() { verify(quickQsBrightnessController).refreshVisibility(anyBoolean()) } + + @Test + fun testMediaExpansionUpdatedWhenConfigurationChanged() { + `when`(mediaFlags.useMediaSessionLayout()).thenReturn(true) + + // times(2) because both controller and base controller are registering their listeners + verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture()) + + captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) } + verify(mediaHost).expansion = MediaHostState.EXPANDED + + // Rotate device, verify media size updated + controller.setRotation(RotationUtils.ROTATION_LANDSCAPE) + captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) } + + // times(2) because init will have set to collapsed because the flag was off + verify(mediaHost, times(2)).expansion = MediaHostState.COLLAPSED + } + + class TestQuickQSPanelController( + view: QuickQSPanel, + qsTileHost: QSTileHost, + qsCustomizerController: QSCustomizerController, + usingMediaPlayer: Boolean, + mediaHost: MediaHost, + usingCollapsedLandscapeMedia: Boolean, + mediaFlags: MediaFlags, + metricsLogger: MetricsLogger, + uiEventLogger: UiEventLoggerFake, + qsLogger: QSLogger, + dumpManager: DumpManager, + quickQSBrightnessController: QuickQSBrightnessController + ) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer, + mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger, + dumpManager, quickQSBrightnessController) { + + private var rotation = RotationUtils.ROTATION_NONE + + @Override + override fun getRotation(): Int = rotation + + fun setRotation(newRotation: Int) { + rotation = newRotation + } + } } 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/NotifPipelineChoreographerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt new file mode 100644 index 000000000000..3820b98ab632 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt @@ -0,0 +1,100 @@ +/* + * 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.statusbar.notification.collection + +import android.testing.AndroidTestingRunner +import android.view.Choreographer +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.withArgCaptor +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyLong +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class NotifPipelineChoreographerTest : SysuiTestCase() { + + val viewChoreographer: Choreographer = mock() + val timeoueSubscription: Runnable = mock() + val executor: DelayableExecutor = mock<DelayableExecutor>().also { + whenever(it.executeDelayed(any(), anyLong())).thenReturn(timeoueSubscription) + } + + val pipelineChoreographer: NotifPipelineChoreographer = NotifPipelineChoreographerModule + .provideChoreographer(viewChoreographer, executor) + + @Test + fun scheduleThenEvalFrameCallback() { + // GIVEN a registered eval listener and scheduled choreographer + var hasEvaluated = false + pipelineChoreographer.addOnEvalListener { + hasEvaluated = true + } + pipelineChoreographer.schedule() + val frameCallback: Choreographer.FrameCallback = withArgCaptor { + verify(viewChoreographer).postFrameCallback(capture()) + } + // WHEN the choreographer would invoke its callback + frameCallback.doFrame(0) + // THEN the choreographer would evaluate, and the timeoutSubscription would have been + // cancelled + assertTrue(hasEvaluated) + verify(timeoueSubscription).run() + } + + @Test + fun scheduleThenEvalTimeoutCallback() { + // GIVEN a registered eval listener and scheduled choreographer + var hasEvaluated = false + pipelineChoreographer.addOnEvalListener { + hasEvaluated = true + } + pipelineChoreographer.schedule() + val frameCallback: Choreographer.FrameCallback = withArgCaptor { + verify(viewChoreographer).postFrameCallback(capture()) + } + val runnable: Runnable = withArgCaptor { + verify(executor).executeDelayed(capture(), anyLong()) + } + // WHEN the executor would invoke its callback (indicating a timeout) + runnable.run() + // THEN the choreographer would evaluate, and the FrameCallback would have been unregistered + assertTrue(hasEvaluated) + verify(viewChoreographer).removeFrameCallback(frameCallback) + } + + @Test + fun scheduleThenCancel() { + // GIVEN a scheduled choreographer + pipelineChoreographer.schedule() + val frameCallback: Choreographer.FrameCallback = withArgCaptor { + verify(viewChoreographer).postFrameCallback(capture()) + } + // WHEN the scheduled run is cancelled + pipelineChoreographer.cancel() + // THEN both the FrameCallback is unregistered and the timeout subscription is cancelled. + verify(viewChoreographer).removeFrameCallback(frameCallback) + verify(timeoueSubscription).run() + } +}
\ No newline at end of file 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 cb248b05e4bd..f470715b92c4 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 @@ -111,6 +111,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { @Captor private ArgumentCaptor<CollectionReadyForBuildListener> mBuildListenerCaptor; + private final FakeNotifPipelineChoreographer mPipelineChoreographer = + new FakeNotifPipelineChoreographer(); private CollectionReadyForBuildListener mReadyForBuildListener; private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>(); private List<NotificationEntry> mEntrySet = new ArrayList<>(); @@ -127,11 +129,12 @@ public class ShadeListBuilderTest extends SysuiTestCase { allowTestableLooperAsMainThread(); mListBuilder = new ShadeListBuilder( - mSystemClock, + mDumpManager, + mPipelineChoreographer, mNotifPipelineFlags, + mInteractionTracker, mLogger, - mDumpManager, - mInteractionTracker + mSystemClock ); mListBuilder.setOnRenderListListener(mOnRenderListListener); @@ -567,6 +570,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN the pipeline is kicked off mReadyForBuildListener.onBuildList(singletonList(entry)); + mPipelineChoreographer.runIfScheduled(); // THEN the entry's initialization time is reset assertFalse(entry.hasFinishedInitialization()); @@ -1024,26 +1028,38 @@ public class ShadeListBuilderTest extends SysuiTestCase { clearInvocations(mOnRenderListListener); packageFilter.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); idPromoter.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); section.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); hypeComparator.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); sectionComparator.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); preRenderInvalidator.invalidateList(); + assertTrue(mPipelineChoreographer.isScheduled()); + mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); } @@ -1515,6 +1531,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN visual stability manager allows group changes again mStabilityManager.setAllowGroupChanges(true); mStabilityManager.invalidateList(); + mPipelineChoreographer.runIfScheduled(); // THEN entries are grouped verifyBuiltList( @@ -1553,6 +1570,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN section changes are allowed again mStabilityManager.setAllowSectionChanges(true); mStabilityManager.invalidateList(); + mPipelineChoreographer.runIfScheduled(); // THEN the section updates assertEquals(newSectioner, mEntrySet.get(0).getSection().getSectioner()); @@ -1773,6 +1791,30 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test + public void testMultipleInvalidationsCoalesce() { + // GIVEN a PreGroupFilter and a FinalizeFilter + NotifFilter filter1 = new PackageFilter(PACKAGE_5); + NotifFilter filter2 = new PackageFilter(PACKAGE_0); + mListBuilder.addPreGroupFilter(filter1); + mListBuilder.addFinalizeFilter(filter2); + + // WHEN both filters invalidate + filter1.invalidateList(); + filter2.invalidateList(); + + // THEN the pipeline choreographer is scheduled to evaluate, AND the pipeline hasn't + // actually run. + assertTrue(mPipelineChoreographer.isScheduled()); + verify(mOnRenderListListener, never()).onRenderList(anyList()); + + // WHEN the pipeline choreographer actually runs + mPipelineChoreographer.runIfScheduled(); + + // THEN the pipeline runs + verify(mOnRenderListListener).onRenderList(anyList()); + } + + @Test public void testIsSorted() { Comparator<Integer> intCmp = Integer::compare; assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp)); @@ -1914,6 +1956,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } mReadyForBuildListener.onBuildList(mEntrySet); + mPipelineChoreographer.runIfScheduled(); } private void verifyBuiltList(ExpectedEntry ...expectedEntries) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index a890414115dd..52189e417017 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -48,6 +48,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -273,6 +274,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { null, new FalsingManagerFake(), new FalsingCollectorFake(), + mock(FeatureFlags.class), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)), mock(ExpandableNotificationRowDragController.class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index a0e91fc77148..1305d79e4648 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -42,6 +42,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SwipeHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -71,6 +72,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { private Handler mHandler; private ExpandableNotificationRow mNotificationRow; private Runnable mFalsingCheck; + private FeatureFlags mFeatureFlags; @Rule public MockitoRule mockito = MockitoJUnit.rule(); @@ -78,9 +80,10 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { public void setUp() throws Exception { mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); + mFeatureFlags = mock(FeatureFlags.class); mSwipeHelper = spy(new NotificationSwipeHelper( mContext.getResources(), ViewConfiguration.get(mContext), - new FalsingManagerFake(), SwipeHelper.X, mCallback, mListener)); + new FalsingManagerFake(), mFeatureFlags, SwipeHelper.X, mCallback, mListener)); mView = mock(View.class); mEvent = mock(MotionEvent.class); mMenuRow = mock(NotificationMenuRowPlugin.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 01e9822e0484..7de35458a893 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -47,6 +47,9 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -99,6 +102,12 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor; @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor; + @Mock + private StatusBarUserSwitcherFeatureController mStatusBarUserSwitcherFeatureController; + @Mock + private StatusBarUserSwitcherController mStatusBarUserSwitcherController; + @Mock + private StatusBarUserInfoTracker mStatusBarUserInfoTracker; private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider; private KeyguardStatusBarView mKeyguardStatusBarView; @@ -117,7 +126,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { .inflate(R.layout.keyguard_status_bar, null)); }); - mController = new KeyguardStatusBarViewController( + mController = createController(); + } + + private KeyguardStatusBarViewController createController() { + return new KeyguardStatusBarViewController( mKeyguardStatusBarView, mCarrierTextController, mConfigurationController, @@ -134,7 +147,10 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mBiometricUnlockController, mStatusBarStateController, mStatusBarContentInsetsProvider, - mUserManager + mUserManager, + mStatusBarUserSwitcherFeatureController, + mStatusBarUserSwitcherController, + mStatusBarUserInfoTracker ); } @@ -356,6 +372,32 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE); } + @Test + public void testNewUserSwitcherDisablesAvatar_newUiOn() { + // GIVEN the status bar user switcher chip is enabled + when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled()) + .thenReturn(true); + + // WHEN the controller is created + mController = createController(); + + // THEN keyguard status bar view avatar is disabled + assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isFalse(); + } + + @Test + public void testNewUserSwitcherDisablesAvatar_newUiOff() { + // GIVEN the status bar user switcher chip is disabled + when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled()) + .thenReturn(false); + + // WHEN the controller is created + mController = createController(); + + // THEN keyguard status bar view avatar is enabled + assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isTrue(); + } + private void updateStateToNotKeyguard() { updateStatusBarState(SHADE); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index dee88dbfe29d..7347565408f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -99,7 +98,6 @@ import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.idle.IdleHostViewController; @@ -394,7 +392,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mConfiguration.orientation = ORIENTATION_PORTRAIT; when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); mDisplayMetrics.density = 100; - when(mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHADE_DRAG)).thenReturn(true); + when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index c65a6b6cde1a..589116184710 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.config.UnfoldTransitionConfig @@ -60,6 +61,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider @Mock private lateinit var configurationController: ConfigurationController + @Mock + private lateinit var userSwitcherController: StatusBarUserSwitcherController private lateinit var view: PhoneStatusBarView private lateinit var controller: PhoneStatusBarViewController @@ -187,6 +190,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { return PhoneStatusBarViewController.Factory( Optional.of(sysuiUnfoldComponent), Optional.of(progressProvider), + userSwitcherController, configurationController ).create(view, touchEventHandler).also { it.init() 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/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index 6938e25ea9af..c9903ea19868 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -43,6 +43,7 @@ import android.hardware.camera2.extension.IImageProcessorImpl; import android.hardware.camera2.extension.IInitializeSessionCallback; import android.hardware.camera2.extension.IPreviewExtenderImpl; import android.hardware.camera2.extension.IPreviewImageProcessorImpl; +import android.hardware.camera2.extension.IProcessResultImpl; import android.hardware.camera2.extension.IRequestCallback; import android.hardware.camera2.extension.IRequestProcessorImpl; import android.hardware.camera2.extension.IRequestUpdateProcessorImpl; @@ -90,6 +91,7 @@ import androidx.camera.extensions.impl.NightPreviewExtenderImpl; import androidx.camera.extensions.impl.PreviewExtenderImpl; import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType; import androidx.camera.extensions.impl.PreviewImageProcessorImpl; +import androidx.camera.extensions.impl.ProcessResultImpl; import androidx.camera.extensions.impl.RequestUpdateProcessorImpl; import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl; @@ -124,14 +126,17 @@ public class CameraExtensionsProxyService extends Service { private static final String LATEST_VERSION = "1.2.0"; private static final String NON_INIT_VERSION_PREFIX = "1.0"; private static final String ADVANCED_VERSION_PREFIX = "1.2"; - private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX, - "1.1", NON_INIT_VERSION_PREFIX}; + private static final String RESULTS_VERSION_PREFIX = "1.3"; + private static final String[] SUPPORTED_VERSION_PREFIXES = {RESULTS_VERSION_PREFIX, + ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX}; private static final boolean EXTENSIONS_PRESENT = checkForExtensions(); private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ? (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null; private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI(); private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT && (!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX)); + private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT && + (EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX)); private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>(); private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>(); @@ -1242,7 +1247,7 @@ public class CameraExtensionsProxyService extends Service { } if (processor != null) { - return new PreviewImageProcessorImplStub(processor); + return new PreviewImageProcessorImplStub(processor, mCameraId); } return null; @@ -1332,7 +1337,7 @@ public class CameraExtensionsProxyService extends Service { public ICaptureProcessorImpl getCaptureProcessor() { CaptureProcessorImpl captureProcessor = mImageExtender.getCaptureProcessor(); if (captureProcessor != null) { - return new CaptureProcessorImplStub(captureProcessor); + return new CaptureProcessorImplStub(captureProcessor, mCameraId); } return null; @@ -1390,13 +1395,97 @@ public class CameraExtensionsProxyService extends Service { return null; } + + @Override + public CameraMetadataNative getAvailableCaptureRequestKeys() { + if (RESULT_API_SUPPORTED) { + List<CaptureRequest.Key> supportedCaptureKeys = + mImageExtender.getAvailableCaptureRequestKeys(); + + if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) { + CameraMetadataNative ret = new CameraMetadataNative(); + long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ? + mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE; + ret.setVendorId(vendorId); + int requestKeyTags [] = new int[supportedCaptureKeys.size()]; + int i = 0; + for (CaptureRequest.Key key : supportedCaptureKeys) { + requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId); + } + ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags); + + return ret; + } + } + + return null; + } + + @Override + public CameraMetadataNative getAvailableCaptureResultKeys() { + if (RESULT_API_SUPPORTED) { + List<CaptureResult.Key> supportedResultKeys = + mImageExtender.getAvailableCaptureResultKeys(); + + if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) { + CameraMetadataNative ret = new CameraMetadataNative(); + long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ? + mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE; + ret.setVendorId(vendorId); + int resultKeyTags [] = new int[supportedResultKeys.size()]; + int i = 0; + for (CaptureResult.Key key : supportedResultKeys) { + resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId); + } + ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags); + + return ret; + } + } + + return null; + } + } + + private class ProcessResultCallback implements ProcessResultImpl { + private final IProcessResultImpl mProcessResult; + private final String mCameraId; + + private ProcessResultCallback(IProcessResultImpl processResult, String cameraId) { + mProcessResult = processResult; + mCameraId = cameraId; + } + + @Override + public void onCaptureCompleted(long shutterTimestamp, + List<Pair<CaptureResult.Key, Object>> result) { + if (result == null) { + Log.e(TAG, "Invalid capture result received!"); + } + + CameraMetadataNative captureResults = new CameraMetadataNative(); + if (mMetadataVendorIdMap.containsKey(mCameraId)) { + captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId)); + } + for (Pair<CaptureResult.Key, Object> pair : result) { + captureResults.set(pair.first, pair.second); + } + + try { + mProcessResult.onCaptureCompleted(shutterTimestamp, captureResults); + } catch (RemoteException e) { + Log.e(TAG, "Remote client doesn't respond to capture results!"); + } + } } private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub { private final CaptureProcessorImpl mCaptureProcessor; + private final String mCameraId; - public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor) { + public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor, String cameraId) { mCaptureProcessor = captureProcessor; + mCameraId = cameraId; } @Override @@ -1415,7 +1504,7 @@ public class CameraExtensionsProxyService extends Service { } @Override - public void process(List<CaptureBundle> captureList) { + public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback) { HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>(); for (CaptureBundle captureBundle : captureList) { captureMap.put(captureBundle.stage, new Pair<> ( @@ -1424,7 +1513,14 @@ public class CameraExtensionsProxyService extends Service { captureBundle.sequenceId))); } if (!captureMap.isEmpty()) { - mCaptureProcessor.process(captureMap); + if ((resultCallback != null) && (RESULT_API_SUPPORTED)) { + mCaptureProcessor.process(captureMap, new ProcessResultCallback(resultCallback, + mCameraId), null /*executor*/); + } else if (resultCallback == null) { + mCaptureProcessor.process(captureMap); + } else { + Log.e(TAG, "Process requests with capture results are not supported!"); + } } else { Log.e(TAG, "Process request with absent capture stages!"); } @@ -1433,9 +1529,11 @@ public class CameraExtensionsProxyService extends Service { private class PreviewImageProcessorImplStub extends IPreviewImageProcessorImpl.Stub { private final PreviewImageProcessorImpl mProcessor; + private final String mCameraId; - public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor) { + public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor, String cameraId) { mProcessor = processor; + mCameraId = cameraId; } @Override @@ -1455,9 +1553,17 @@ public class CameraExtensionsProxyService extends Service { @Override public void process(android.hardware.camera2.extension.ParcelImage image, - CameraMetadataNative result, int sequenceId) { - mProcessor.process(new ExtensionImage(image), - new TotalCaptureResult(result, sequenceId)); + CameraMetadataNative result, int sequenceId, IProcessResultImpl resultCallback) { + if ((resultCallback != null) && RESULT_API_SUPPORTED) { + mProcessor.process(new ExtensionImage(image), + new TotalCaptureResult(result, sequenceId), + new ProcessResultCallback(resultCallback, mCameraId), null /*executor*/); + } else if (resultCallback == null) { + mProcessor.process(new ExtensionImage(image), + new TotalCaptureResult(result, sequenceId)); + } else { + + } } } diff --git a/proto/src/camera.proto b/proto/src/camera.proto index 0338b93c8842..2d62f32d3941 100644 --- a/proto/src/camera.proto +++ b/proto/src/camera.proto @@ -65,4 +65,6 @@ message CameraStreamProto { // The dynamic range profile of the stream optional int32 dynamic_range_profile = 14; + // The stream use case + optional int32 stream_use_case = 15; } diff --git a/services/Android.bp b/services/Android.bp index 654e8d05d713..4e7e3c1ed4d1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -187,10 +187,6 @@ cc_library_shared { name: "libandroid_servers", defaults: ["libservices.core-libs"], whole_static_libs: ["libservices.core"], - required: [ - // TODO: remove after NetworkStatsService is moved to the mainline module. - "libcom_android_net_module_util_jni", - ], } platform_compat_config { 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/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index c3ab2a79e288..cef0e83f6006 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -35,10 +35,10 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainRunna import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED; import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage; import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice; -import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId; import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage; import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageCompanionDevice; import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr; +import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId; import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation; import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation; @@ -459,7 +459,7 @@ public class CompanionDeviceManagerService extends SystemService @Override public List<AssociationInfo> getAllAssociationsForUser(int userId) throws RemoteException { - enforceCallerCanInteractWithUserId(getContext(), userId); + enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser"); return mAssociationStore.getAssociationsForUser(userId); @@ -468,7 +468,7 @@ public class CompanionDeviceManagerService extends SystemService @Override public void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId) { - enforceCallerCanInteractWithUserId(getContext(), userId); + enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); enforceCallerCanManageCompanionDevice(getContext(), "addOnAssociationsChangedListener"); @@ -478,7 +478,7 @@ public class CompanionDeviceManagerService extends SystemService @Override public void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId) { - enforceCallerCanInteractWithUserId(getContext(), userId); + enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId); enforceCallerCanManageCompanionDevice( getContext(), "removeOnAssociationsChangedListener"); diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java index 7ebe33ea66b1..0e593e14a037 100644 --- a/services/companion/java/com/android/server/companion/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java @@ -114,6 +114,12 @@ final class PermissionsUtils { context.enforceCallingPermission(INTERACT_ACROSS_USERS, null); } + static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) { + if (getCallingUid() == SYSTEM_UID) return; + + enforceCallerCanInteractWithUserId(context, userId); + } + static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) { final int callingUid = getCallingUid(); if (callingUid == SYSTEM_UID) return true; 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/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index 48f5b51a8404..a0575cf6bab9 100644 --- a/services/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -484,7 +484,9 @@ public class BootReceiver extends BroadcastReceiver { HashMap<String, Long> timestamps = readTimestamps(); try { if (proto) { - db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0); + if (recordFileTimestamp(tombstone, timestamps)) { + db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0); + } } else { final String headers = getBootHeadersToLogAndUpdate(); addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE, @@ -526,16 +528,10 @@ public class BootReceiver extends BroadcastReceiver { if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled File file = new File(filename); - long fileTime = file.lastModified(); - if (fileTime <= 0) return; // File does not exist - - if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) { - return; // Already logged this particular file + if (!recordFileTimestamp(file, timestamps)) { + return; } - timestamps.put(filename, fileTime); - - String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED); String text = headers + fileContents + footers; // Create an additional report for system server native crashes, with a special tag. @@ -548,6 +544,19 @@ public class BootReceiver extends BroadcastReceiver { addTextToDropBox(db, tag, text, filename, maxSize); } + private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) { + final long fileTime = file.lastModified(); + if (fileTime <= 0) return false; // File does not exist + + final String filename = file.getPath(); + if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) { + return false; // Already logged this particular file + } + + timestamps.put(filename, fileTime); + return true; + } + private static void addTextToDropBox(DropBoxManager db, String tag, String text, String filename, int maxSize) { Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index ac20a08c1e30..8b48d0f39aaa 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -16,12 +16,17 @@ package com.android.server; +import static com.android.server.Watchdog.HandlerCheckerAndTimeout.withCustomTimeout; +import static com.android.server.Watchdog.HandlerCheckerAndTimeout.withDefaultTimeout; + import android.app.IActivityController; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.hidl.manager.V1_0.IServiceManager; +import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Debug; @@ -35,12 +40,15 @@ import android.os.ServiceDebugInfo; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; import android.sysprop.WatchdogProperties; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.os.BackgroundThread; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.ZygoteConnectionConstants; import com.android.internal.util.FrameworkStatsLog; @@ -61,10 +69,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; -/** This class calls its monitor every minute. Killing this process if they don't return **/ +/** + * This class calls its monitor every minute. Killing this process if they don't return + **/ public class Watchdog { static final String TAG = "Watchdog"; @@ -79,9 +90,7 @@ public class Watchdog { // can trigger the watchdog. // Note 2: The debug value is already below the wait time in ZygoteConnection. Wrapped // applications may not work with a debug build. CTS will fail. - private static final long DEFAULT_TIMEOUT = - (DB ? 10 * 1000 : 60 * 1000) * Build.HW_TIMEOUT_MULTIPLIER; - private static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2; + private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000; // These are temporally ordered: larger values as lateness increases private static final int COMPLETED = 0; @@ -156,34 +165,71 @@ public class Watchdog { private final Object mLock = new Object(); /* This handler will be used to post message back onto the main thread */ - private final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>(); + private final ArrayList<HandlerCheckerAndTimeout> mHandlerCheckers = new ArrayList<>(); private final HandlerChecker mMonitorChecker; private ActivityManagerService mActivity; - private IActivityController mController; private boolean mAllowRestart = true; + // We start with DEFAULT_TIMEOUT. This will then be update with the timeout values from Settings + // once the settings provider is initialized. + private volatile long mWatchdogTimeoutMillis = DEFAULT_TIMEOUT; private final List<Integer> mInterestingJavaPids = new ArrayList<>(); - private final TraceErrorLogger mTraceErrorLogger; + /** Holds a checker and its timeout. */ + static final class HandlerCheckerAndTimeout { + private final HandlerChecker mHandler; + private final Optional<Long> mCustomTimeoutMillis; + + private HandlerCheckerAndTimeout(HandlerChecker checker, Optional<Long> timeoutMillis) { + this.mHandler = checker; + this.mCustomTimeoutMillis = timeoutMillis; + } + + HandlerChecker checker() { + return mHandler; + } + + /** Returns the timeout. */ + Optional<Long> customTimeoutMillis() { + return mCustomTimeoutMillis; + } + + /** + * Creates a checker with the default timeout. The timeout will use the default value which + * is configurable server-side. + */ + static HandlerCheckerAndTimeout withDefaultTimeout(HandlerChecker checker) { + return new HandlerCheckerAndTimeout(checker, Optional.empty()); + } + + /** + * Creates a checker with a custom timeout. The timeout overrides the default value and will + * always be used. + */ + static HandlerCheckerAndTimeout withCustomTimeout( + HandlerChecker checker, long timeoutMillis) { + return new HandlerCheckerAndTimeout(checker, Optional.of(timeoutMillis)); + } + } + /** * Used for checking status of handle threads and scheduling monitor callbacks. */ public final class HandlerChecker implements Runnable { private final Handler mHandler; private final String mName; - private final long mWaitMax; private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>(); private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>(); + private long mWaitMax; private boolean mCompleted; private Monitor mCurrentMonitor; private long mStartTime; private int mPauseCount; - HandlerChecker(Handler handler, String name, long waitMaxMillis) { + HandlerChecker(Handler handler, String name) { mHandler = handler; mName = name; - mWaitMax = waitMaxMillis; mCompleted = true; } @@ -193,7 +239,13 @@ public class Watchdog { mMonitorQueue.add(monitor); } - public void scheduleCheckLocked() { + /** + * Schedules a run on the handler thread. + * + * @param handlerCheckerTimeoutMillis the timeout to use for this run + */ + public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) { + mWaitMax = handlerCheckerTimeoutMillis; if (mCompleted) { // Safe to update monitors in queue, Handler is not in the middle of work mMonitors.addAll(mMonitorQueue); @@ -222,10 +274,6 @@ public class Watchdog { mHandler.postAtFrontOfQueue(this); } - boolean isOverdueLocked() { - return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax); - } - public int getCompletionStateLocked() { if (mCompleted) { return COMPLETED; @@ -336,36 +384,37 @@ public class Watchdog { private Watchdog() { mThread = new Thread(this::run, "watchdog"); + // Initialize handler checkers for each common thread we want to check. Note // that we are not currently checking the background thread, since it can // potentially hold longer running operations with no guarantees about the timeliness // of operations there. - + // // The shared foreground thread is the main checker. It is where we // will also dispatch monitor checks and do other work. mMonitorChecker = new HandlerChecker(FgThread.getHandler(), - "foreground thread", DEFAULT_TIMEOUT); - mHandlerCheckers.add(mMonitorChecker); + "foreground thread"); + mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker)); // Add checker for main thread. We only do a quick check since there // can be UI running on the thread. - mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()), - "main thread", DEFAULT_TIMEOUT)); + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread"))); // Add checker for shared UI thread. - mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), - "ui thread", DEFAULT_TIMEOUT)); + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(UiThread.getHandler(), "ui thread"))); // And also check IO thread. - mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), - "i/o thread", DEFAULT_TIMEOUT)); + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(IoThread.getHandler(), "i/o thread"))); // And the display thread. - mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(), - "display thread", DEFAULT_TIMEOUT)); + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(DisplayThread.getHandler(), "display thread"))); // And the animation thread. - mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(), - "animation thread", DEFAULT_TIMEOUT)); + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(AnimationThread.getHandler(), "animation thread"))); // And the surface animation thread. - mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(), - "surface animation thread", DEFAULT_TIMEOUT)); - + mHandlerCheckers.add(withDefaultTimeout( + new HandlerChecker(SurfaceAnimationThread.getHandler(), + "surface animation thread"))); // Initialize monitor for Binder threads. addMonitor(new BinderThreadMonitor()); @@ -397,6 +446,62 @@ public class Watchdog { android.Manifest.permission.REBOOT, null); } + private static class SettingsObserver extends ContentObserver { + private final Uri mUri = Settings.Global.getUriFor(Settings.Global.WATCHDOG_TIMEOUT_MILLIS); + private final Context mContext; + private final Watchdog mWatchdog; + + SettingsObserver(Context context, Watchdog watchdog) { + super(BackgroundThread.getHandler()); + mContext = context; + mWatchdog = watchdog; + // Always kick once to ensure that we match current state + onChange(); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + if (mUri.equals(uri)) { + onChange(); + } + } + + public void onChange() { + try { + mWatchdog.updateWatchdogTimeout(Settings.Global.getLong( + mContext.getContentResolver(), + Settings.Global.WATCHDOG_TIMEOUT_MILLIS, DEFAULT_TIMEOUT)); + } catch (RuntimeException e) { + Slog.e(TAG, "Exception while reading settings " + e.getMessage(), e); + } + } + } + + /** + * Register an observer to listen to settings. + * + * It needs to be called after the settings service is initialized. + */ + public void registerSettingsObserver(Context context) { + context.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.WATCHDOG_TIMEOUT_MILLIS), + false, + new SettingsObserver(context, this), + UserHandle.USER_SYSTEM); + } + + /** + * Updates watchdog timeout values. + */ + void updateWatchdogTimeout(long timeoutMillis) { + // See the notes on DEFAULT_TIMEOUT. + if (!DB && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) { + timeoutMillis = ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS + 1; + } + mWatchdogTimeoutMillis = timeoutMillis; + Slog.i(TAG, "Watchdog timeout updated to " + mWatchdogTimeoutMillis + " millis"); + } + private static boolean isInterestingJavaProcess(String processName) { return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName) || processName.equals("com.android.phone"); @@ -446,13 +551,17 @@ public class Watchdog { } public void addThread(Handler thread) { - addThread(thread, DEFAULT_TIMEOUT); + synchronized (mLock) { + final String name = thread.getLooper().getThread().getName(); + mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name))); + } } public void addThread(Handler thread, long timeoutMillis) { synchronized (mLock) { final String name = thread.getLooper().getThread().getName(); - mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis)); + mHandlerCheckers.add( + withCustomTimeout(new HandlerChecker(thread, name), timeoutMillis)); } } @@ -471,9 +580,10 @@ public class Watchdog { */ public void pauseWatchingCurrentThread(String reason) { synchronized (mLock) { - for (HandlerChecker hc : mHandlerCheckers) { - if (Thread.currentThread().equals(hc.getThread())) { - hc.pauseLocked(reason); + for (HandlerCheckerAndTimeout hc : mHandlerCheckers) { + HandlerChecker checker = hc.checker(); + if (Thread.currentThread().equals(checker.getThread())) { + checker.pauseLocked(reason); } } } @@ -493,9 +603,10 @@ public class Watchdog { */ public void resumeWatchingCurrentThread(String reason) { synchronized (mLock) { - for (HandlerChecker hc : mHandlerCheckers) { - if (Thread.currentThread().equals(hc.getThread())) { - hc.resumeLocked(reason); + for (HandlerCheckerAndTimeout hc : mHandlerCheckers) { + HandlerChecker checker = hc.checker(); + if (Thread.currentThread().equals(checker.getThread())) { + checker.resumeLocked(reason); } } } @@ -516,17 +627,17 @@ public class Watchdog { private int evaluateCheckerCompletionLocked() { int state = COMPLETED; for (int i=0; i<mHandlerCheckers.size(); i++) { - HandlerChecker hc = mHandlerCheckers.get(i); + HandlerChecker hc = mHandlerCheckers.get(i).checker(); state = Math.max(state, hc.getCompletionStateLocked()); } return state; } - private ArrayList<HandlerChecker> getBlockedCheckersLocked() { + private ArrayList<HandlerChecker> getCheckersWithStateLocked(int completionState) { ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>(); for (int i=0; i<mHandlerCheckers.size(); i++) { - HandlerChecker hc = mHandlerCheckers.get(i); - if (hc.isOverdueLocked()) { + HandlerChecker hc = mHandlerCheckers.get(i).checker(); + if (hc.getCompletionStateLocked() == completionState) { checkers.add(hc); } } @@ -595,20 +706,28 @@ public class Watchdog { private void run() { boolean waitedHalf = false; + while (true) { List<HandlerChecker> blockedCheckers = Collections.emptyList(); String subject = ""; boolean allowRestart = true; int debuggerWasConnected = 0; boolean doWaitedHalfDump = false; + // The value of mWatchdogTimeoutMillis might change while we are executing the loop. + // We store the current value to use a consistent value for all handlers. + final long watchdogTimeoutMillis = mWatchdogTimeoutMillis; + final long checkIntervalMillis = watchdogTimeoutMillis / 2; final ArrayList<Integer> pids; synchronized (mLock) { - long timeout = CHECK_INTERVAL; + long timeout = checkIntervalMillis; // Make sure we (re)spin the checkers that have become idle within // this wait-and-check interval for (int i=0; i<mHandlerCheckers.size(); i++) { - HandlerChecker hc = mHandlerCheckers.get(i); - hc.scheduleCheckLocked(); + HandlerCheckerAndTimeout hc = mHandlerCheckers.get(i); + // We pick the watchdog to apply every time we reschedule the checkers. The + // default timeout might have changed since the last run. + hc.checker().scheduleCheckLocked(hc.customTimeoutMillis() + .orElse(watchdogTimeoutMillis * Build.HW_TIMEOUT_MULTIPLIER)); } if (debuggerWasConnected > 0) { @@ -633,7 +752,7 @@ public class Watchdog { if (Debug.isDebuggerConnected()) { debuggerWasConnected = 2; } - timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start); + timeout = checkIntervalMillis - (SystemClock.uptimeMillis() - start); } final int waitState = evaluateCheckerCompletionLocked(); @@ -649,6 +768,8 @@ public class Watchdog { Slog.i(TAG, "WAITED_HALF"); waitedHalf = true; // We've waited half, but we'd need to do the stack trace dump w/o the lock. + blockedCheckers = getCheckersWithStateLocked(WAITED_HALF); + subject = describeCheckersLocked(blockedCheckers); pids = new ArrayList<>(mInterestingJavaPids); doWaitedHalfDump = true; } else { @@ -656,90 +777,27 @@ public class Watchdog { } } else { // something is overdue! - blockedCheckers = getBlockedCheckersLocked(); + blockedCheckers = getCheckersWithStateLocked(OVERDUE); subject = describeCheckersLocked(blockedCheckers); allowRestart = mAllowRestart; pids = new ArrayList<>(mInterestingJavaPids); } } // END synchronized (mLock) - if (doWaitedHalfDump) { - // Get critical event log before logging the half watchdog so that it doesn't - // occur in the log. - String criticalEvents = - CriticalEventLog.getInstance().logLinesForSystemServerTraceFile(); - CriticalEventLog.getInstance().logHalfWatchdog(subject); - - // We've waited half the deadlock-detection interval. Pull a stack - // trace and wait another half. - ActivityManagerService.dumpStackTraces(pids, null, null, - getInterestingNativePids(), null, subject, criticalEvents); - continue; - } - // If we got here, that means that the system is most likely hung. + // // First collect stack traces from all threads of the system process. - // Then kill this process so that the system will restart. - EventLog.writeEvent(EventLogTags.WATCHDOG, subject); + // + // Then, if we reached the full timeout, kill this process so that the system will + // restart. If we reached half of the timeout, just log some information and continue. + logWatchog(doWaitedHalfDump, subject, pids); - final UUID errorId = mTraceErrorLogger.generateErrorId(); - if (mTraceErrorLogger.isAddErrorIdEnabled()) { - mTraceErrorLogger.addErrorIdToTrace("system_server", errorId); - mTraceErrorLogger.addSubjectToTrace(subject, errorId); + if (doWaitedHalfDump) { + // We have waited for only half of the timeout, we continue to wait for the duration + // of the full timeout before killing the process. + continue; } - // Log the atom as early as possible since it is used as a mechanism to trigger - // Perfetto. Ideally, the Perfetto trace capture should happen as close to the - // point in time when the Watchdog happens as possible. - FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject); - - // Get critical event log before logging the watchdog so that it doesn't occur in the - // log. - String criticalEvents = - CriticalEventLog.getInstance().logLinesForSystemServerTraceFile(); - CriticalEventLog.getInstance().logWatchdog(subject, errorId); - - long anrTime = SystemClock.uptimeMillis(); - StringBuilder report = new StringBuilder(); - report.append(MemoryPressureUtil.currentPsiState()); - ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false); - StringWriter tracesFileException = new StringWriter(); - final File stack = ActivityManagerService.dumpStackTraces( - pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(), - tracesFileException, subject, criticalEvents); - - // Give some extra time to make sure the stack traces get written. - // The system's been hanging for a minute, another second or two won't hurt much. - SystemClock.sleep(5000); - - processCpuTracker.update(); - report.append(processCpuTracker.printCurrentState(anrTime)); - report.append(tracesFileException.getBuffer()); - - // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log - doSysRq('w'); - doSysRq('l'); - - // Try to add the error to the dropbox, but assuming that the ActivityManager - // itself may be deadlocked. (which has happened, causing this statement to - // deadlock and the watchdog as a whole to be ineffective) - Thread dropboxThread = new Thread("watchdogWriteToDropbox") { - public void run() { - // If a watched thread hangs before init() is called, we don't have a - // valid mActivity. So we can't log the error to dropbox. - if (mActivity != null) { - mActivity.addErrorToDropBox( - "watchdog", null, "system_server", null, null, null, - null, report.toString(), stack, null, null, null, - errorId); - } - } - }; - dropboxThread.start(); - try { - dropboxThread.join(2000); // wait up to 2 seconds for it to return. - } catch (InterruptedException ignored) {} - IActivityController controller; synchronized (mLock) { controller = mController; @@ -785,6 +843,74 @@ public class Watchdog { } } + private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) { + // Get critical event log before logging the half watchdog so that it doesn't + // occur in the log. + String criticalEvents = + CriticalEventLog.getInstance().logLinesForSystemServerTraceFile(); + final UUID errorId = mTraceErrorLogger.generateErrorId(); + if (mTraceErrorLogger.isAddErrorIdEnabled()) { + mTraceErrorLogger.addErrorIdToTrace("system_server", errorId); + mTraceErrorLogger.addSubjectToTrace(subject, errorId); + } + + final String dropboxTag; + if (halfWatchdog) { + dropboxTag = "pre_watchdog"; + CriticalEventLog.getInstance().logHalfWatchdog(subject); + } else { + dropboxTag = "watchdog"; + CriticalEventLog.getInstance().logWatchdog(subject, errorId); + EventLog.writeEvent(EventLogTags.WATCHDOG, subject); + // Log the atom as early as possible since it is used as a mechanism to trigger + // Perfetto. Ideally, the Perfetto trace capture should happen as close to the + // point in time when the Watchdog happens as possible. + FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject); + } + + long anrTime = SystemClock.uptimeMillis(); + StringBuilder report = new StringBuilder(); + report.append(MemoryPressureUtil.currentPsiState()); + ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false); + StringWriter tracesFileException = new StringWriter(); + final File stack = ActivityManagerService.dumpStackTraces( + pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(), + tracesFileException, subject, criticalEvents); + // Give some extra time to make sure the stack traces get written. + // The system's been hanging for a whlie, another second or two won't hurt much. + SystemClock.sleep(5000); + processCpuTracker.update(); + report.append(processCpuTracker.printCurrentState(anrTime)); + report.append(tracesFileException.getBuffer()); + + if (!halfWatchdog) { + // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the + // kernel log + doSysRq('w'); + doSysRq('l'); + } + + // Try to add the error to the dropbox, but assuming that the ActivityManager + // itself may be deadlocked. (which has happened, causing this statement to + // deadlock and the watchdog as a whole to be ineffective) + Thread dropboxThread = new Thread("watchdogWriteToDropbox") { + public void run() { + // If a watched thread hangs before init() is called, we don't have a + // valid mActivity. So we can't log the error to dropbox. + if (mActivity != null) { + mActivity.addErrorToDropBox( + dropboxTag, null, "system_server", null, null, null, + null, report.toString(), stack, null, null, null, + errorId); + } + } + }; + dropboxThread.start(); + try { + dropboxThread.join(2000); // wait up to 2 seconds for it to return. + } catch (InterruptedException ignored) { } + } + private void doSysRq(char c) { try { FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger"); diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 2845fbfc6ebf..6667d1b45027 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -311,14 +311,15 @@ public class AdbService extends IAdbManager.Stub { } /** - * @return true if the device supports secure ADB over Wi-Fi. + * @return true if the device supports secure ADB over Wi-Fi or Ethernet. * @hide */ @Override public boolean isAdbWifiSupported() { mContext.enforceCallingPermission( android.Manifest.permission.MANAGE_DEBUGGING, "AdbService"); - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) || + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_ETHERNET); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index bcb1be39d1b1..940ad73eee98 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -123,6 +123,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE = "kill_bg_restricted_cached_idle"; static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME = "kill_bg_restricted_cached_idle_settle_time"; + static final String KEY_ENABLE_COMPONENT_ALIAS = "enable_experimental_component_alias"; static final String KEY_COMPONENT_ALIAS_OVERRIDES = "component_alias_overrides"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; @@ -199,6 +200,7 @@ final class ActivityManagerConstants extends ContentObserver { * Whether or not to enable the extra delays to service restarts on memory pressure. */ private static final boolean DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = true; + private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false; private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = ""; // Flag stored in the DeviceConfig API. @@ -595,6 +597,12 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE; /** + * Whether to enable "component alias" experimental feature. This can only be enabled + * on userdebug or eng builds. + */ + volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS; + + /** * Defines component aliases. Format * ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )* */ @@ -831,6 +839,7 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE: updateEnableExtraServiceRestartDelayOnMemPressure(); break; + case KEY_ENABLE_COMPONENT_ALIAS: case KEY_COMPONENT_ALIAS_OVERRIDES: updateComponentAliases(); break; @@ -1269,11 +1278,15 @@ final class ActivityManagerConstants extends ContentObserver { } private void updateComponentAliases() { + mEnableComponentAlias = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_ENABLE_COMPONENT_ALIAS, + DEFAULT_ENABLE_COMPONENT_ALIAS); mComponentAliasOverrides = DeviceConfig.getString( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPONENT_ALIAS_OVERRIDES, DEFAULT_COMPONENT_ALIAS_OVERRIDES); - mService.mComponentAliasResolver.update(mComponentAliasOverrides); + mService.mComponentAliasResolver.update(mEnableComponentAlias, mComponentAliasOverrides); } private void updateProcessKillTimeout() { @@ -1512,6 +1525,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mPushMessagingOverQuotaBehavior); pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT); pw.print("="); pw.println(mFgsAllowOptOut); + pw.print(" "); pw.print(KEY_ENABLE_COMPONENT_ALIAS); + pw.print("="); pw.println(mEnableComponentAlias); pw.print(" "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES); pw.print("="); pw.println(mComponentAliasOverrides); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 17fc4dd4bdf4..0310b0fc4469 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; @@ -8066,7 +8066,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Load the component aliases. t.traceBegin("componentAlias"); - mComponentAliasResolver.onSystemReady(mConstants.mComponentAliasOverrides); + mComponentAliasResolver.onSystemReady(mConstants.mEnableComponentAlias, + mConstants.mComponentAliasOverrides); t.traceEnd(); // componentAlias t.traceEnd(); // PhaseActivityManagerReady @@ -17905,6 +17906,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/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/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java index 23553a7cf12a..aef7a6c77b6f 100644 --- a/services/core/java/com/android/server/am/ComponentAliasResolver.java +++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; +import android.os.Build; import android.os.ServiceManager; import android.os.UserHandle; import android.text.TextUtils; @@ -56,6 +57,8 @@ import java.util.function.Supplier; * "quick & dirty". For example, to define aliases, we use a regular intent filter and meta-data * in the manifest, instead of adding proper tags/attributes to AndroidManifest.xml. * + * Because it's an experimental feature, it can't be enabled on a user build. + * * Also, for now, aliases can be defined across any packages, but in the final version, there'll * be restrictions: * - We probably should only allow either privileged or preinstalled apps. @@ -78,6 +81,9 @@ public class ComponentAliasResolver { private final Context mContext; @GuardedBy("mLock") + private boolean mEnabledByDeviceConfig; + + @GuardedBy("mLock") private boolean mEnabled; @GuardedBy("mLock") @@ -141,7 +147,7 @@ public class ComponentAliasResolver { /** * Call this on systemRead(). */ - public void onSystemReady(String overrides) { + public void onSystemReady(boolean enabledByDeviceConfig, String overrides) { synchronized (mLock) { mPlatformCompat = (PlatformCompat) ServiceManager.getService( Context.PLATFORM_COMPAT_SERVICE); @@ -149,19 +155,21 @@ public class ComponentAliasResolver { mCompatChangeListener); } if (DEBUG) Slog.d(TAG, "Compat listener set."); - update(overrides); + update(enabledByDeviceConfig, overrides); } /** * (Re-)loads aliases from <meta-data> and the device config override. */ - public void update(String overrides) { + public void update(boolean enabledByDeviceConfig, String overrides) { synchronized (mLock) { if (mPlatformCompat == null) { return; // System not ready. } - final boolean enabled = mPlatformCompat.isChangeEnabledByPackageName( - USE_EXPERIMENTAL_COMPONENT_ALIAS, "android", UserHandle.USER_SYSTEM); + final boolean enabled = Build.isDebuggable() + && (enabledByDeviceConfig + || mPlatformCompat.isChangeEnabledByPackageName( + USE_EXPERIMENTAL_COMPONENT_ALIAS, "android", UserHandle.USER_SYSTEM)); if (enabled != mEnabled) { Slog.i(TAG, (enabled ? "Enabling" : "Disabling") + " component aliases..."); if (enabled) { @@ -172,6 +180,7 @@ public class ComponentAliasResolver { } } mEnabled = enabled; + mEnabledByDeviceConfig = enabledByDeviceConfig; mOverrideString = overrides; if (mEnabled) { @@ -184,7 +193,7 @@ public class ComponentAliasResolver { private void refresh() { synchronized (mLock) { - update(mOverrideString); + update(mEnabledByDeviceConfig, mOverrideString); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index e4c0846d090f..481b6dddc31b 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2034,6 +2034,11 @@ class UserController implements Handler.Callback { } } + /** + * Tell WindowManager we're ready to unfreeze the screen, at its leisure. Note that there is + * likely a lot going on, and WM won't unfreeze until the drawing is all done, so + * the actual unfreeze may still not happen for a long time; this is expected. + */ @VisibleForTesting void unfreezeScreen() { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index 145a298af95e..4eba77168b8e 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -473,7 +473,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } try { - mWindowManagerInternal.addTaskOverlay( + mWindowManagerInternal.addTrustedTaskOverlay( taskId, createGameSessionResult.getSurfacePackage()); } catch (IllegalArgumentException ex) { @@ -519,7 +519,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage(); if (surfacePackage != null) { try { - mWindowManagerInternal.removeTaskOverlay( + mWindowManagerInternal.removeTrustedTaskOverlay( gameSessionRecord.getTaskId(), surfacePackage); } catch (IllegalArgumentException ex) { diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 366718c65d84..aa0d3da6a94f 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -35,6 +35,8 @@ import android.app.ActivityThread; import android.app.IActivityManager; import android.app.StatsManager; import android.app.StatsManager.StatsPullAtomCallback; +import android.app.usage.StorageStats; +import android.app.usage.StorageStatsManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.UsageEventListener; @@ -78,6 +80,7 @@ import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -111,6 +114,7 @@ public final class AppHibernationService extends SystemService { private final PackageManagerInternal mPackageManagerInternal; private final IActivityManager mIActivityManager; private final UserManager mUserManager; + private final StorageStatsManager mStorageStatsManager; @GuardedBy("mLock") private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>(); @@ -147,6 +151,7 @@ public final class AppHibernationService extends SystemService { mPackageManagerInternal = injector.getPackageManagerInternal(); mIActivityManager = injector.getActivityManager(); mUserManager = injector.getUserManager(); + mStorageStatsManager = injector.getStorageStatsManager(); mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore(); mBackgroundExecutor = injector.getBackgroundExecutor(); mOatArtifactDeletionEnabled = injector.isOatArtifactDeletionEnabled(); @@ -217,7 +222,7 @@ public final class AppHibernationService extends SystemService { */ boolean isHibernatingForUser(String packageName, int userId) { String methodName = "isHibernatingForUser"; - if (!checkHibernationEnabled(methodName)) { + if (!sIsServiceEnabled) { return false; } getContext().enforceCallingOrSelfPermission( @@ -246,7 +251,7 @@ public final class AppHibernationService extends SystemService { * @param packageName package to check */ boolean isHibernatingGlobally(String packageName) { - if (!checkHibernationEnabled("isHibernatingGlobally")) { + if (!sIsServiceEnabled) { return false; } getContext().enforceCallingOrSelfPermission( @@ -272,7 +277,7 @@ public final class AppHibernationService extends SystemService { */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { String methodName = "setHibernatingForUser"; - if (!checkHibernationEnabled(methodName)) { + if (!sIsServiceEnabled) { return; } getContext().enforceCallingOrSelfPermission( @@ -297,7 +302,8 @@ public final class AppHibernationService extends SystemService { pkgState.hibernated = isHibernating; if (isHibernating) { - mBackgroundExecutor.execute(() -> hibernatePackageForUser(packageName, realUserId)); + mBackgroundExecutor.execute( + () -> hibernatePackageForUser(packageName, realUserId, pkgState)); } else { mBackgroundExecutor.execute( () -> unhibernatePackageForUser(packageName, realUserId)); @@ -326,7 +332,7 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingGlobally(String packageName, boolean isHibernating) { - if (!checkHibernationEnabled("setHibernatingGlobally")) { + if (!sIsServiceEnabled) { return; } getContext().enforceCallingOrSelfPermission( @@ -359,7 +365,7 @@ public final class AppHibernationService extends SystemService { @NonNull List<String> getHibernatingPackagesForUser(int userId) { ArrayList<String> hibernatingPackages = new ArrayList<>(); String methodName = "getHibernatingPackagesForUser"; - if (!checkHibernationEnabled(methodName)) { + if (!sIsServiceEnabled) { return hibernatingPackages; } getContext().enforceCallingOrSelfPermission( @@ -390,6 +396,9 @@ public final class AppHibernationService extends SystemService { @Nullable Set<String> packageNames, int userId) { Map<String, HibernationStats> statsMap = new ArrayMap<>(); String methodName = "getHibernationStatsForUser"; + if (!sIsServiceEnabled) { + return statsMap; + } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); @@ -412,8 +421,9 @@ public final class AppHibernationService extends SystemService { + "the package was uninstalled? ", pkgName, userId)); continue; } - HibernationStats stats = new HibernationStats( - mGlobalHibernationStates.get(pkgName).savedByte); + long diskBytesSaved = mGlobalHibernationStates.get(pkgName).savedByte + + userPackageStates.get(pkgName).savedByte; + HibernationStats stats = new HibernationStats(diskBytesSaved); statsMap.put(pkgName, stats); } } @@ -424,16 +434,28 @@ public final class AppHibernationService extends SystemService { * Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do * not hold {@link #mLock} while calling this to avoid deadlock scenarios. */ - private void hibernatePackageForUser(@NonNull String packageName, int userId) { + private void hibernatePackageForUser(@NonNull String packageName, int userId, + UserLevelState state) { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage"); final long caller = Binder.clearCallingIdentity(); try { + ApplicationInfo info = mIPackageManager.getApplicationInfo( + packageName, PACKAGE_MATCH_FLAGS, userId); + StorageStats stats = mStorageStatsManager.queryStatsForPackage( + info.storageUuid, packageName, new UserHandle(userId)); mIActivityManager.forceStopPackage(packageName, userId); mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId, null /* observer */); + synchronized (mLock) { + state.savedByte = stats.getCacheBytes(); + } } catch (RemoteException e) { throw new IllegalStateException( "Failed to hibernate due to manager not being available", e); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Package name not found when querying storage stats", e); + } catch (IOException e) { + Slog.e(TAG, "Storage device not found", e); } finally { Binder.restoreCallingIdentity(caller); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); @@ -677,6 +699,7 @@ public final class AppHibernationService extends SystemService { for (String key : properties.getKeyset()) { if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) { sIsServiceEnabled = isDeviceConfigAppHibernationEnabled(); + Slog.d(TAG, "App hibernation changed to enabled=" + sIsServiceEnabled); break; } } @@ -721,13 +744,6 @@ public final class AppHibernationService extends SystemService { return true; } - private boolean checkHibernationEnabled(String methodName) { - if (!sIsServiceEnabled) { - Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); - } - return sIsServiceEnabled; - } - private void dump(PrintWriter pw) { // Check usage stats permission since hibernation indirectly informs usage. if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; @@ -923,6 +939,8 @@ public final class AppHibernationService extends SystemService { UserManager getUserManager(); + StorageStatsManager getStorageStatsManager(); + Executor getBackgroundExecutor(); UsageStatsManagerInternal getUsageStatsManagerInternal(); @@ -972,6 +990,11 @@ public final class AppHibernationService extends SystemService { } @Override + public StorageStatsManager getStorageStatsManager() { + return mContext.getSystemService(StorageStatsManager.class); + } + + @Override public Executor getBackgroundExecutor() { return mScheduledExecutorService; } diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java index 68c363c8256a..6a489d28ac17 100644 --- a/services/core/java/com/android/server/apphibernation/UserLevelState.java +++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java @@ -28,6 +28,8 @@ final class UserLevelState { public String packageName; public boolean hibernated; + // Saved bytes from user level hibernation. + public long savedByte; @CurrentTimeMillisLong public long lastUnhibernatedMs; @@ -36,6 +38,7 @@ final class UserLevelState { UserLevelState(UserLevelState state) { packageName = state.packageName; hibernated = state.hibernated; + savedByte = state.savedByte; lastUnhibernatedMs = state.lastUnhibernatedMs; } @@ -44,6 +47,7 @@ final class UserLevelState { return "UserLevelState{" + "packageName='" + packageName + '\'' + ", hibernated=" + hibernated + '\'' + + ", savedByte=" + savedByte + '\'' + ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs) + '}'; } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 5330845907ca..daf3561d75ce 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -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); } } @@ -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 4494d963418e..05955c3cab44 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -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) */ 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 a006b91c9eb6..3491cd59ebb7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -595,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, @@ -604,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/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 1b0341c1ce26..e0b768d7c592 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -808,6 +808,7 @@ public class CameraServiceProxy extends SystemService streamProtos[i].histogramBins = streamStats.getHistogramBins(); streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); + streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); if (CameraServiceProxy.DEBUG) { String histogramTypeName = @@ -828,7 +829,8 @@ public class CameraServiceProxy extends SystemService + Arrays.toString(streamProtos[i].histogramBins) + ", histogramCounts " + Arrays.toString(streamProtos[i].histogramCounts) - + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile); + + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile + + ", streamUseCase " + streamProtos[i].streamUseCase); } } } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 53fe4509b1cc..eb2f80b96dce 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -722,7 +722,7 @@ public class ClipboardService extends SystemService { clipboard.primaryClipListeners.getBroadcastItem(i) .dispatchPrimaryClipChanged(); } - } catch (RemoteException e) { + } catch (RemoteException | SecurityException e) { // The RemoteCallbackList will take care of removing // the dead object for us. } 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..6d625f6d47d7 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -41,7 +41,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.database.ContentObserver; import android.graphics.PointF; -import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayViewport; import android.hardware.input.IInputDevicesChangedListener; @@ -150,8 +149,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"; @@ -198,8 +195,9 @@ public class InputManagerService extends IInputManager.Stub private final Object mTabletModeLock = new Object(); // List of currently registered tablet mode changed listeners by process id + @GuardedBy("mTabletModeLock") private final SparseArray<TabletModeChangedListenerRecord> mTabletModeChangedListeners = - new SparseArray<>(); // guarded by mTabletModeLock + new SparseArray<>(); private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify = new ArrayList<>(); @@ -217,40 +215,39 @@ public class InputManagerService extends IInputManager.Stub private final PersistentDataStore mDataStore = new PersistentDataStore(); // List of currently registered input devices changed listeners by process id. - private Object mInputDevicesLock = new Object(); + private final Object mInputDevicesLock = new Object(); @GuardedBy("mInputDevicesLock") - private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock + private boolean mInputDevicesChangedPending; @GuardedBy("mInputDevicesLock") private InputDevice[] mInputDevices = new InputDevice[0]; + @GuardedBy("mInputDevicesLock") private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners = - new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock + new SparseArray<>(); private final ArrayList<InputDevicesChangedListenerRecord> - mTempInputDevicesChangedListenersToNotify = - new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only - private final ArrayList<InputDevice> - mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only + mTempInputDevicesChangedListenersToNotify = new ArrayList<>(); // handler thread only + private final ArrayList<InputDevice> mTempFullKeyboards = + new ArrayList<>(); // handler thread only private boolean mKeyboardLayoutNotificationShown; private Toast mSwitchedKeyboardLayoutToast; // State for vibrator tokens. - private Object mVibratorLock = new Object(); - private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>(); + private final Object mVibratorLock = new Object(); + private final Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<>(); private int mNextVibratorTokenValue; // List of currently registered vibrator state changed listeners by device id. @GuardedBy("mVibratorLock") private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners = - new SparseArray<RemoteCallbackList<IVibratorStateListener>>(); + new SparseArray<>(); // List of vibrator states by device id. @GuardedBy("mVibratorLock") private final SparseBooleanArray mIsVibrating = new SparseBooleanArray(); - private Object mLightLock = new Object(); + private final Object mLightLock = new Object(); // State for light tokens. A light token marks a lights manager session, it is generated // by light session open() and deleted in session close(). // When lights session requests light states, the token will be used to find the light session. @GuardedBy("mLightLock") - private final ArrayMap<IBinder, LightSession> mLightSessions = - new ArrayMap<IBinder, LightSession>(); + private final ArrayMap<IBinder, LightSession> mLightSessions = new ArrayMap<>(); // State for lid switch // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events @@ -259,19 +256,21 @@ public class InputManagerService extends IInputManager.Stub // events that occur at the same time are delivered after the callback has returned. private final Object mLidSwitchLock = new Object(); @GuardedBy("mLidSwitchLock") - private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>(); + private final List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>(); // State for the currently installed input filter. final Object mInputFilterLock = new Object(); - IInputFilter mInputFilter; // guarded by mInputFilterLock - InputFilterHost mInputFilterHost; // guarded by mInputFilterLock + @GuardedBy("mInputFilterLock") + IInputFilter mInputFilter; + @GuardedBy("mInputFilterLock") + InputFilterHost mInputFilterHost; // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") - private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>(); + private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<>(); @GuardedBy("mAssociationLock") private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>(); private final Object mPointerDisplayIdLock = new Object(); @@ -303,11 +302,12 @@ 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); - private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode); + private static native boolean nativeSetInTouchMode(long ptr, boolean inTouchMode, int pid, + int uid, boolean hasPermission); private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity); private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode); private static native int nativeInjectInputEvent(long ptr, InputEvent event, @@ -582,32 +582,6 @@ public class InputManagerService extends IInputManager.Stub nativeReloadDeviceAliases(mPtr); } - /** Rotates CCW by `delta` 90-degree increments. */ - private static void rotateBounds(Rect inOutBounds, int parentW, int parentH, int delta) { - int rdelta = ((delta % 4) + 4) % 4; - int origLeft = inOutBounds.left; - switch (rdelta) { - case 0: - return; - case 1: - inOutBounds.left = inOutBounds.top; - inOutBounds.top = parentW - inOutBounds.right; - inOutBounds.right = inOutBounds.bottom; - inOutBounds.bottom = parentW - origLeft; - return; - case 2: - inOutBounds.left = parentW - inOutBounds.right; - inOutBounds.right = parentW - origLeft; - return; - case 3: - inOutBounds.left = parentH - inOutBounds.bottom; - inOutBounds.bottom = inOutBounds.right; - inOutBounds.right = parentH - inOutBounds.top; - inOutBounds.top = origLeft; - return; - } - } - private void setDisplayViewportsInternal(List<DisplayViewport> viewports) { final DisplayViewport[] vArray = new DisplayViewport[viewports.size()]; for (int i = viewports.size() - 1; i >= 0; --i) { @@ -720,8 +694,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 +763,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); @@ -872,12 +842,16 @@ public class InputManagerService extends IInputManager.Stub * other apps, when they become focused. * * When input dispatches focus to the apps, the touch mode state - * will be sent together with the focus change. + * will be sent together with the focus change (but each one in its own event). * - * @param inTouchMode true if the device is in touch mode. + * @param inTouchMode true if the device is in touch mode + * @param pid the pid of the process that requested to switch touch mode state + * @param uid the uid of the process that requested to switch touch mode state + * @param hasPermission if set to {@code true} then no further authorization will be performed + * @return {@code true} if the touch mode was successfully changed, {@code false} otherwise */ - public void setInTouchMode(boolean inTouchMode) { - nativeSetInTouchMode(mPtr, inTouchMode); + public boolean setInTouchMode(boolean inTouchMode, int pid, int uid, boolean hasPermission) { + return nativeSetInTouchMode(mPtr, inTouchMode, pid, uid, hasPermission); } @Override // Binder call @@ -934,9 +908,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public InputDevice getInputDevice(int deviceId) { synchronized (mInputDevicesLock) { - final int count = mInputDevices.length; - for (int i = 0; i < count; i++) { - final InputDevice inputDevice = mInputDevices[i]; + for (final InputDevice inputDevice : mInputDevices) { if (inputDevice.getId() == deviceId) { return inputDevice; } @@ -1119,23 +1091,19 @@ public class InputManagerService extends IInputManager.Stub return null; } final List<KeyboardLayout> layouts = new ArrayList<>(); - visitAllKeyboardLayouts(new KeyboardLayoutVisitor() { - @Override - public void visitKeyboardLayout(Resources resources, - int keyboardLayoutResId, KeyboardLayout layout) { - // Only select a default when we know the layout is appropriate. For now, this - // means its a custom layout for a specific keyboard. - if (layout.getVendorId() != d.getVendorId() - || layout.getProductId() != d.getProductId()) { - return; - } - final LocaleList locales = layout.getLocales(); - final int numLocales = locales.size(); - for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) { - if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) { - layouts.add(layout); - break; - } + visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> { + // Only select a default when we know the layout is appropriate. For now, this + // means it's a custom layout for a specific keyboard. + if (layout.getVendorId() != d.getVendorId() + || layout.getProductId() != d.getProductId()) { + return; + } + final LocaleList locales = layout.getLocales(); + final int numLocales = locales.size(); + for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) { + if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) { + layouts.add(layout); + break; } } }); @@ -1335,13 +1303,8 @@ public class InputManagerService extends IInputManager.Stub private void updateKeyboardLayouts() { // Scan all input devices state for keyboard layouts that have been uninstalled. final HashSet<String> availableKeyboardLayouts = new HashSet<String>(); - visitAllKeyboardLayouts(new KeyboardLayoutVisitor() { - @Override - public void visitKeyboardLayout(Resources resources, - int keyboardLayoutResId, KeyboardLayout layout) { - availableKeyboardLayouts.add(layout.getDescriptor()); - } - }); + visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> + availableKeyboardLayouts.add(layout.getDescriptor())); synchronized (mDataStore) { try { mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts); @@ -1368,14 +1331,8 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public KeyboardLayout[] getKeyboardLayouts() { - final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>(); - visitAllKeyboardLayouts(new KeyboardLayoutVisitor() { - @Override - public void visitKeyboardLayout(Resources resources, - int keyboardLayoutResId, KeyboardLayout layout) { - list.add(layout); - } - }); + final ArrayList<KeyboardLayout> list = new ArrayList<>(); + visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> list.add(layout)); return list.toArray(new KeyboardLayout[list.size()]); } @@ -1383,10 +1340,10 @@ public class InputManagerService extends IInputManager.Stub public KeyboardLayout[] getKeyboardLayoutsForInputDevice( final InputDeviceIdentifier identifier) { final String[] enabledLayoutDescriptors = - getEnabledKeyboardLayoutsForInputDevice(identifier); + getEnabledKeyboardLayoutsForInputDevice(identifier); final ArrayList<KeyboardLayout> enabledLayouts = - new ArrayList<KeyboardLayout>(enabledLayoutDescriptors.length); - final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<KeyboardLayout>(); + new ArrayList<>(enabledLayoutDescriptors.length); + final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<>(); visitAllKeyboardLayouts(new KeyboardLayoutVisitor() { boolean mHasSeenDeviceSpecificLayout; @@ -1434,13 +1391,8 @@ public class InputManagerService extends IInputManager.Stub "keyboardLayoutDescriptor must not be null"); final KeyboardLayout[] result = new KeyboardLayout[1]; - visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() { - @Override - public void visitKeyboardLayout(Resources resources, - int keyboardLayoutResId, KeyboardLayout layout) { - result[0] = layout; - } - }); + visitKeyboardLayout(keyboardLayoutDescriptor, + (resources, keyboardLayoutResId, layout) -> result[0] = layout); if (result[0] == null) { Slog.w(TAG, "Could not get keyboard layout with descriptor '" + keyboardLayoutDescriptor + "'."); @@ -1472,7 +1424,7 @@ public class InputManagerService extends IInputManager.Stub | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, 0, visitor); - } catch (NameNotFoundException ex) { + } catch (NameNotFoundException ignored) { } } } @@ -1502,11 +1454,10 @@ public class InputManagerService extends IInputManager.Stub try { Resources resources = pm.getResourcesForApplication(receiver.applicationInfo); - XmlResourceParser parser = resources.getXml(configResId); - try { + try (XmlResourceParser parser = resources.getXml(configResId)) { XmlUtils.beginDocument(parser, "keyboard-layouts"); - for (;;) { + while (true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) { @@ -1514,22 +1465,22 @@ public class InputManagerService extends IInputManager.Stub } if (element.equals("keyboard-layout")) { TypedArray a = resources.obtainAttributes( - parser, com.android.internal.R.styleable.KeyboardLayout); + parser, R.styleable.KeyboardLayout); try { String name = a.getString( - com.android.internal.R.styleable.KeyboardLayout_name); + R.styleable.KeyboardLayout_name); String label = a.getString( - com.android.internal.R.styleable.KeyboardLayout_label); + R.styleable.KeyboardLayout_label); int keyboardLayoutResId = a.getResourceId( - com.android.internal.R.styleable.KeyboardLayout_keyboardLayout, + R.styleable.KeyboardLayout_keyboardLayout, 0); String languageTags = a.getString( - com.android.internal.R.styleable.KeyboardLayout_locale); + R.styleable.KeyboardLayout_locale); LocaleList locales = getLocalesFromLanguageTags(languageTags); int vid = a.getInt( - com.android.internal.R.styleable.KeyboardLayout_vendorId, -1); + R.styleable.KeyboardLayout_vendorId, -1); int pid = a.getInt( - com.android.internal.R.styleable.KeyboardLayout_productId, -1); + R.styleable.KeyboardLayout_productId, -1); if (name == null || label == null || keyboardLayoutResId == 0) { Slog.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' " @@ -1556,8 +1507,6 @@ public class InputManagerService extends IInputManager.Stub + receiver.packageName + "/" + receiver.name); } } - } finally { - parser.close(); } } catch (Exception ex) { Slog.w(TAG, "Could not parse keyboard layout resource from receiver " @@ -1584,10 +1533,7 @@ public class InputManagerService extends IInputManager.Stub if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) { return identifier.getDescriptor(); } - StringBuilder bob = new StringBuilder(); - bob.append("vendor:").append(identifier.getVendorId()); - bob.append(",product:").append(identifier.getProductId()); - return bob.toString(); + return "vendor:" + identifier.getVendorId() + ",product:" + identifier.getProductId(); } @Override // Binder call @@ -1595,7 +1541,7 @@ public class InputManagerService extends IInputManager.Stub String key = getLayoutDescriptor(identifier); synchronized (mDataStore) { - String layout = null; + String layout; // try loading it using the layout descriptor if we have it layout = mDataStore.getCurrentKeyboardLayout(key); if (layout == null && !key.equals(identifier.getDescriptor())) { @@ -1870,7 +1816,7 @@ public class InputManagerService extends IInputManager.Stub try { speed = Settings.System.getIntForUser(mContext.getContentResolver(), Settings.System.POINTER_SPEED, UserHandle.USER_CURRENT); - } catch (SettingNotFoundException snfe) { + } catch (SettingNotFoundException ignored) { } return speed; } @@ -2134,7 +2080,7 @@ public class InputManagerService extends IInputManager.Stub SparseArray<VibrationEffect> effects = stereo.getEffects(); long[] pattern = new long[0]; int repeat = Integer.MIN_VALUE; - SparseArray<int[]> amplitudes = new SparseArray<int[]>(effects.size()); + SparseArray<int[]> amplitudes = new SparseArray<>(effects.size()); for (int i = 0; i < effects.size(); i++) { VibrationInfo info = new VibrationInfo(effects.valueAt(i)); // Pattern of all effects should be same @@ -2184,6 +2130,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifyVibratorState(int deviceId, boolean isOn) { if (DEBUG) { Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn); @@ -2225,7 +2172,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { - Preconditions.checkNotNull(listener, "listener must not be null"); + Objects.requireNonNull(listener, "listener must not be null"); RemoteCallbackList<IVibratorStateListener> listeners; synchronized (mVibratorLock) { @@ -2383,7 +2330,7 @@ public class InputManagerService extends IInputManager.Stub } Objects.requireNonNull(listener, "listener must not be null"); - synchronized (mInputDevicesLock) { + synchronized (mSensorEventLock) { int callingPid = Binder.getCallingPid(); if (mSensorEventListeners.get(callingPid) != null) { Slog.e(TAG, "The calling process " + callingPid + " has already " @@ -2415,7 +2362,7 @@ public class InputManagerService extends IInputManager.Stub Objects.requireNonNull(listener, "listener must not be null"); - synchronized (mInputDevicesLock) { + synchronized (mSensorEventLock) { int callingPid = Binder.getCallingPid(); if (mSensorEventListeners.get(callingPid) != null) { SensorEventListenerRecord record = mSensorEventListeners.get(callingPid); @@ -2429,7 +2376,7 @@ public class InputManagerService extends IInputManager.Stub @Override // Binder call public boolean flushSensor(int deviceId, int sensorType) { - synchronized (mInputDevicesLock) { + synchronized (mSensorEventLock) { int callingPid = Binder.getCallingPid(); SensorEventListenerRecord listener = mSensorEventListeners.get(callingPid); if (listener != null) { @@ -2497,7 +2444,7 @@ public class InputManagerService extends IInputManager.Stub * Set specified light state with for a specific input device. */ private void setLightStateInternal(int deviceId, Light light, LightState lightState) { - Preconditions.checkNotNull(light, "light does not exist"); + Objects.requireNonNull(light, "light does not exist"); if (DEBUG) { Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light + "lightState " + lightState); @@ -2560,7 +2507,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void openLightSession(int deviceId, String opPkg, IBinder token) { - Preconditions.checkNotNull(token); + Objects.requireNonNull(token); synchronized (mLightLock) { Preconditions.checkState(mLightSessions.get(token) == null, "already registered"); LightSession lightSession = new LightSession(deviceId, opPkg, token); @@ -2579,7 +2526,7 @@ public class InputManagerService extends IInputManager.Stub @Override public void closeLightSession(int deviceId, IBinder token) { - Preconditions.checkNotNull(token); + Objects.requireNonNull(token); synchronized (mLightLock) { LightSession lightSession = mLightSessions.get(token); Preconditions.checkState(lightSession != null, "not registered"); @@ -2689,11 +2636,13 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifyConfigurationChanged(long whenNanos) { mWindowManagerCallbacks.notifyConfigurationChanged(); } // Native callback. + @SuppressWarnings("unused") private void notifyInputDevicesChanged(InputDevice[] inputDevices) { synchronized (mInputDevicesLock) { if (!mInputDevicesChangedPending) { @@ -2707,6 +2656,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifySwitch(long whenNanos, int switchValues, int switchMask) { if (DEBUG) { Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues) @@ -2739,7 +2689,7 @@ public class InputManagerService extends IInputManager.Stub SomeArgs args = SomeArgs.obtain(); args.argi1 = (int) (whenNanos & 0xFFFFFFFF); args.argi2 = (int) (whenNanos >> 32); - args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0); + args.arg1 = (switchValues & SW_TABLET_MODE_BIT) != 0; mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED, args).sendToTarget(); } @@ -2752,6 +2702,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifyInputChannelBroken(IBinder token) { synchronized (mInputMonitors) { if (mInputMonitors.containsKey(token)) { @@ -2762,16 +2713,19 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private void notifyFocusChanged(IBinder oldToken, IBinder newToken) { mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken); } // Native callback + @SuppressWarnings("unused") private void notifyDropWindow(IBinder token, float x, float y) { mWindowManagerCallbacks.notifyDropWindow(token, x, y); } // Native callback + @SuppressWarnings("unused") private void notifyUntrustedTouch(String packageName) { // TODO(b/169067926): Remove toast after gathering feedback on dogfood. if (!UNTRUSTED_TOUCHES_TOAST || ArrayUtils.contains( @@ -2787,11 +2741,13 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifyNoFocusedWindowAnr(InputApplicationHandle inputApplicationHandle) { mWindowManagerCallbacks.notifyNoFocusedWindowAnr(inputApplicationHandle); } // Native callback + @SuppressWarnings("unused") private void notifyWindowUnresponsive(IBinder token, String reason) { int gestureMonitorPid = -1; synchronized (mInputMonitors) { @@ -2808,11 +2764,13 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private void notifyMonitorUnresponsive(int pid, String reason) { mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(pid, reason); } // Native callback + @SuppressWarnings("unused") private void notifyWindowResponsive(IBinder token) { int gestureMonitorPid = -1; synchronized (mInputMonitors) { @@ -2829,11 +2787,13 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private void notifyMonitorResponsive(int pid) { mWindowManagerCallbacks.notifyGestureMonitorResponsive(pid); } // Native callback. + @SuppressWarnings("unused") private void notifySensorEvent(int deviceId, int sensorType, int accuracy, long timestamp, float[] values) { if (DEBUG) { @@ -2857,6 +2817,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifySensorAccuracy(int deviceId, int sensorType, int accuracy) { mSensorAccuracyListenersToNotify.clear(); final int numListeners; @@ -2874,6 +2835,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") final boolean filterInputEvent(InputEvent event, int policyFlags) { synchronized (mInputFilterLock) { if (mInputFilter != null) { @@ -2890,11 +2852,13 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags); } // Native callback. + @SuppressWarnings("unused") private int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags) { return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive( @@ -2902,33 +2866,39 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) { return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags); } // Native callback. + @SuppressWarnings("unused") private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) { return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags); } // Native callback. + @SuppressWarnings("unused") private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) { return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED; } // Native callback. + @SuppressWarnings("unused") private void onPointerDownOutsideFocus(IBinder touchedToken) { mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken); } // Native callback. + @SuppressWarnings("unused") private int getVirtualKeyQuietTimeMillis() { return mContext.getResources().getInteger( com.android.internal.R.integer.config_virtualKeyQuietTimeMillis); } // Native callback. + @SuppressWarnings("unused") private static String[] getExcludedDeviceNames() { List<String> names = new ArrayList<>(); // Read partner-provided list of excluded input devices @@ -2984,6 +2954,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private String[] getInputPortAssociations() { final Map<String, Integer> associations = new HashMap<>(mStaticAssociations); @@ -2996,6 +2967,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private String[] getInputUniqueIdAssociations() { final Map<String, String> associations; synchronized (mAssociationsLock) { @@ -3016,46 +2988,55 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private int getKeyRepeatTimeout() { return ViewConfiguration.getKeyRepeatTimeout(); } // Native callback. + @SuppressWarnings("unused") private int getKeyRepeatDelay() { return ViewConfiguration.getKeyRepeatDelay(); } // Native callback. + @SuppressWarnings("unused") private int getHoverTapTimeout() { return ViewConfiguration.getHoverTapTimeout(); } // Native callback. + @SuppressWarnings("unused") private int getHoverTapSlop() { return ViewConfiguration.getHoverTapSlop(); } // Native callback. + @SuppressWarnings("unused") private int getDoubleTapTimeout() { return ViewConfiguration.getDoubleTapTimeout(); } // Native callback. + @SuppressWarnings("unused") private int getLongPressTimeout() { return ViewConfiguration.getLongPressTimeout(); } // Native callback. + @SuppressWarnings("unused") private int getPointerLayer() { return mWindowManagerCallbacks.getPointerLayer(); } // Native callback. + @SuppressWarnings("unused") private PointerIcon getPointerIcon(int displayId) { return PointerIcon.getDefaultIcon(getContextForPointerIcon(displayId)); } // Native callback. + @SuppressWarnings("unused") private long getParentSurfaceForPointers(int displayId) { final SurfaceControl sc = mWindowManagerCallbacks.getParentSurfaceForPointers(displayId); if (sc == null) { @@ -3101,6 +3082,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private int getPointerDisplayId() { synchronized (mPointerDisplayIdLock) { // Prefer the override to all other displays. @@ -3112,6 +3094,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) { if (!mSystemReady) { return null; @@ -3123,19 +3106,15 @@ public class InputManagerService extends IInputManager.Stub } final String[] result = new String[2]; - visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() { - @Override - public void visitKeyboardLayout(Resources resources, - int keyboardLayoutResId, KeyboardLayout layout) { - try (final InputStreamReader stream = new InputStreamReader( - resources.openRawResource(keyboardLayoutResId))) { - result[0] = layout.getDescriptor(); - result[1] = Streams.readFully(stream); - } catch (IOException ex) { - } catch (NotFoundException ex) { - } - } - }); + visitKeyboardLayout(keyboardLayoutDescriptor, + (resources, keyboardLayoutResId, layout) -> { + try (InputStreamReader stream = new InputStreamReader( + resources.openRawResource(keyboardLayoutResId))) { + result[0] = layout.getDescriptor(); + result[1] = Streams.readFully(stream); + } catch (IOException | NotFoundException ignored) { + } + }); if (result[0] == null) { Slog.w(TAG, "Could not get keyboard layout with descriptor '" + keyboardLayoutDescriptor + "'."); @@ -3145,6 +3124,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private String getDeviceAlias(String uniqueId) { if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) { // TODO(BT) mBluetoothService.getRemoteAlias(uniqueId) @@ -3294,8 +3274,18 @@ public class InputManagerService extends IInputManager.Stub * Callback interface implemented by WiredAccessoryObserver. */ public interface WiredAccessoryCallbacks { - public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask); - public void systemReady(); + /** + * Notifies WiredAccessoryObserver that input state for wired accessories has changed + * @param whenNanos When the wired accessories changed + * @param switchValues The state of the switches + * @param switchMask The mask of switches that changed + */ + void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask); + + /** + * Notifies WiredAccessoryObserver that the system is now ready. + */ + void systemReady(); } /** @@ -3326,7 +3316,7 @@ public class InputManagerService extends IInputManager.Stub break; case MSG_DELIVER_TABLET_MODE_CHANGED: SomeArgs args = (SomeArgs) msg.obj; - long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); + long whenNanos = (args.argi1 & 0xFFFFFFFFL) | ((long) args.argi2 << 32); boolean inTabletMode = (boolean) args.arg1; deliverTabletModeChanged(whenNanos, inTabletMode); break; @@ -3338,8 +3328,10 @@ public class InputManagerService extends IInputManager.Stub * Hosting interface for input filters to call back into the input manager. */ private final class InputFilterHost extends IInputFilterHost.Stub { + @GuardedBy("mInputFilterLock") private boolean mDisconnected; + @GuardedBy("mInputFilterLock") public void disconnectLocked() { mDisconnected = true; } @@ -3375,11 +3367,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/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index c207738a4f88..ba15a047af4a 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1495,8 +1495,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onStart() { - LocalServices.addService(InputMethodManagerInternal.class, - new LocalServiceImpl(mService)); + mService.publishLocalService(); publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO); } @@ -4866,26 +4865,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return mCurrentSubtype; } - private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) { - synchronized (ImfLock.class) { - return getInputMethodListLocked(userId, DirectBootAwareness.AUTO); - } - } - - private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) { - synchronized (ImfLock.class) { - return getEnabledInputMethodListLocked(userId); - } - } - - private void onCreateInlineSuggestionsRequest(@UserIdInt int userId, - InlineSuggestionsRequestInfo requestInfo, - IInlineSuggestionsRequestCallback callback) { - synchronized (ImfLock.class) { - onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback); - } - } - private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) { final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); @@ -4897,161 +4876,153 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return methodMap; } - private boolean switchToInputMethod(String imeId, @UserIdInt int userId) { - synchronized (ImfLock.class) { - if (userId == mSettings.getCurrentUserId()) { - if (!mMethodMap.containsKey(imeId) - || !mSettings.getEnabledInputMethodListLocked() - .contains(mMethodMap.get(imeId))) { - return false; // IME is not found or not enabled. - } - setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); - return true; - } - final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings( - mContext.getResources(), mContext.getContentResolver(), methodMap, - userId, false); - if (!methodMap.containsKey(imeId) - || !settings.getEnabledInputMethodListLocked() - .contains(methodMap.get(imeId))) { + @GuardedBy("ImfLock.class") + private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) { + if (userId == mSettings.getCurrentUserId()) { + if (!mMethodMap.containsKey(imeId) + || !mSettings.getEnabledInputMethodListLocked() + .contains(mMethodMap.get(imeId))) { return false; // IME is not found or not enabled. } - settings.putSelectedInputMethod(imeId); - settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); - return true; - } - } - - private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) { - synchronized (ImfLock.class) { - if (userId == mSettings.getCurrentUserId()) { - if (!mMethodMap.containsKey(imeId)) { - return false; // IME is not found. - } - setInputMethodEnabledLocked(imeId, enabled); - return true; - } - final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); - final InputMethodSettings settings = new InputMethodSettings( - mContext.getResources(), mContext.getContentResolver(), methodMap, - userId, false); - if (!methodMap.containsKey(imeId)) { - return false; // IME is not found. - } - if (enabled) { - if (!settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) { - settings.appendAndPutEnabledInputMethodLocked(imeId, false); - } - } else { - settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( - new StringBuilder(), - settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId); - } + setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); return true; } + final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); + final InputMethodSettings settings = new InputMethodSettings( + mContext.getResources(), mContext.getContentResolver(), methodMap, + userId, false); + if (!methodMap.containsKey(imeId) + || !settings.getEnabledInputMethodListLocked() + .contains(methodMap.get(imeId))) { + return false; // IME is not found or not enabled. + } + settings.putSelectedInputMethod(imeId); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + return true; } - private boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken, - int displayId) { - //TODO(b/150843766): Check if Input Token is valid. - final IBinder curHostInputToken; - synchronized (ImfLock.class) { - if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) { - return false; - } - curHostInputToken = mCurHostInputToken; - } - return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken); - } - - private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) { - synchronized (ImfLock.class) { - if (mCurFocusedWindow != windowToken) { - // mCurPerceptible was set by the focused window, but it is no longer in control, - // so we reset mCurPerceptible. - mCurPerceptible = true; - } - if (imeParentChanged) { - // Hide the IME method menu earlier when the IME surface parent will change in - // case seeing the dialog dismiss flickering during the next focused window - // starting the input connection. - mMenuController.hideInputMethodMenu(); - } - } + private void publishLocalService() { + LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl()); } - private static final class LocalServiceImpl extends InputMethodManagerInternal { - @NonNull - private final InputMethodManagerService mService; - - LocalServiceImpl(@NonNull InputMethodManagerService service) { - mService = service; - } + private final class LocalServiceImpl extends InputMethodManagerInternal { @Override public void setInteractive(boolean interactive) { // Do everything in handler so as not to block the caller. - mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0) - .sendToTarget(); + mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0).sendToTarget(); } @Override public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { - mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); - mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget(); + mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); + mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget(); } @Override public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) { - return mService.getInputMethodListAsUser(userId); + synchronized (ImfLock.class) { + return getInputMethodListLocked(userId, DirectBootAwareness.AUTO); + } } @Override public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) { - return mService.getEnabledInputMethodListAsUser(userId); + synchronized (ImfLock.class) { + return getEnabledInputMethodListLocked(userId); + } } @Override public void onCreateInlineSuggestionsRequest(@UserIdInt int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) { - mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb); + synchronized (ImfLock.class) { + onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb); + } } @Override public boolean switchToInputMethod(String imeId, @UserIdInt int userId) { - return mService.switchToInputMethod(imeId, userId); + synchronized (ImfLock.class) { + return switchToInputMethodLocked(imeId, userId); + } } @Override public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) { - return mService.setInputMethodEnabled(imeId, enabled, userId); + synchronized (ImfLock.class) { + if (userId == mSettings.getCurrentUserId()) { + if (!mMethodMap.containsKey(imeId)) { + return false; // IME is not found. + } + setInputMethodEnabledLocked(imeId, enabled); + return true; + } + final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId); + final InputMethodSettings settings = new InputMethodSettings( + mContext.getResources(), mContext.getContentResolver(), methodMap, + userId, false); + if (!methodMap.containsKey(imeId)) { + return false; // IME is not found. + } + if (enabled) { + if (!settings.getEnabledInputMethodListLocked().contains( + methodMap.get(imeId))) { + settings.appendAndPutEnabledInputMethodLocked(imeId, false); + } + } else { + settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( + new StringBuilder(), + settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId); + } + return true; + } } @Override public void registerInputMethodListListener(InputMethodListListener listener) { - mService.mInputMethodListListeners.addIfAbsent(listener); + mInputMethodListListeners.addIfAbsent(listener); } @Override public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken, int displayId) { - return mService.transferTouchFocusToImeWindow(sourceInputToken, displayId); + //TODO(b/150843766): Check if Input Token is valid. + final IBinder curHostInputToken; + synchronized (ImfLock.class) { + if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) { + return false; + } + curHostInputToken = mCurHostInputToken; + } + return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken); } @Override public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) { - mService.reportImeControl(windowToken, imeParentChanged); + synchronized (ImfLock.class) { + if (mCurFocusedWindow != windowToken) { + // mCurPerceptible was set by the focused window, but it is no longer in + // control, so we reset mCurPerceptible. + mCurPerceptible = true; + } + if (imeParentChanged) { + // Hide the IME method menu earlier when the IME surface parent will change in + // case seeing the dialog dismiss flickering during the next focused window + // starting the input connection. + mMenuController.hideInputMethodMenu(); + } + } } @Override public void removeImeSurface() { - mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget(); + mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget(); } @Override public void updateImeWindowStatus(boolean disableImeIcon) { - mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0) + mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0) .sendToTarget(); } } @@ -5689,7 +5660,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (!userHasDebugPriv(userId, shellCommand)) { continue; } - boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId); + boolean failedToSelectUnknownIme = !switchToInputMethodLocked(imeId, userId); if (failedToSelectUnknownIme) { error.print("Unknown input method "); error.print(imeId); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 8ce67a657740..76d06c8801f4 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -119,9 +119,7 @@ import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; -import static com.android.internal.util.XmlUtils.readThisIntArrayXml; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; -import static com.android.internal.util.XmlUtils.writeIntArrayXml; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; @@ -246,7 +244,6 @@ import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.StatLogger; -import com.android.internal.util.XmlUtils; import com.android.net.module.util.NetworkIdentityUtils; import com.android.net.module.util.NetworkStatsUtils; import com.android.net.module.util.PermissionUtils; @@ -260,8 +257,6 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import libcore.io.IoUtils; -import org.xmlpull.v1.XmlPullParserException; - import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -336,7 +331,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int VERSION_ADDED_CYCLE = 11; private static final int VERSION_ADDED_NETWORK_TYPES = 12; private static final int VERSION_SUPPORTED_CARRIER_USAGE = 13; - private static final int VERSION_LATEST = VERSION_SUPPORTED_CARRIER_USAGE; + private static final int VERSION_REMOVED_SUBSCRIPTION_PLANS = 14; + private static final int VERSION_LATEST = VERSION_REMOVED_SUBSCRIPTION_PLANS; @VisibleForTesting public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING; @@ -349,7 +345,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG_POLICY_LIST = "policy-list"; private static final String TAG_NETWORK_POLICY = "network-policy"; - private static final String TAG_SUBSCRIPTION_PLAN = "subscription-plan"; private static final String TAG_UID_POLICY = "uid-policy"; private static final String TAG_APP_POLICY = "app-policy"; private static final String TAG_WHITELIST = "whitelist"; @@ -426,6 +421,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * obj = oldBlockedReasons */ private static final int MSG_BLOCKED_REASON_CHANGED = 21; + /** + * Message to indicate that subscription plans expired and should be cleared. + * arg1 = subId + * arg2 = setSubscriptionPlans call ID + * obj = callingPackage + */ + private static final int MSG_CLEAR_SUBSCRIPTION_PLANS = 22; private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -492,6 +494,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Map from subId to package name that owns subscription plans. */ @GuardedBy("mNetworkPoliciesSecondLock") final SparseArray<String> mSubscriptionPlansOwner = new SparseArray<>(); + /** Map from subId to the ID of the clear plans request. */ + @GuardedBy("mNetworkPoliciesSecondLock") + final SparseIntArray mSetSubscriptionPlansIds = new SparseIntArray(); + /** Atomic integer to generate a new ID for each clear plans request. */ + @GuardedBy("mNetworkPoliciesSecondLock") + int mSetSubscriptionPlansIdCounter = 0; /** Map from subId to daily opportunistic quota. */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -2523,56 +2531,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred)); } - - } else if (TAG_SUBSCRIPTION_PLAN.equals(tag)) { - final String start = readStringAttribute(in, ATTR_CYCLE_START); - final String end = readStringAttribute(in, ATTR_CYCLE_END); - final String period = readStringAttribute(in, ATTR_CYCLE_PERIOD); - final SubscriptionPlan.Builder builder = new SubscriptionPlan.Builder( - RecurrenceRule.convertZonedDateTime(start), - RecurrenceRule.convertZonedDateTime(end), - RecurrenceRule.convertPeriod(period)); - builder.setTitle(readStringAttribute(in, ATTR_TITLE)); - builder.setSummary(readStringAttribute(in, ATTR_SUMMARY)); - - final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES, - SubscriptionPlan.BYTES_UNKNOWN); - final int limitBehavior = readIntAttribute(in, ATTR_LIMIT_BEHAVIOR, - SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN); - if (limitBytes != SubscriptionPlan.BYTES_UNKNOWN - && limitBehavior != SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) { - builder.setDataLimit(limitBytes, limitBehavior); - } - - final long usageBytes = readLongAttribute(in, ATTR_USAGE_BYTES, - SubscriptionPlan.BYTES_UNKNOWN); - final long usageTime = readLongAttribute(in, ATTR_USAGE_TIME, - SubscriptionPlan.TIME_UNKNOWN); - if (usageBytes != SubscriptionPlan.BYTES_UNKNOWN - && usageTime != SubscriptionPlan.TIME_UNKNOWN) { - builder.setDataUsage(usageBytes, usageTime); - } - - final int subId = readIntAttribute(in, ATTR_SUB_ID); - final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE); - - if (version >= VERSION_ADDED_NETWORK_TYPES) { - final int depth = in.getDepth(); - while (XmlUtils.nextElementWithin(in, depth)) { - if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName()) - && ATTR_NETWORK_TYPES.equals( - readStringAttribute(in, ATTR_XML_UTILS_NAME))) { - final int[] networkTypes = - readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null); - builder.setNetworkTypes(networkTypes); - } - } - } - - final SubscriptionPlan plan = builder.build(); - mSubscriptionPlans.put(subId, ArrayUtils.appendElement( - SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan)); - mSubscriptionPlansOwner.put(subId, ownerPackage); } else if (TAG_UID_POLICY.equals(tag)) { final int uid = readIntAttribute(in, ATTR_UID); final int policy = readIntAttribute(in, ATTR_POLICY); @@ -2763,38 +2721,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { out.endTag(null, TAG_NETWORK_POLICY); } - // write all known subscription plans - for (int i = 0; i < mSubscriptionPlans.size(); i++) { - final int subId = mSubscriptionPlans.keyAt(i); - if (subId == INVALID_SUBSCRIPTION_ID) continue; - final String ownerPackage = mSubscriptionPlansOwner.get(subId); - final SubscriptionPlan[] plans = mSubscriptionPlans.valueAt(i); - if (ArrayUtils.isEmpty(plans)) continue; - - for (SubscriptionPlan plan : plans) { - out.startTag(null, TAG_SUBSCRIPTION_PLAN); - writeIntAttribute(out, ATTR_SUB_ID, subId); - writeStringAttribute(out, ATTR_OWNER_PACKAGE, ownerPackage); - final RecurrenceRule cycleRule = plan.getCycleRule(); - writeStringAttribute(out, ATTR_CYCLE_START, - RecurrenceRule.convertZonedDateTime(cycleRule.start)); - writeStringAttribute(out, ATTR_CYCLE_END, - RecurrenceRule.convertZonedDateTime(cycleRule.end)); - writeStringAttribute(out, ATTR_CYCLE_PERIOD, - RecurrenceRule.convertPeriod(cycleRule.period)); - writeStringAttribute(out, ATTR_TITLE, plan.getTitle()); - writeStringAttribute(out, ATTR_SUMMARY, plan.getSummary()); - writeLongAttribute(out, ATTR_LIMIT_BYTES, plan.getDataLimitBytes()); - writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior()); - writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes()); - writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime()); - try { - writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out); - } catch (XmlPullParserException ignored) { } - out.endTag(null, TAG_SUBSCRIPTION_PLAN); - } - } - // write all known uid policies for (int i = 0; i < mUidPolicy.size(); i++) { final int uid = mUidPolicy.keyAt(i); @@ -3668,7 +3594,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) { + public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, + long expirationDurationMillis, String callingPackage) { enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); enforceSubscriptionPlanValidity(plans); @@ -3678,31 +3605,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long token = Binder.clearCallingIdentity(); try { - synchronized (mUidRulesFirstLock) { - synchronized (mNetworkPoliciesSecondLock) { - mSubscriptionPlans.put(subId, plans); - mSubscriptionPlansOwner.put(subId, callingPackage); + setSubscriptionPlansInternal(subId, plans, expirationDurationMillis, callingPackage); + } finally { + Binder.restoreCallingIdentity(token); + } + } - final String subscriberId = mSubIdToSubscriberId.get(subId, null); - if (subscriberId != null) { - ensureActiveCarrierPolicyAL(subId, subscriberId); - maybeUpdateCarrierPolicyCycleAL(subId, subscriberId); - } else { - Slog.wtf(TAG, "Missing subscriberId for subId " + subId); - } + private void setSubscriptionPlansInternal(int subId, SubscriptionPlan[] plans, + long expirationDurationMillis, String callingPackage) { + synchronized (mUidRulesFirstLock) { + synchronized (mNetworkPoliciesSecondLock) { + mSubscriptionPlans.put(subId, plans); + mSubscriptionPlansOwner.put(subId, callingPackage); - handleNetworkPoliciesUpdateAL(true); + final String subscriberId = mSubIdToSubscriberId.get(subId, null); + if (subscriberId != null) { + ensureActiveCarrierPolicyAL(subId, subscriberId); + maybeUpdateCarrierPolicyCycleAL(subId, subscriberId); + } else { + Slog.wtf(TAG, "Missing subscriberId for subId " + subId); } - } - final Intent intent = new Intent(SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); - mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS); - mHandler.sendMessage( - mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans)); - } finally { - Binder.restoreCallingIdentity(token); + handleNetworkPoliciesUpdateAL(true); + + final Intent intent = new Intent( + SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); + mContext.sendBroadcast(intent, + android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS); + mHandler.sendMessage(mHandler.obtainMessage( + MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans)); + final int setPlansId = mSetSubscriptionPlansIdCounter++; + mSetSubscriptionPlansIds.put(subId, setPlansId); + if (expirationDurationMillis > 0) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_SUBSCRIPTION_PLANS, + subId, setPlansId, callingPackage), expirationDurationMillis); + } + } } } @@ -3728,7 +3668,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, - int[] networkTypes, long timeoutMillis, String callingPackage) { + int[] networkTypes, long expirationDurationMillis, String callingPackage) { enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); final ArraySet<Integer> allNetworksSet = new ArraySet<>(); @@ -3766,10 +3706,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { args.arg3 = overrideValue; args.arg4 = applicableNetworks.toArray(); mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args)); - if (timeoutMillis > 0) { + if (expirationDurationMillis > 0) { args.arg3 = 0; mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args), - timeoutMillis); + expirationDurationMillis); } } } @@ -5184,6 +5124,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.finishBroadcast(); return true; } + case MSG_CLEAR_SUBSCRIPTION_PLANS: { + synchronized (mUidRulesFirstLock) { + synchronized (mNetworkPoliciesSecondLock) { + int subId = msg.arg1; + if (msg.arg2 == mSetSubscriptionPlansIds.get(subId)) { + if (LOGD) Slog.d(TAG, "Clearing expired subscription plans."); + setSubscriptionPlansInternal(subId, new SubscriptionPlan[]{}, + 0 /* expirationDurationMillis */, + (String) msg.obj /* callingPackage */); + } else { + if (LOGD) Slog.d(TAG, "Ignoring stale CLEAR_SUBSCRIPTION_PLANS."); + } + } + } + return true; + } case MSG_BLOCKED_REASON_CHANGED: { final int uid = msg.arg1; final int newBlockedReasons = msg.arg2; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6b27321f856f..bc38087ebc73 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2463,7 +2463,7 @@ public class NotificationManagerService extends SystemService { }; mAllowFgsDismissal = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false); + SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, new HandlerExecutor(mHandler), diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 2e9ad50f23f6..2d870997bed7 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -32,9 +32,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.SigningDetails; -import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.component.ParsedApexSystemService; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.os.Binder; @@ -59,6 +56,9 @@ import com.android.modules.utils.build.UnboundedSdkLevel; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.android.server.pm.pkg.component.ParsedApexSystemService; +import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.TimingsTraceAndSlog; import com.google.android.collect.Lists; @@ -414,9 +414,11 @@ public abstract class ApexManager { throws PackageManagerException; /** - * Get a map of system services defined in an apex mapped to the jar files they reside in. + * Get a list of apex system services implemented in an apex. + * + * <p>The list is sorted by initOrder for consistency. */ - public abstract Map<String, String> getApexSystemServices(); + public abstract List<ApexSystemServiceInfo> getApexSystemServices(); /** * Dumps various state information to the provided {@link PrintWriter} object. @@ -449,7 +451,7 @@ public abstract class ApexManager { * Map of all apex system services to the jar files they are contained in. */ @GuardedBy("mLock") - private Map<String, String> mApexSystemServices = new ArrayMap<>(); + private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>(); /** * Contains the list of {@code packageName}s of apks-in-apex for given @@ -605,14 +607,19 @@ public abstract class ApexManager { } String name = service.getName(); - if (mApexSystemServices.containsKey(name)) { - throw new IllegalStateException(String.format( - "Duplicate apex-system-service %s from %s, %s", - name, mApexSystemServices.get(name), service.getJarPath())); + for (ApexSystemServiceInfo info : mApexSystemServices) { + if (info.getName().equals(name)) { + throw new IllegalStateException(String.format( + "Duplicate apex-system-service %s from %s, %s", + name, info.mJarPath, service.getJarPath())); + } } - mApexSystemServices.put(name, service.getJarPath()); + ApexSystemServiceInfo info = new ApexSystemServiceInfo( + service.getName(), service.getJarPath(), service.getInitOrder()); + mApexSystemServices.add(info); } + Collections.sort(mApexSystemServices); mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); if (ai.isActive) { if (activePackagesSet.contains(packageInfo.packageName)) { @@ -1133,7 +1140,7 @@ public abstract class ApexManager { } @Override - public Map<String, String> getApexSystemServices() { + public List<ApexSystemServiceInfo> getApexSystemServices() { synchronized (mLock) { Preconditions.checkState(mApexSystemServices != null, "APEX packages have not been scanned"); @@ -1423,10 +1430,10 @@ public abstract class ApexManager { } @Override - public Map<String, String> getApexSystemServices() { + public List<ApexSystemServiceInfo> getApexSystemServices() { // TODO(satayev): we can't really support flattened apex use case, and need to migrate // the manifest entries into system's manifest asap. - return Collections.emptyMap(); + return Collections.emptyList(); } @Override diff --git a/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java new file mode 100644 index 000000000000..f75ba6d165c9 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.Nullable; + +/** + * A helper class that contains information about apex-system-service to be used within system + * server process. + */ +public final class ApexSystemServiceInfo implements Comparable<ApexSystemServiceInfo> { + + final String mName; + @Nullable + final String mJarPath; + final int mInitOrder; + + public ApexSystemServiceInfo(String name, String jarPath, int initOrder) { + this.mName = name; + this.mJarPath = jarPath; + this.mInitOrder = initOrder; + } + + public String getName() { + return mName; + } + + public String getJarPath() { + return mJarPath; + } + + public int getInitOrder() { + return mInitOrder; + } + + @Override + public int compareTo(ApexSystemServiceInfo other) { + if (mInitOrder == other.mInitOrder) { + return mName.compareTo(other.mName); + } + // higher initOrder values take precedence + return -Integer.compare(mInitOrder, other.mInitOrder); + } +} diff --git a/services/core/java/com/android/server/pm/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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b07cd1063ed2..652080a3f11d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2515,13 +2515,17 @@ public class UserManagerService extends IUserManager.Stub { return 0 < getRemainingCreatableProfileCount(userType, userId, allowedToRemoveOne); } + @Override + public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId) { + return getRemainingCreatableProfileCount(userType, userId, false); + } + /** * Returns the remaining number of profiles of the given type that can be added to the given * user. (taking into account the total number of users on the device as well as how many * profiles exist of that type both in general and for the given user) */ - @Override - public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId, + private int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId, boolean allowedToRemoveOne) { checkQueryOrCreateUsersPermission( "get the remaining number of profiles that can be added to the given user."); 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 d0bbe3d04bdb..698068dd8091 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/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java index 586d2c48d27d..cf478b1da2e4 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java @@ -34,4 +34,7 @@ public interface ParsedApexSystemService { @Nullable String getMaxSdkVersion(); + + int getInitOrder(); + } diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java index 1e427d015cc1..167aba301f35 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java @@ -48,18 +48,18 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @Nullable private String maxSdkVersion; + private int initOrder; + public ParsedApexSystemServiceImpl() { } - - // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -71,13 +71,15 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @NonNull String name, @Nullable String jarPath, @Nullable String minSdkVersion, - @Nullable String maxSdkVersion) { + @Nullable String maxSdkVersion, + int initOrder) { this.name = name; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, name); this.jarPath = jarPath; this.minSdkVersion = minSdkVersion; this.maxSdkVersion = maxSdkVersion; + this.initOrder = initOrder; // onConstructed(); // You can define this method to get a callback } @@ -103,6 +105,11 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @DataClass.Generated.Member + public int getInitOrder() { + return initOrder; + } + + @DataClass.Generated.Member public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) { name = value; com.android.internal.util.AnnotationValidations.validate( @@ -129,6 +136,12 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @DataClass.Generated.Member + public @NonNull ParsedApexSystemServiceImpl setInitOrder( int value) { + initOrder = value; + return this; + } + + @DataClass.Generated.Member static Parcelling<String> sParcellingForName = Parcelling.Cache.get( Parcelling.BuiltIn.ForInternedString.class); @@ -187,6 +200,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par sParcellingForJarPath.parcel(jarPath, dest, flags); sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags); sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags); + dest.writeInt(initOrder); } @Override @@ -205,6 +219,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par String _jarPath = sParcellingForJarPath.unparcel(in); String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in); String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in); + int _initOrder = in.readInt(); this.name = _name; com.android.internal.util.AnnotationValidations.validate( @@ -212,6 +227,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par this.jarPath = _jarPath; this.minSdkVersion = _minSdkVersion; this.maxSdkVersion = _maxSdkVersion; + this.initOrder = _initOrder; // onConstructed(); // You can define this method to get a callback } @@ -231,10 +247,10 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par }; @DataClass.Generated( - time = 1641431950080L, + time = 1643723578605L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java", - inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") + sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java index 38a6f5a356e7..ed9aa2e6860a 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java @@ -53,10 +53,13 @@ public class ParsedApexSystemServiceUtils { R.styleable.AndroidManifestApexSystemService_minSdkVersion); String maxSdkVersion = sa.getString( R.styleable.AndroidManifestApexSystemService_maxSdkVersion); + int initOrder = sa.getInt(R.styleable.AndroidManifestApexSystemService_initOrder, 0); systemService.setName(className) .setMinSdkVersion(minSdkVersion) - .setMaxSdkVersion(maxSdkVersion); + .setMaxSdkVersion(maxSdkVersion) + .setInitOrder(initOrder); + if (!TextUtils.isEmpty(jarPath)) { systemService.setJarPath(jarPath); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7dd942507a16..7e3629025142 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -27,6 +27,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; @@ -129,6 +130,7 @@ import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IAudioService; import android.media.session.MediaSessionLegacyHelper; +import android.os.BatteryManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.DeviceIdleManager; @@ -392,11 +394,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; + BatteryManagerInternal mBatteryManagerInternal; AudioManagerInternal mAudioManagerInternal; DisplayManager mDisplayManager; DisplayManagerInternal mDisplayManagerInternal; boolean mPreloadedRecentApps; - final Object mServiceAquireLock = new Object(); + final Object mServiceAcquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes SearchManager mSearchManager; AccessibilityManager mAccessibilityManager; @@ -782,7 +785,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onWakeUp() { synchronized (mLock) { - if (shouldEnableWakeGestureLp()) { + if (shouldEnableWakeGestureLp() + && mBatteryManagerInternal.getPlugType() != BATTERY_PLUGGED_WIRELESS) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, @@ -811,7 +815,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } IStatusBarService getStatusBarService() { - synchronized (mServiceAquireLock) { + synchronized (mServiceAcquireLock) { if (mStatusBarService == null) { mStatusBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService("statusbar")); @@ -821,7 +825,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } StatusBarManagerInternal getStatusBarManagerInternal() { - synchronized (mServiceAquireLock) { + synchronized (mServiceAcquireLock) { if (mStatusBarManagerInternal == null) { mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); @@ -831,7 +835,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } AudioManagerInternal getAudioManagerInternal() { - synchronized (mServiceAquireLock) { + synchronized (mServiceAcquireLock) { if (mAudioManagerInternal == null) { mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); } @@ -839,6 +843,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + BatteryManagerInternal getBatteryManagerInternal() { + synchronized (mServiceAcquireLock) { + if (mBatteryManagerInternal == null) { + mBatteryManagerInternal = + LocalServices.getService(BatteryManagerInternal.class); + } + return mBatteryManagerInternal; + } + } // returns true if the key was handled and should not be passed to the user private boolean backKeyPress() { diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java new file mode 100644 index 000000000000..750d4004cb60 --- /dev/null +++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java @@ -0,0 +1,125 @@ +/* + * 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.sensorprivacy; + +import android.app.AppOpsManager; +import android.content.Context; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; +import android.permission.PermissionManager; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.server.FgThread; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener { + + private final Context mContext; + private final LightsManager mLightsManager; + + private final Set<String> mActivePackages = new ArraySet<>(); + private final Set<String> mActivePhonePackages = new ArraySet<>(); + + private final int mCameraPrivacyLightColor; + + private final List<Light> mCameraLights = new ArrayList<>(); + private final AppOpsManager mAppOpsManager; + + private LightsManager.LightsSession mLightsSession = null; + + CameraPrivacyLightController(Context context) { + mContext = context; + + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mLightsManager = mContext.getSystemService(LightsManager.class); + + mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light); + + List<Light> lights = mLightsManager.getLights(); + for (int i = 0; i < lights.size(); i++) { + Light light = lights.get(i); + if (light.getType() == Light.LIGHT_TYPE_CAMERA) { + mCameraLights.add(light); + } + } + + if (mCameraLights.isEmpty()) { + return; + } + + mAppOpsManager.startWatchingActive( + new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA}, + FgThread.getExecutor(), this); + } + + @Override + public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { + final Set<String> activePackages; + if (AppOpsManager.OPSTR_CAMERA.equals(op)) { + activePackages = mActivePackages; + } else if (AppOpsManager.OPSTR_PHONE_CALL_CAMERA.equals(op)) { + activePackages = mActivePhonePackages; + } else { + return; + } + + if (active) { + activePackages.add(packageName); + } else { + activePackages.remove(packageName); + } + + updateLightSession(); + } + + private void updateLightSession() { + Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext); + + boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages) + && exemptedPackages.containsAll(mActivePhonePackages); + + if (shouldSessionEnd) { + if (mLightsSession == null) { + return; + } + + mLightsSession.close(); + mLightsSession = null; + } else { + if (mLightsSession != null) { + return; + } + + LightsRequest.Builder requestBuilder = new LightsRequest.Builder(); + for (int i = 0; i < mCameraLights.size(); i++) { + requestBuilder.addLight(mCameraLights.get(i), + new LightState.Builder() + .setColor(mCameraPrivacyLightColor) + .build()); + } + + mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE); + mLightsSession.requestLights(requestBuilder.build()); + } + } +} diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index e9b5f1156d39..040fffa885f1 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -154,6 +154,8 @@ public final class SensorPrivacyService extends SystemService { private final AppOpsManagerInternal mAppOpsManagerInternal; private final TelephonyManager mTelephonyManager; + private CameraPrivacyLightController mCameraPrivacyLightController; + private final IBinder mAppOpsRestrictionToken = new Binder(); private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal; @@ -190,6 +192,8 @@ public final class SensorPrivacyService extends SystemService { if (phase == PHASE_SYSTEM_SERVICES_READY) { mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mEmergencyCallHelper = new EmergencyCallHelper(); + } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { + mCameraPrivacyLightController = new CameraPrivacyLightController(mContext); } } diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java index 21d4cbbbcca7..f4b335e42ec5 100644 --- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java +++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java @@ -365,6 +365,20 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn } @Override + public void onSegmentResults(Bundle results) throws RemoteException { + mRemoteListener.onSegmentResults(results); + } + + @Override + public void onEndOfSegmentedSession() throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#onEndOfSegmentedSession invoked for a recognition session"); + } + mOnSessionComplete.run(); + mRemoteListener.onEndOfSegmentedSession(); + } + + @Override public void onEvent(int eventType, Bundle params) throws RemoteException { mRemoteListener.onEvent(eventType, params); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index ee2cc7bd7486..56985af9ef9f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2591,8 +2591,20 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ @Override public void setWallpaperDimAmount(float dimAmount) throws RemoteException { + setWallpaperDimAmountForUid(Binder.getCallingUid(), dimAmount); + } + + /** + * Sets wallpaper dim amount for a given UID. This only applies to FLAG_SYSTEM wallpaper as the + * lock screen does not have a wallpaper component, so we use mWallpaperMap. + * + * @param uid Caller UID that wants to set the wallpaper dim amount + * @param dimAmount Dim amount where 0f reverts any dimming applied by the caller (fully bright) + * and 1f is fully black + * @throws RemoteException + */ + public void setWallpaperDimAmountForUid(int uid, float dimAmount) { checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT); - int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java index fc827b40f3a1..458bf20877f6 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java @@ -43,6 +43,8 @@ public class WallpaperManagerShellCommand extends ShellCommand { switch(cmd) { case "set-dim-amount": return setWallpaperDimAmount(); + case "dim-with-uid": + return setDimmingWithUid(); case "get-dim-amount": return getWallpaperDimAmount(); case "-h": @@ -64,6 +66,10 @@ public class WallpaperManagerShellCommand extends ShellCommand { pw.println(" set-dim-amount DIMMING"); pw.println(" Sets the current dimming value to DIMMING (a number between 0 and 1)."); pw.println(); + pw.println(" dim-with-uid UID DIMMING"); + pw.println(" Sets the wallpaper dim amount to DIMMING as if an app with uid, UID, " + + "called it."); + pw.println(); pw.println(" get-dim-amount"); pw.println(" Get the current wallpaper dim amount."); } @@ -92,4 +98,17 @@ public class WallpaperManagerShellCommand extends ShellCommand { getOutPrintWriter().println("The current wallpaper dim amount is: " + dimAmount); return 0; } + + /** + * Sets the wallpaper dim amount for an arbitrary UID to simulate multiple applications setting + * a dim amount on the wallpaper. + */ + private int setDimmingWithUid() { + int mockUid = Integer.parseInt(getNextArgRequired()); + float mockDimAmount = Float.parseFloat(getNextArgRequired()); + mService.setWallpaperDimAmountForUid(mockUid, mockDimAmount); + getOutPrintWriter().println("Dimming the wallpaper for UID: " + mockUid + " to: " + + mockDimAmount); + return 0; + } } 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/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java index 0cd36808f8de..f3be66c5e6da 100644 --- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java +++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java @@ -106,14 +106,18 @@ final class PackageConfigurationUpdaterImpl implements private void updateConfig(int uid, String packageName) { final ArraySet<WindowProcessController> processes = mAtm.mProcessMap.getProcesses(uid); - if (processes == null) return; + if (processes == null || processes.isEmpty()) return; + LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists( + mLocales, mAtm.getGlobalConfiguration().getLocales()); for (int i = processes.size() - 1; i >= 0; i--) { final WindowProcessController wpc = processes.valueAt(i); - if (!wpc.mInfo.packageName.equals(packageName)) continue; - LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists( - mLocales, mAtm.getGlobalConfiguration().getLocales()); - wpc.applyAppSpecificConfig(mNightMode, localesOverride); - wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride); + if (wpc.mInfo.packageName.equals(packageName)) { + wpc.applyAppSpecificConfig(mNightMode, localesOverride); + } + // Always inform individual activities about the update, since activities from other + // packages may be sharing this process + wpc.updateAppSpecificSettingsForAllActivitiesInPackage(packageName, mNightMode, + localesOverride); } } 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/Transition.java b/services/core/java/com/android/server/wm/Transition.java index ad4594873cf0..155db2c58654 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1082,6 +1082,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) { final ChangeInfo parentChange = changes.get(p); if (parentChange == null || !parentChange.hasChanged(p)) break; + if (p.mRemoteToken == null) { + // Intermediate parents must be those that has window to be managed by Shell. + continue; + } if (parentChange.mParent != null && !skipIntermediateReports) { changes.get(wc).mParent = p; // The chain above the parent was processed. diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java index 90f5b09968ea..975b21c6f02c 100644 --- a/services/core/java/com/android/server/wm/OverlayHost.java +++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java @@ -32,14 +32,20 @@ import java.util.ArrayList; * * Also handles multiplexing of event dispatch and tracking of overlays * to make things easier for WindowContainer. + * + * These overlays are to be used for various types of System UI and UI + * under the systems control. Provided SurfacePackages will be able + * to overlay application content, without engaging the usual cross process + * obscured touch filtering mechanisms. It's imperative that all UI provided + * be under complete control of the system. */ -class OverlayHost { +class TrustedOverlayHost { // Lazily initialized when required SurfaceControl mSurfaceControl; final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>(); final WindowManagerService mWmService; - OverlayHost(WindowManagerService wms) { + TrustedOverlayHost(WindowManagerService wms) { mWmService = wms; } @@ -51,6 +57,8 @@ class OverlayHost { .setName("Overlay Host Leash"); mSurfaceControl = b.build(); + SurfaceControl.Transaction t = mWmService.mTransactionFactory.get(); + t.setTrustedOverlay(mSurfaceControl, true).apply(); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 1bd153b2a577..8a373bf5c09c 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -314,7 +314,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< private final List<WindowContainerListener> mListeners = new ArrayList<>(); - protected OverlayHost mOverlayHost; + protected TrustedOverlayHost mOverlayHost; WindowContainer(WindowManagerService wms) { mWmService = wms; @@ -3600,9 +3600,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @AnimationType int type, @Nullable AnimationAdapter snapshotAnim); } - void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) { + void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { if (mOverlayHost == null) { - mOverlayHost = new OverlayHost(mWmService); + mOverlayHost = new TrustedOverlayHost(mWmService); } mOverlayHost.addOverlay(overlay, mSurfaceControl); @@ -3613,11 +3613,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< overlay.getRemoteInterface().onConfigurationChanged(getConfiguration()); } catch (Exception e) { Slog.e(TAG, "Error sending initial configuration change to WindowContainer overlay"); - removeOverlay(overlay); + removeTrustedOverlay(overlay); } } - void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) { + void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) { mOverlayHost.release(); mOverlayHost = null; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 4900f9292f2a..9585a4b93a97 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -827,6 +827,11 @@ public abstract class WindowManagerInternal { * Internal methods for other parts of SystemServer to manage * SurfacePackage based overlays on tasks. * + * Since these overlays will overlay application content, they exist + * in a container with setTrustedOverlay(true). This means its imperative + * that this overlay feature only be used with UI completely under the control + * of the system, without 3rd party content. + * * Callers prepare a view hierarchy with SurfaceControlViewHost * and send the package to WM here. The remote view hierarchy will receive * configuration change, lifecycle events, etc, forwarded over the @@ -837,8 +842,10 @@ public abstract class WindowManagerInternal { * The embedded hierarchy exists in a coordinate space relative to the task * bounds. */ - public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay); - public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay); + public abstract void addTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay); + public abstract void removeTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay); /** * Get a SurfaceControl that is the container layer that should be used to receive input to diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5751777fc2cd..c9c3f1da5b40 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; +import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; @@ -38,6 +39,7 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; +import static android.os.Process.myUid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; @@ -462,11 +464,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. @@ -711,6 +708,7 @@ public class WindowManagerService extends IWindowManager.Stub final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2; int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE; + /** Indicates that the system server is actively demanding the screen be frozen. */ boolean mClientFreezingScreen = false; int mAppsFreezingScreen = 0; @@ -811,8 +809,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 +849,6 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mRenderShadowsInCompositorUri.equals(uri)) { - setShadowRenderer(); - return; - } - if (mDisplaySettingsPathUri.equals(uri)) { updateDisplaySettingsLocation(); return; @@ -972,11 +963,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; @@ -1202,7 +1188,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_hasPermanentDpad); mInTouchMode = context.getResources().getBoolean( com.android.internal.R.bool.config_defaultInTouchMode); - inputManager.setInTouchMode(mInTouchMode); + inputManager.setInTouchMode( + mInTouchMode, myPid(), myUid(), /* hasPermission = */ true); mDrawLockTimeoutMillis = context.getResources().getInteger( com.android.internal.R.integer.config_drawLockTimeoutMillis); mAllowAnimationsInLowPowerMode = context.getResources().getBoolean( @@ -1395,7 +1382,6 @@ public class WindowManagerService extends IWindowManager.Stub float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha}; SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ, lightRadius); - setShadowRenderer(); } /** @@ -2669,7 +2655,6 @@ public class WindowManagerService extends IWindowManager.Stub } boolean checkCallingPermission(String permission, String func, boolean printLog) { - // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == myPid()) { return true; } @@ -3109,6 +3094,7 @@ public class WindowManagerService extends IWindowManager.Stub // Misc IWindowSession methods // ------------------------------------------------------------- + /** Freeze the screen during a user-switch event. Called by UserController. */ @Override public void startFreezingScreen(int exitAnim, int enterAnim) { if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, @@ -3131,6 +3117,11 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * No longer actively demand that the screen remain frozen. + * Called by UserController after a user-switch. + * This doesn't necessarily immediately unlock the screen; it just allows it if we're ready. + */ @Override public void stopFreezingScreen() { if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN, @@ -3703,12 +3694,33 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override + /** + * Sets the touch mode state. + * + * To be able to change touch mode state, the caller must either own the focused window, or must + * have the MODIFY_TOUCH_MODE_STATE permission. + * + * @param mode the touch mode to set + */ + @Override // Binder call public void setInTouchMode(boolean mode) { synchronized (mGlobalLock) { - mInTouchMode = mode; + if (mInTouchMode == mode) { + return; + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final boolean hasPermission = checkCallingPermission(MODIFY_TOUCH_MODE_STATE, + "setInTouchMode()"); + final long token = Binder.clearCallingIdentity(); + try { + if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) { + mInTouchMode = mode; + } + } finally { + Binder.restoreCallingIdentity(token); + } } - mInputManager.setInTouchMode(mode); } boolean getInTouchMode() { @@ -5881,6 +5893,13 @@ public class WindowManagerService extends IWindowManager.Stub return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStartFreezingDisplay"); + doStartFreezingDisplay(exitAnim, enterAnim, displayContent, overrideOriginalRotation); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent, + int overrideOriginalRotation) { ProtoLog.d(WM_DEBUG_ORIENTATION, "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s", exitAnim, enterAnim, Debug.getCallers(8)); @@ -5951,10 +5970,16 @@ public class WindowManagerService extends IWindowManager.Stub return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStopFreezingDisplayLocked-" + + mLastFinishedFreezeSource); + doStopFreezingDisplayLocked(displayContent); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + + private void doStopFreezingDisplayLocked(DisplayContent displayContent) { ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Unfreezing now"); - // We must make a local copy of the displayId as it can be potentially overwritten later on // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result // of update rotation, but we reference the frozen display after that call in this method. @@ -7987,27 +8012,29 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) { return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken); - } + } @Override - public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) { + public void addTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay) { synchronized (mGlobalLock) { final Task task = mRoot.getRootTask(taskId); if (task == null) { throw new IllegalArgumentException("no task with taskId" + taskId); } - task.addOverlay(overlay); + task.addTrustedOverlay(overlay); } } @Override - public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) { + public void removeTrustedTaskOverlay(int taskId, + SurfaceControlViewHost.SurfacePackage overlay) { synchronized (mGlobalLock) { final Task task = mRoot.getRootTask(taskId); if (task == null) { throw new IllegalArgumentException("no task with taskId" + taskId); } - task.removeOverlay(overlay); + task.removeTrustedOverlay(overlay); } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 3ccb06ccef15..ac9fbdedf43e 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -821,10 +821,15 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // TODO(b/199277065): Re-assess how app-specific locales are applied based on UXR // TODO(b/199277729): Consider whether we need to add special casing for edge cases like // activity-embeddings etc. - void updateAppSpecificSettingsForAllActivities(Integer nightMode, LocaleList localesOverride) { + void updateAppSpecificSettingsForAllActivitiesInPackage(String packageName, Integer nightMode, + LocaleList localesOverride) { for (int i = mActivities.size() - 1; i >= 0; --i) { final ActivityRecord r = mActivities.get(i); - if (r.applyAppSpecificConfig(nightMode, localesOverride) && r.mVisibleRequested) { + // Activities from other packages could be sharing this process. Only propagate updates + // to those activities that are part of the package whose app-specific settings changed + if (packageName.equals(r.packageName) + && r.applyAppSpecificConfig(nightMode, localesOverride) + && r.mVisibleRequested) { r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */); } } 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/Android.bp b/services/core/jni/Android.bp index 99abf440910e..f9de53ce8016 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -44,7 +44,6 @@ cc_library_static { "com_android_server_lights_LightsService.cpp", "com_android_server_location_GnssLocationProvider.cpp", "com_android_server_locksettings_SyntheticPasswordManager.cpp", - "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", "com_android_server_powerstats_PowerStatsService.cpp", "com_android_server_hint_HintManagerService.cpp", @@ -71,7 +70,6 @@ cc_library_static { "com_android_server_wm_TaskFpsCallbackController.cpp", "onload.cpp", ":lib_cachedAppOptimizer_native", - ":lib_networkStatsFactory_native", ":lib_gameManagerService_native", ], @@ -86,7 +84,6 @@ cc_library_static { header_libs: [ "bionic_libc_platform_headers", - "bpf_connectivity_headers", ], } @@ -145,9 +142,6 @@ cc_defaults { "libhidlbase", "libutils", "libhwui", - "libbpf_android", - "libnetdutils", - "libnetworkstats", "libpsi", "libdataloader", "libincfs", @@ -214,13 +208,6 @@ cc_defaults { } filegroup { - name: "lib_networkStatsFactory_native", - srcs: [ - "com_android_server_net_NetworkStatsFactory.cpp", - ], -} - -filegroup { name: "lib_cachedAppOptimizer_native", srcs: [ "com_android_server_am_CachedAppOptimizer.cpp", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 8cb27e179c19..3c122b03d0d1 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(); @@ -1707,7 +1704,6 @@ static void nativePilferPointers(JNIEnv* env, jclass /* clazz */, jlong ptr, job im->pilferPointers(token); } - static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jboolean enabled) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -1715,11 +1711,13 @@ static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, im->getInputManager()->getDispatcher().setInputFilterEnabled(enabled); } -static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jboolean inTouchMode) { +static jboolean nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, + jboolean inTouchMode, jint pid, jint uid, + jboolean hasPermission) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode); + return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid, + hasPermission); } static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */, @@ -2381,12 +2379,12 @@ 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}, {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled}, - {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode}, + {"nativeSetInTouchMode", "(JZIIZ)Z", (void*)nativeSetInTouchMode}, {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V", (void*)nativeSetMaximumObscuringOpacityForTouch}, {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode}, 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/jni/onload.cpp b/services/core/jni/onload.cpp index ba5b3f54efa1..1c6a3b5eb03f 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -51,8 +51,6 @@ int register_android_server_Watchdog(JNIEnv* env); int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); -int register_android_server_net_NetworkStatsFactory(JNIEnv* env); -int register_android_server_net_NetworkStatsService(JNIEnv* env); int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env); @@ -110,8 +108,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_SyntheticPasswordManager(env); register_android_graphics_GraphicsStatsService(env); register_android_hardware_display_DisplayViewport(env); - register_android_server_net_NetworkStatsFactory(env); - register_android_server_net_NetworkStatsService(env); register_android_server_am_CachedAppOptimizer(env); register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env); diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml index c5b054949bc5..69e13b38873a 100644 --- a/services/core/lint-baseline.xml +++ b/services/core/lint-baseline.xml @@ -1,158 +1,140 @@ <?xml version="1.0" encoding="UTF-8"?> -<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev"> +<issues format="5" by="lint 7.2.0-dev"> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" return Settings.Secure.getInt(context.getContentResolver()," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java" - line="1106" - column="20"/> + line="1122"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" return Settings.Secure.getInt(context.getContentResolver()," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java" - line="1111" - column="20"/> + line="1127"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. " - errorLine1=" return Settings.Secure.getString(mContentResolver, mKey);" - errorLine2=" ~~~~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/CertBlacklister.java" - line="73" - column="36"/> + file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java" + line="670"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" }" - errorLine2=" ^"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java" - line="973" - column="7"/> + file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java" + line="678"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" boolean accessibilityEnabled = Settings.Secure.getInt(cr," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/DockObserver.java" - line="176" - column="60"/> + file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java" + line="679"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. " - errorLine1=" String mediaButtonReceiverInfo = Settings.Secure.getString(mContentResolver," - errorLine2=" ~~~~~~~~~"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java" - line="928" - column="46"/> + file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java" + line="696"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" final boolean isSecureFrpEnabled =" - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java" - line="1959" - column="36"/> + file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java" + line="705"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" private int getUnknownSourcesSettings() {" - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java" - line="16741" - column="39"/> + file="frameworks/base/services/core/java/com/android/server/CertBlacklister.java" + line="73"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. " - errorLine1=" String inputMethodComponent = Settings.Secure.getString(mContext.getContentResolver()," - errorLine2=" ~~~~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location - file="frameworks/base/services/core/java/com/android/server/SensorPrivacyService.java" - line="489" - column="35"/> + file="frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java" + line="1072"/> + </issue> + + <issue + id="NonUserGetterCalled" + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> + <location + file="frameworks/base/services/core/java/com/android/server/DockObserver.java" + line="260"/> + </issue> + + <issue + id="NonUserGetterCalled" + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> + <location + file="frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java" + line="928"/> + </issue> + + <issue + id="NonUserGetterCalled" + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> + <location + file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java" + line="1902"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. " - errorLine1=" return Settings.Secure.getString(getContentResolverAsUser(userId), key);" - errorLine2=" ~~~~~~~~~"> + message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/connectivity/Vpn.java" - line="1994" - column="12"/> + line="2069"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" return Settings.Secure.getInt(getContentResolverAsUser(userId), key, def);" - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/connectivity/Vpn.java" - line="2001" - column="12"/> + line="2076"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" if (Settings.Secure.getInt(mContext.getContentResolver()," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java" - line="980" - column="45"/> + line="984"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" && Settings.Secure.getInt(mContext.getContentResolver()," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java" - line="1442" - column="36"/> + line="1446"/> </issue> <issue id="NonUserGetterCalled" - message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. " - errorLine1=" && Settings.Secure.getInt(mContext.getContentResolver()," - errorLine2=" ~~~~~~"> + message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "> <location file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java" - line="1444" - column="36"/> + line="1448"/> </issue> </issues> 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 7f4dcdcef44a..b9da2cdeb2e0 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; @@ -11613,6 +11615,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller) || isProfileOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); @@ -13988,7 +13991,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (isFinancedDeviceOwner(caller)) { - enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission); + enforcePermissionGrantStateOnFinancedDevice(packageName, permission); } long ident = mInjector.binderClearCallingIdentity(); try { @@ -14049,14 +14052,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void enforceCanSetPermissionGrantOnFinancedDevice( + private void enforcePermissionGrantStateOnFinancedDevice( String packageName, String permission) { if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) { - throw new SecurityException("Cannot grant " + permission - + " when managing a financed device"); + throw new SecurityException(permission + " cannot be used when managing a financed" + + " device for permission grant state"); } else if (!mOwners.getDeviceOwnerPackageName().equals(packageName)) { - throw new SecurityException("Cannot grant permission to a package that is not" - + " the device owner"); + throw new SecurityException("Device owner package is the only package that can be used" + + " for permission grant state when managing a financed device"); } } @@ -14065,10 +14068,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent() - && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); synchronized (getLockObject()) { + if (isFinancedDeviceOwner(caller)) { + enforcePermissionGrantStateOnFinancedDevice(packageName, permission); + } return mInjector.binderWithCleanCallingIdentity(() -> { int granted; if (getTargetSdk(caller.getPackageName(), caller.getUserId()) @@ -18669,4 +18676,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 d3ddb4726b01..03b1c53beaf8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -54,7 +54,6 @@ import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; import android.net.NetworkStackClient; -import android.net.TrafficStats; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -143,7 +142,6 @@ import com.android.server.media.MediaRouterService; import com.android.server.media.metrics.MediaMetricsManagerService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; -import com.android.server.net.NetworkStatsService; import com.android.server.net.watchlist.NetworkWatchlistService; import com.android.server.notification.NotificationManagerService; import com.android.server.oemlock.OemLockService; @@ -154,6 +152,7 @@ import com.android.server.os.NativeTombstoneManagerService; import com.android.server.os.SchedulingPolicyService; import com.android.server.people.PeopleService; import com.android.server.pm.ApexManager; +import com.android.server.pm.ApexSystemServiceInfo; import com.android.server.pm.CrossProfileAppsService; import com.android.server.pm.DataLoaderManagerService; import com.android.server.pm.DynamicCodeLoggingService; @@ -224,8 +223,8 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; +import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Timer; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; @@ -396,6 +395,8 @@ public final class SystemServer implements Dumpable { "com.android.server.media.MediaResourceMonitorService"; private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = "com.android.server.ConnectivityServiceInitializer"; + private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS = + "com.android.server.NetworkStatsServiceInitializer"; private static final String IP_CONNECTIVITY_METRICS_CLASS = "com.android.server.connectivity.IpConnectivityMetrics"; private static final String MEDIA_COMMUNICATION_SERVICE_CLASS = @@ -1394,7 +1395,6 @@ public final class SystemServer implements Dumpable { NetworkManagementService networkManagement = null; VpnManagerService vpnManager = null; VcnManagementService vcnManagement = null; - NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; WindowManagerService wm = null; SerialService serial = null; @@ -1471,7 +1471,7 @@ public final class SystemServer implements Dumpable { // TelecomLoader hooks into classes with defined HFP logic, // so check for either telephony or microphone. if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) || - mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { t.traceBegin("StartTelecomLoaderService"); mSystemServiceManager.startService(TelecomLoaderService.class); t.traceEnd(); @@ -1479,7 +1479,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartTelephonyRegistry"); telephonyRegistry = new TelephonyRegistry( - context, new TelephonyRegistry.ConfigurationProvider()); + context, new TelephonyRegistry.ConfigurationProvider()); ServiceManager.addService("telephony.registry", telephonyRegistry); t.traceEnd(); @@ -1504,6 +1504,10 @@ public final class SystemServer implements Dumpable { SQLiteCompatibilityWalFlags.reset(); t.traceEnd(); + t.traceBegin("UpdateWatchdogTimeout"); + Watchdog.getInstance().registerSettingsObserver(context); + t.traceEnd(); + // Records errors and logs, for example wtf() // Currently this service indirectly depends on SettingsProvider so do this after // InstallSystemProviders. @@ -1916,13 +1920,10 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("StartNetworkStatsService"); - try { - networkStats = NetworkStatsService.create(context); - ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); - TrafficStats.init(context); - } catch (Throwable e) { - reportWtf("starting NetworkStats Service", e); - } + // This has to be called before NetworkPolicyManager because NetworkPolicyManager + // needs to take NetworkStatsService to initialize. + mSystemServiceManager.startServiceFromJar(NETWORK_STATS_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); t.traceEnd(); t.traceBegin("StartNetworkPolicyManagerService"); @@ -2774,7 +2775,6 @@ public final class SystemServer implements Dumpable { // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; - final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; @@ -2873,15 +2873,6 @@ public final class SystemServer implements Dumpable { .networkScoreAndNetworkManagementServiceReady(); } t.traceEnd(); - t.traceBegin("MakeNetworkStatsServiceReady"); - try { - if (networkStatsF != null) { - networkStatsF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making Network Stats Service ready", e); - } - t.traceEnd(); t.traceBegin("MakeConnectivityServiceReady"); try { if (connectivityF != null) { @@ -2997,7 +2988,9 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("MakeTelephonyRegistryReady"); try { - if (telephonyRegistryF != null) telephonyRegistryF.systemRunning(); + if (telephonyRegistryF != null) { + telephonyRegistryF.systemRunning(); + } } catch (Throwable e) { reportWtf("Notifying TelephonyRegistry running", e); } @@ -3062,10 +3055,12 @@ public final class SystemServer implements Dumpable { */ private void startApexServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startApexServices"); - Map<String, String> services = ApexManager.getInstance().getApexSystemServices(); - // TODO(satayev): introduce android:order for services coming the same apexes - for (String name : new TreeSet<>(services.keySet())) { - String jarPath = services.get(name); + // TODO(b/192880996): get the list from "android" package, once the manifest entries + // are migrated to system manifest. + List<ApexSystemServiceInfo> services = ApexManager.getInstance().getApexSystemServices(); + for (ApexSystemServiceInfo info : services) { + String name = info.getName(); + String jarPath = info.getJarPath(); t.traceBegin("starting " + name); if (TextUtils.isEmpty(jarPath)) { mSystemServiceManager.startService(name); @@ -3166,11 +3161,6 @@ public final class SystemServer implements Dumpable { } private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) { - if (!AmbientContextManagerService.isDetectionServiceConfigured()) { - Slog.d(TAG, "AmbientContextDetectionService is not configured on this device"); - return; - } - t.traceBegin("StartAmbientContextService"); mSystemServiceManager.startService(AmbientContextManagerService.class); t.traceEnd(); diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp index 16d624199d5a..0a9b7b1302cc 100644 --- a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp +++ b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp @@ -32,7 +32,7 @@ apex_test { name: "test_com.android.server", manifest: "manifest.json", androidManifest: "AndroidManifest.xml", - java_libs: ["FakeApexSystemService"], + java_libs: ["FakeApexSystemServices"], file_contexts: ":apex.test-file_contexts", key: "test_com.android.server.key", updatable: false, diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml index eb741cad4ad9..6bec28463dc3 100644 --- a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml +++ b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml @@ -21,21 +21,29 @@ <application android:hasCode="false" android:testOnly="true"> <apex-system-service android:name="com.android.server.testing.FakeApexSystemService" - android:path="/apex/test_com.android.server/javalib/FakeApexSystemService.jar" - android:minSdkVersion="30"/> + android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar" + android:minSdkVersion="30" + /> + + <apex-system-service + android:name="com.android.server.testing.FakeApexSystemService2" + android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar" + android:minSdkVersion="30" + android:initOrder="1" + /> <!-- Always inactive system service, since maxSdkVersion is low --> <apex-system-service - android:name="com.android.apex.test.OldApexSystemService" - android:path="/apex/com.android.apex.test/javalib/fake.jar" + android:name="com.android.server.testing.OldApexSystemService" + android:path="/apex/test_com.android.server/javalib/fake.jar" android:minSdkVersion="1" android:maxSdkVersion="1" /> <!-- Always inactive system service, since minSdkVersion is high --> <apex-system-service - android:name="com.android.apex.test.NewApexSystemService" - android:path="/apex/com.android.apex.test/javalib/fake.jar" + android:name="com.android.server.testing.NewApexSystemService" + android:path="/apex/test_com.android.server/javalib/fake.jar" android:minSdkVersion="999999" /> </application> diff --git a/services/tests/apexsystemservices/service/Android.bp b/services/tests/apexsystemservices/services/Android.bp index 9d04f39f2237..477ea4cdad37 100644 --- a/services/tests/apexsystemservices/service/Android.bp +++ b/services/tests/apexsystemservices/services/Android.bp @@ -8,7 +8,7 @@ package { } java_library { - name: "FakeApexSystemService", + name: "FakeApexSystemServices", srcs: ["**/*.java"], sdk_version: "system_server_current", libs: [ diff --git a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java index 4947c3455cc7..4947c3455cc7 100644 --- a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java +++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java diff --git a/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java new file mode 100644 index 000000000000..e83343b9c996 --- /dev/null +++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testing; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.server.SystemService; + +/** + * A fake system service that just logs when it is started. + */ +public class FakeApexSystemService2 extends SystemService { + + private static final String TAG = "FakeApexSystemService"; + + public FakeApexSystemService2(@NonNull Context context) { + super(context); + } + + @Override + public void onStart() { + Log.d(TAG, "FakeApexSystemService2 onStart"); + } +} diff --git a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java index 2b453a9265bb..10635a138eb9 100644 --- a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java +++ b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java @@ -37,9 +37,15 @@ import org.junit.rules.RuleChain; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + @RunWith(DeviceJUnit4ClassRunner.class) public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { + private static final int REBOOT_TIMEOUT = 1 * 60 * 1000; + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); private final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice); @@ -67,7 +73,7 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @Test - public void noApexSystemServerStartsWithoutApex() throws Exception { + public void testNoApexSystemServiceStartsWithoutApex() throws Exception { mPreparer.reboot(); assertThat(getFakeApexSystemServiceLogcat()) @@ -75,20 +81,55 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @Test - public void apexSystemServerStarts() throws Exception { + public void testApexSystemServiceStarts() throws Exception { // Pre-install the apex String apex = "test_com.android.server.apex"; mPreparer.pushResourceFile(apex, "/system/apex/" + apex); // Reboot activates the apex mPreparer.reboot(); + mDevice.waitForBootComplete(REBOOT_TIMEOUT); + assertThat(getFakeApexSystemServiceLogcat()) .contains("FakeApexSystemService onStart"); } + @Test + public void testInitOrder() throws Exception { + // Pre-install the apex + String apex = "test_com.android.server.apex"; + mPreparer.pushResourceFile(apex, "/system/apex/" + apex); + // Reboot activates the apex + mPreparer.reboot(); + + mDevice.waitForBootComplete(REBOOT_TIMEOUT); + + assertThat(getFakeApexSystemServiceLogcat().lines() + .map(ApexSystemServicesTestCases::getDebugMessage) + .filter(Objects::nonNull) + .collect(Collectors.toList())) + .containsExactly( + // Second service has a higher initOrder and must be started first + "FakeApexSystemService2 onStart", + "FakeApexSystemService onStart" + ) + .inOrder(); + } + private String getFakeApexSystemServiceLogcat() throws DeviceNotAvailableException { return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "FakeApexSystemService:D", "*:S"); } + private static final Pattern DEBUG_MESSAGE = + Pattern.compile("(FakeApexSystemService[0-9]* onStart)"); + + private static String getDebugMessage(String logcatLine) { + return DEBUG_MESSAGE.matcher(logcatLine) + .results() + .map(m -> m.group(1)) + .findFirst() + .orElse(null); + } + } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 7f571195f5f8..ed232e5458b0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -406,7 +406,7 @@ public final class GameServiceProviderInstanceImplTest { mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); - verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10)); + verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10)); } @Test @@ -556,8 +556,9 @@ public final class GameServiceProviderInstanceImplTest { stopTask(10); - verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10)); - verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10)); + verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10)); + verify(mMockWindowManagerInternal).removeTrustedTaskOverlay(eq(10), + eq(mockSurfacePackage10)); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java new file mode 100644 index 000000000000..fa3e05a6b001 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java @@ -0,0 +1,248 @@ +/* + * 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.sensorprivacy; + +import static android.app.AppOpsManager.OPSTR_CAMERA; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; + +import android.app.AppOpsManager; +import android.content.Context; +import android.hardware.lights.Light; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; +import android.permission.PermissionManager; +import android.util.ArraySet; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +public class CameraPrivacyLightControllerTest { + + private MockitoSession mMockitoSession; + + @Mock + private Context mContext; + + @Mock + private LightsManager mLightsManager; + + @Mock + private AppOpsManager mAppOpsManager; + + @Mock + private LightsManager.LightsSession mLightsSession; + + private ArgumentCaptor<AppOpsManager.OnOpActiveChangedListener> mAppOpsListenerCaptor = + ArgumentCaptor.forClass(AppOpsManager.OnOpActiveChangedListener.class); + + private ArgumentCaptor<LightsRequest> mLightsRequestCaptor = + ArgumentCaptor.forClass(LightsRequest.class); + + private Set<String> mExemptedPackages = new ArraySet<>(); + private List<Light> mLights = new ArrayList<>(); + + private int mNextLightId = 1; + + @Before + public void setUp() { + mMockitoSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .strictness(Strictness.WARN) + .spyStatic(PermissionManager.class) + .startMocking(); + + doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class); + doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); + + doReturn(mLights).when(mLightsManager).getLights(); + doReturn(mLightsSession).when(mLightsManager).openSession(anyInt()); + + doReturn(mExemptedPackages) + .when(() -> PermissionManager.getIndicatorExemptedPackages(any())); + } + + @After + public void tearDown() { + mExemptedPackages.clear(); + mLights.clear(); + + mMockitoSession.finishMocking(); + } + + @Test + public void testAppsOpsListenerNotRegisteredWithoutCameraLights() { + mLights.add(getNextLight(false)); + new CameraPrivacyLightController(mContext); + + verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any()); + } + + @Test + public void testAppsOpsListenerRegisteredWithCameraLight() { + mLights.add(getNextLight(true)); + + new CameraPrivacyLightController(mContext); + + verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any()); + } + + @Test + public void testAllCameraLightsAreRequestedOnOpActive() { + Random r = new Random(0); + for (int i = 0; i < 30; i++) { + mLights.add(getNextLight(r.nextBoolean())); + } + + new CameraPrivacyLightController(mContext); + + // Verify no session has been opened at this point. + verify(mLightsManager, times(0)).openSession(anyInt()); + + // Set camera op as active. + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + + // Verify session has been opened exactly once + verify(mLightsManager, times(1)).openSession(anyInt()); + + verify(mLightsSession).requestLights(mLightsRequestCaptor.capture()); + assertEquals("requestLights() not invoked exactly once", + 1, mLightsRequestCaptor.getAllValues().size()); + + List<Integer> expectedCameraLightIds = mLights.stream() + .filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA) + .map(l -> l.getId()) + .collect(Collectors.toList()); + List<Integer> lightsRequestLightIds = mLightsRequestCaptor.getValue().getLights(); + + // We don't own lights framework, don't assume it will retain order + lightsRequestLightIds.sort(Integer::compare); + expectedCameraLightIds.sort(Integer::compare); + + assertEquals(expectedCameraLightIds, lightsRequestLightIds); + } + + @Test + public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() { + mLights.add(getNextLight(true)); + + new CameraPrivacyLightController(mContext); + + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + + AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); + listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + verify(mLightsManager, times(1)).openSession(anyInt()); + listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true); + verify(mLightsManager, times(1)).openSession(anyInt()); + } + + @Test + public void testWillCloseOnFinishOp() { + mLights.add(getNextLight(true)); + + new CameraPrivacyLightController(mContext); + + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + + AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); + listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + + verify(mLightsSession, times(0)).close(); + listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false); + verify(mLightsSession, times(1)).close(); + } + + @Test + public void testWillCloseOnFinishOpForAllPackages() { + mLights.add(getNextLight(true)); + + new CameraPrivacyLightController(mContext); + + int numUids = 100; + List<Integer> uids = new ArrayList<>(numUids); + for (int i = 0; i < numUids; i++) { + uids.add(10001 + i); + } + + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + + AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); + + for (int i = 0; i < numUids; i++) { + listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true); + } + + // Change the order which their ops are finished + Collections.shuffle(uids, new Random(0)); + + for (int i = 0; i < numUids - 1; i++) { + listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false); + } + + verify(mLightsSession, times(0)).close(); + int lastUid = uids.get(numUids - 1); + listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false); + verify(mLightsSession, times(1)).close(); + } + + @Test + public void testWontOpenForExemptedPackage() { + mLights.add(getNextLight(true)); + mExemptedPackages.add("pkg1"); + + new CameraPrivacyLightController(mContext); + + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + + AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); + listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + verify(mLightsManager, times(0)).openSession(anyInt()); + } + + private Light getNextLight(boolean cameraType) { + Light light = ExtendedMockito.mock(Light.class); + if (cameraType) { + doReturn(Light.LIGHT_TYPE_CAMERA).when(light).getType(); + } else { + doReturn(Light.LIGHT_TYPE_MICROPHONE).when(light).getType(); + } + doReturn(mNextLightId++).when(light).getId(); + return light; + } +} diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index e1aa08d9176a..51607e528216 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -29,6 +29,8 @@ import static org.mockito.AdditionalAnswers.returnsArgAt; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.longThat; import static org.mockito.Mockito.doAnswer; @@ -38,6 +40,8 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.IActivityManager; +import android.app.usage.StorageStats; +import android.app.usage.StorageStatsManager; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.UsageEventListener; @@ -48,6 +52,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; @@ -68,10 +73,12 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.Executor; /** @@ -104,6 +111,8 @@ public final class AppHibernationServiceTest { @Mock private UserManager mUserManager; @Mock + private StorageStatsManager mStorageStatsManager; + @Mock private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore; @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @@ -115,7 +124,7 @@ public final class AppHibernationServiceTest { private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor; @Before - public void setUp() throws RemoteException { + public void setUp() throws RemoteException, PackageManager.NameNotFoundException, IOException { // Share class loader to allow access to package-private classes System.setProperty("dexmaker.share_classloader", "true"); MockitoAnnotations.initMocks(this); @@ -140,6 +149,11 @@ public final class AppHibernationServiceTest { packages.add(makePackageInfo(PACKAGE_NAME_3)); doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages( longThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt()); + doReturn(mock(ApplicationInfo.class)).when(mIPackageManager).getApplicationInfo( + any(), anyLong(), anyInt()); + StorageStats storageStats = new StorageStats(); + doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage( + (UUID) any(), anyString(), any()); mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); UserInfo userInfo = addUser(USER_ID_1); @@ -381,18 +395,31 @@ public final class AppHibernationServiceTest { } @Test - public void testGetHibernationStatsForUser_getsStatsForPackage() { - // GIVEN a package is hibernating globally and for a user + public void testGetHibernationStatsForUser_getsStatsForPackage() + throws PackageManager.NameNotFoundException, IOException, RemoteException { + // GIVEN a package is hibernating globally and for a user with some storage saved + final long cacheSavings = 1000; + StorageStats storageStats = new StorageStats(); + storageStats.cacheBytes = cacheSavings; + doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage( + (UUID) any(), eq(PACKAGE_NAME_1), any()); + final long oatDeletionSavings = 2000; + doReturn(oatDeletionSavings).when(mPackageManagerInternal).deleteOatArtifactsOfPackage( + PACKAGE_NAME_1); + mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); // WHEN we ask for the hibernation stats for the package - Map<String, HibernationStats> stats = + Map<String, HibernationStats> statsMap = mAppHibernationService.getHibernationStatsForUser( Set.of(PACKAGE_NAME_1), USER_ID_1); - // THEN the stats exist for the package - assertTrue(stats.containsKey(PACKAGE_NAME_1)); + // THEN the stats exist for the package and add up to the OAT deletion and cache deletion + // savings + HibernationStats stats = statsMap.get(PACKAGE_NAME_1); + assertNotNull(stats); + assertEquals(cacheSavings + oatDeletionSavings, stats.getDiskBytesSaved()); } @Test @@ -519,6 +546,11 @@ public final class AppHibernationServiceTest { } @Override + public StorageStatsManager getStorageStatsManager() { + return mStorageStatsManager; + } + + @Override public UsageStatsManagerInternal getUsageStatsManagerInternal() { return mUsageStatsManagerInternal; } 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 bdea679a3311..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,6 +28,7 @@ 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.BluetoothProfileConnectionInfo; @@ -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 @@ -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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index c877bd141220..48e21225f6d6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -120,6 +120,7 @@ import android.graphics.Color; import android.hardware.usb.UsbManager; import android.net.ProfileNetworkPreference; import android.net.Uri; +import android.net.wifi.WifiSsid; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -164,6 +165,7 @@ import org.mockito.stubbing.Answer; import java.io.File; import java.net.InetSocketAddress; import java.net.Proxy; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -7779,6 +7781,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().userManagerInternal, never()) .setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean()); + DpmTestUtils.assertRestrictions(new Bundle(), dpm.getUserRestrictions(admin1)); } } } @@ -7810,6 +7813,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(true)); reset(getServices().userManagerInternal); + DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(restriction), + dpm.getUserRestrictions(admin1)); + dpm.clearUserRestriction(admin1, restriction); reset(getServices().userManagerInternal); } @@ -8056,6 +8062,28 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testGetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.getPermissionGrantState(admin1, admin1.getPackageName(), + permission.READ_CALENDAR)); + } + + @Test + public void testGetPermissionGrantState_financeDo_notDeviceOwnerPackage_throwsException() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.getPermissionGrantState(admin1, "com.android.foo.package", + permission.READ_PHONE_STATE)); + } + + @Test public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() { assertThrows(SecurityException.class, () -> dpm.setUsbDataSignalingEnabled(true)); @@ -8334,8 +8362,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { @Test public void testSetSsidAllowlist_noDeviceOwnerOrPoOfOrgOwnedDevice() { - final Set<String> ssids = Collections.singleton("ssid1"); - WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids); assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy)); } @@ -8343,8 +8373,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetSsidAllowlist_asDeviceOwner() throws Exception { setDeviceOwner(); - final Set<String> ssids = Collections.singleton("ssid1"); - WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids); dpm.setWifiSsidPolicy(policy); assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids); assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo( @@ -8359,8 +8391,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId); mContext.binder.callingUid = managedProfileAdminUid; - final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3")); - WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)), + WifiSsid.fromBytes("ssid2".getBytes(StandardCharsets.UTF_8)), + WifiSsid.fromBytes("ssid3".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids); dpm.setWifiSsidPolicy(policy); assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids); assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo( @@ -8371,15 +8407,17 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetSsidAllowlist_emptyList() throws Exception { setDeviceOwner(); - final Set<String> ssids = new ArraySet<>(); + final Set<WifiSsid> ssids = new ArraySet<>(); assertThrows(IllegalArgumentException.class, - () -> WifiSsidPolicy.createAllowlistPolicy(ssids)); + () -> new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids)); } @Test public void testSetSsidDenylist_noDeviceOwnerOrPoOfOrgOwnedDevice() { - final Set<String> ssids = Collections.singleton("ssid1"); - WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids); assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy)); } @@ -8387,8 +8425,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetSsidDenylist_asDeviceOwner() throws Exception { setDeviceOwner(); - final Set<String> ssids = Collections.singleton("ssid1"); - WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids); dpm.setWifiSsidPolicy(policy); assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids); assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo( @@ -8403,8 +8443,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId); mContext.binder.callingUid = managedProfileAdminUid; - final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3")); - WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids); + final Set<WifiSsid> ssids = new ArraySet<>( + Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)), + WifiSsid.fromBytes("ssid2".getBytes(StandardCharsets.UTF_8)), + WifiSsid.fromBytes("ssid3".getBytes(StandardCharsets.UTF_8)))); + WifiSsidPolicy policy = new WifiSsidPolicy( + WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids); dpm.setWifiSsidPolicy(policy); assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids); assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo( @@ -8415,9 +8459,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetSsidDenylist_emptyList() throws Exception { setDeviceOwner(); - final Set<String> ssids = new ArraySet<>(); + final Set<WifiSsid> ssids = new ArraySet<>(); assertThrows(IllegalArgumentException.class, - () -> WifiSsidPolicy.createDenylistPolicy(ssids)); + () -> new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids)); } @Test 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/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 94cf20f9c15b..a7b045f10f2c 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -2237,7 +2237,7 @@ public class NetworkPolicyManagerServiceTest { private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) throws InterruptedException { - mService.setSubscriptionPlans(subId, plans, callingPackage); + mService.setSubscriptionPlans(subId, plans, 0, callingPackage); // setSubscriptionPlans() triggers async events, wait for those to be completed before // moving forward as they could interfere with the tests later. postMsgAndWaitForCompletion(); diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 7f7c716bc1f0..2f5993d1d989 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -61,7 +61,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Map; +import java.util.List; @SmallTest @Presubmit @@ -136,9 +136,10 @@ public class ApexManagerTest { mApexManager.scanApexPackagesTraced(mPackageParser2, ParallelPackageParser.makeExecutorService()); - Map<String, String> services = mApexManager.getApexSystemServices(); + List<ApexSystemServiceInfo> services = mApexManager.getApexSystemServices(); assertThat(services).hasSize(1); - assertThat(services).containsKey("com.android.apex.test.ApexSystemService"); + assertThat(services.stream().map(ApexSystemServiceInfo::getName).findFirst().orElse(null)) + .matches("com.android.apex.test.ApexSystemService"); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 687779d686d1..fb3a6264169a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.window.BackNavigationInfo.typeToString; import static com.google.common.truth.Truth.assertThat; @@ -71,7 +72,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backTypeCrossActivityWhenBackToPreviousActivity() { Task task = createTopTaskWithActivity(); - mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task)); + mAtm.setFocusedTask(task.mTaskId, + createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord); BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(task, new StubTransaction()); assertThat(backNavigationInfo).isNotNull(); @@ -85,7 +87,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfoFullyPopulated() { Task task = createTopTaskWithActivity(); - createActivityRecord(task); + createAppWindow(task, FIRST_APPLICATION_WINDOW, "window"); // We need a mock screenshot so TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController(); @@ -115,6 +117,7 @@ public class BackNavigationControllerTests extends WindowTestsBase { private Task createTopTaskWithActivity() { Task task = createTask(mDefaultDisplay); ActivityRecord record = createActivityRecord(task); + createWindow(null, FIRST_APPLICATION_WINDOW, record, "window"); when(record.mSurfaceControl.isValid()).thenReturn(true); mAtm.setFocusedTask(task.mTaskId, record); return task; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 65b5cf5f13c4..dcaa511bf7cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -80,7 +80,6 @@ import android.app.IApplicationThread; import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.pm.ActivityInfo; -import android.graphics.Rect; import android.os.Binder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -231,34 +230,6 @@ public class RootTaskTests extends WindowTestsBase { } @Test - public void testTaskOutset() { - final Task task = createTask(mDisplayContent); - final int taskOutset = 10; - spyOn(task); - doReturn(taskOutset).when(task).getTaskOutset(); - doReturn(true).when(task).inMultiWindowMode(); - - // Mock the resolved override windowing mode to non-fullscreen - final WindowConfiguration windowConfiguration = - task.getResolvedOverrideConfiguration().windowConfiguration; - spyOn(windowConfiguration); - doReturn(WINDOWING_MODE_MULTI_WINDOW) - .when(windowConfiguration).getWindowingMode(); - - // Prevent adjust task dimensions - doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any()); - - final Rect taskBounds = new Rect(200, 200, 800, 1000); - // Update surface position and size by the given bounds. - task.setBounds(taskBounds); - - assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x); - assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y); - assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x); - assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y); - } - - @Test public void testActivityAndTaskGetsProperType() { final Task task1 = new TaskBuilder(mSupervisor).build(); ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index fd523f0585c7..ba414075e4f1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -739,6 +739,9 @@ public class TransitionTests extends WindowTestsBase { } from = from.getParent(); } + if (end.asDisplayArea() != null) { + end.asDisplayArea().mOrganizer = organizer; + } } /** Fill the change map with all the parents of top. Change maps are usually fully populated */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index c56b6141a652..746f2b50ac3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityRecord.State.PAUSED; @@ -374,12 +375,22 @@ public class WindowProcessControllerTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any()); - mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES, - LocaleList.forLanguageTags("en-XA")); + mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME, + Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean()); } @Test + public void testTopActivityUiModeChangeForDifferentPackage_noScheduledConfigChange() { + final ActivityRecord activity = createActivityRecord(mWpc); + activity.mVisibleRequested = true; + mWpc.updateAppSpecificSettingsForAllActivitiesInPackage("com.different.package", + Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); + verify(activity, never()).applyAppSpecificConfig(anyInt(), any()); + verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean()); + } + + @Test public void testTopActivityDisplayAreaMatchesTopMostActivity_noActivities() { assertNull(mWpc.getTopActivityDisplayArea()); } 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..2141c794d743 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 @@ -1489,9 +1493,14 @@ public class TelecomManager { * when placing calls. The user may still need to enable the {@link PhoneAccount} within * the phone app settings before the account is usable. * <p> + * Note: Each package is limited to 10 {@link PhoneAccount} registrations. + * <p> * A {@link SecurityException} will be thrown if an app tries to register a * {@link PhoneAccountHandle} where the package name specified within * {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app. + * <p> + * A {@link IllegalArgumentException} will be thrown if an app tries to register a + * {@link PhoneAccount} when the upper bound limit, 10, has already been reached. * * @param account The complete {@link PhoneAccount}. */ diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java index 184e1541f1b0..30ed7c287421 100644 --- a/telephony/java/android/service/euicc/EuiccService.java +++ b/telephony/java/android/service/euicc/EuiccService.java @@ -257,6 +257,13 @@ public abstract class EuiccService extends Service { "android.service.euicc.extra.RESOLUTION_CARD_ID"; /** + * Intent extra set for resolution requests containing an int indicating the subscription id + * to be enabled. + */ + public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID = + "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID"; + + /** * Intent extra set for resolution requests containing an int indicating the current port index. */ public static final String EXTRA_RESOLUTION_PORT_INDEX = diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java index 4978692d3964..82333a46914b 100644 --- a/telephony/java/android/telephony/ImsiEncryptionInfo.java +++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java @@ -46,16 +46,17 @@ public final class ImsiEncryptionInfo implements Parcelable { private final int keyType; //Date-Time in UTC when the key will expire. private final Date expirationTime; + private final int carrierId; /** @hide */ public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier, - byte[] key, Date expirationTime) { - this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime); + byte[] key, Date expirationTime, int carrierId) { + this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime, carrierId); } /** @hide */ public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier, - PublicKey publicKey, Date expirationTime) { + PublicKey publicKey, Date expirationTime, int carrierId) { // todo need to validate that ImsiEncryptionInfo is being created with the correct params. // Including validating that the public key is in "X.509" format. This will be done in // a subsequent CL. @@ -65,6 +66,7 @@ public final class ImsiEncryptionInfo implements Parcelable { this.publicKey = publicKey; this.keyIdentifier = keyIdentifier; this.expirationTime = expirationTime; + this.carrierId = carrierId; } /** @hide */ @@ -78,6 +80,7 @@ public final class ImsiEncryptionInfo implements Parcelable { keyIdentifier = in.readString(); keyType = in.readInt(); expirationTime = new Date(in.readLong()); + carrierId = in.readInt(); } /** @hide */ @@ -90,6 +93,11 @@ public final class ImsiEncryptionInfo implements Parcelable { return this.mcc; } + /** @hide */ + public int getCarrierId() { + return carrierId; + } + /** * Returns key identifier, a string that helps the authentication server to locate the * private key to decrypt the permanent identity, or {@code null} when uavailable. @@ -157,6 +165,7 @@ public final class ImsiEncryptionInfo implements Parcelable { dest.writeString(keyIdentifier); dest.writeInt(keyType); dest.writeLong(expirationTime.getTime()); + dest.writeInt(carrierId); } @Override @@ -164,10 +173,11 @@ public final class ImsiEncryptionInfo implements Parcelable { return "[ImsiEncryptionInfo " + "mcc=" + mcc + " mnc=" + mnc - + " publicKey=" + publicKey + + ", publicKey=" + publicKey + ", keyIdentifier=" + keyIdentifier + ", keyType=" + keyType + ", expirationTime=" + expirationTime + + ", carrier_id=" + carrierId + "]"; } } 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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 250e55cf5014..0aaafef492bf 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2863,10 +2863,43 @@ public class SubscriptionManager { * outlined above. * @throws IllegalArgumentException if plans don't meet the requirements * defined in {@link SubscriptionPlan}. + * @deprecated use {@link #setSubscriptionPlans(int, List, long)} instead. */ + @Deprecated public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) { + setSubscriptionPlans(subId, plans, 0); + } + + /** + * Set the description of the billing relationship plan between a carrier + * and a specific subscriber. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this relationship applies to. An empty list + * may be sent to clear any existing plans. + * @param plans the list of plans. The first plan is always the primary and + * most important plan. Any additional plans are secondary and + * may not be displayed or used by decision making logic. + * @param expirationDurationMillis the duration after which the subscription plans + * will be automatically cleared, or {@code 0} to leave the plans until + * explicitly cleared, or the next reboot, whichever happens first. + * @throws SecurityException if the caller doesn't meet the requirements + * outlined above. + * @throws IllegalArgumentException if plans don't meet the requirements + * defined in {@link SubscriptionPlan}. + */ + public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans, + @DurationMillisLong long expirationDurationMillis) { getNetworkPolicyManager().setSubscriptionPlans(subId, - plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName()); + plans.toArray(new SubscriptionPlan[0]), expirationDurationMillis, + mContext.getOpPackageName()); } /** @@ -2885,17 +2918,17 @@ public class SubscriptionManager { * @param subId the subscriber this override applies to. * @param overrideUnmetered set if the billing relationship should be * considered unmetered. - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements * outlined above. */ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, - @DurationMillisLong long timeoutMillis) { + @DurationMillisLong long expirationDurationMillis) { setSubscriptionOverrideUnmetered(subId, overrideUnmetered, - TelephonyManager.getAllNetworkTypes(), timeoutMillis); + TelephonyManager.getAllNetworkTypes(), expirationDurationMillis); } /** @@ -2917,8 +2950,8 @@ public class SubscriptionManager { * @param networkTypes the network types this override applies to. If no * network types are specified, override values will be ignored. * {@see TelephonyManager#getAllNetworkTypes()} - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements @@ -2926,10 +2959,10 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, @NonNull @Annotation.NetworkType int[] networkTypes, - @DurationMillisLong long timeoutMillis) { + @DurationMillisLong long expirationDurationMillis) { final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0; getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED, - overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName()); + overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName()); } /** @@ -2949,17 +2982,17 @@ public class SubscriptionManager { * @param subId the subscriber this override applies to. * @param overrideCongested set if the subscription should be considered * congested. - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements * outlined above. */ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, - @DurationMillisLong long timeoutMillis) { + @DurationMillisLong long expirationDurationMillis) { setSubscriptionOverrideCongested(subId, overrideCongested, - TelephonyManager.getAllNetworkTypes(), timeoutMillis); + TelephonyManager.getAllNetworkTypes(), expirationDurationMillis); } /** @@ -2982,8 +3015,8 @@ public class SubscriptionManager { * @param networkTypes the network types this override applies to. If no * network types are specified, override values will be ignored. * {@see TelephonyManager#getAllNetworkTypes()} - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the + * @param expirationDurationMillis the duration after which the requested override + * will be automatically cleared, or {@code 0} to leave in the * requested state until explicitly cleared, or the next reboot, * whichever happens first. * @throws SecurityException if the caller doesn't meet the requirements @@ -2991,10 +3024,10 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, @NonNull @Annotation.NetworkType int[] networkTypes, - @DurationMillisLong long timeoutMillis) { + @DurationMillisLong long expirationDurationMillis) { final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0; getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED, - overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName()); + overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName()); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index baccb2646516..ba1a6edd74ad 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9199,7 +9199,8 @@ public class TelephonyManager { * @param allowedNetworkTypes The bitmask of allowed network types. * @return true on success; false on any failure. * @hide - * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead. + * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason + * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}. */ @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -9233,10 +9234,7 @@ public class TelephonyManager { /** * To indicate allowed network type change is requested by user. - * - * @hide */ - @SystemApi public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; /** @@ -9255,10 +9253,7 @@ public class TelephonyManager { * Carrier configuration won't affect the settings configured through * other reasons and will result in allowing network types that are in both * configurations (i.e intersection of both sets). - * - * @hide */ - @SystemApi public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; /** @@ -9272,35 +9267,23 @@ public class TelephonyManager { /** * Set the allowed network types of the device and provide the reason triggering the allowed * network change. + * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * * This can be called for following reasons * <ol> * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER} - * <li>Allowed network types control by power manager - * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER} * <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} - * <li>Allowed network types control by the user-controlled "Allow 2G" toggle - * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G} * </ol> * This API will result in allowing an intersection of allowed network types for all reasons, * including the configuration done through other reasons. * - * The functionality of this API with the parameter - * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API - * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of - * {@link TelephonyManager#setAllowedNetworkTypes}. - * <p> - * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} - * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then - * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, - * setPreferredNetworkTypesBitmap is used instead. - * * @param reason the reason the allowed network type change is taking place - * @param allowedNetworkTypes The bitmask of allowed network types. + * @param allowedNetworkTypes The bitmask of allowed network type * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. - * @hide + * @throws SecurityException if the caller does not have the required privileges */ - @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature( enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", @@ -9330,18 +9313,19 @@ public class TelephonyManager { * * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a * specific reason. + * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @param reason the reason the allowed network type change is taking place * @return the allowed network type bitmask * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. - * @hide + * @throws SecurityException if the caller does not have the required permission/privileges */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature( enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK) - @SystemApi public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( @AllowedNetworkTypesReason int reason) { if (!isValidAllowedNetworkTypesReason(reason)) { @@ -13560,127 +13544,88 @@ public class TelephonyManager { // 2G /** * network type bitmask unknown. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; /** * network type bitmask indicating the support of radio tech GSM. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1)); /** * network type bitmask indicating the support of radio tech GPRS. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1)); /** * network type bitmask indicating the support of radio tech EDGE. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1)); /** * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B). - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1)); /** * network type bitmask indicating the support of radio tech 1xRTT. - * @hide */ - @SystemApi + @SuppressLint("AllUpper") public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1)); // 3G /** * network type bitmask indicating the support of radio tech EVDO 0. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1)); /** * network type bitmask indicating the support of radio tech EVDO A. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1)); /** * network type bitmask indicating the support of radio tech EVDO B. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1)); /** * network type bitmask indicating the support of radio tech EHRPD. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1)); /** * network type bitmask indicating the support of radio tech HSUPA. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1)); /** * network type bitmask indicating the support of radio tech HSDPA. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1)); /** * network type bitmask indicating the support of radio tech HSPA. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1)); /** * network type bitmask indicating the support of radio tech HSPAP. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1)); /** * network type bitmask indicating the support of radio tech UMTS. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1)); /** * network type bitmask indicating the support of radio tech TD_SCDMA. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1)); // 4G /** * network type bitmask indicating the support of radio tech LTE. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1)); /** * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation). - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1)); /** * network type bitmask indicating the support of radio tech NR(New Radio) 5G. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1)); /** * network type bitmask indicating the support of radio tech IWLAN. - * @hide */ - @SystemApi public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1)); /** @hide */ @@ -13735,12 +13680,11 @@ public class TelephonyManager { /** * @return Modem supported radio access family bitmask * - * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or + * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). - * @hide + * + * @throws SecurityException if the caller does not have the required permission */ - @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @NetworkTypeBitMask long getSupportedRadioAccessFamily() { 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/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt index d3ad9e8193c0..3af54503d469 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt @@ -41,7 +41,7 @@ class BitmapTransitionView @JvmOverloads constructor( ImageDecoder.createSource(context.resources, R.drawable.very_large_photo)) private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) - private val mShader = RuntimeShader(AGSL, false) + private val mShader = RuntimeShader(AGSL) private var mCurrentProgress = -1f private var mForwardProgress = true private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f) diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java index 65d168bd5238..fafe60b46676 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java @@ -83,7 +83,7 @@ public class ColorFiltersMutateActivity extends Activity { mBlendPaint = new Paint(); mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER)); - mRuntimeShader = new RuntimeShader(sSkSL, false); + mRuntimeShader = new RuntimeShader(sSkSL); mRuntimeShader.setFloatUniform("param1", mShaderParam1); mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1, Shader.TileMode.CLAMP, diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt index 06280d295425..3c71b96c6c31 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt @@ -30,7 +30,7 @@ import android.view.View class RenderEffectViewActivity : Activity() { - private val mDropsShader = RuntimeShader(dropsAGSL, false) + private val mDropsShader = RuntimeShader(dropsAGSL) private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f) private var mStartTime = System.currentTimeMillis() private lateinit var mScratchesImage: Bitmap diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java index 73c4b8af99de..b78907c46744 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java @@ -109,7 +109,7 @@ public class RippleActivity extends Activity { p.setColor(mColor); mPaint = CanvasProperty.createPaint(p); - mRuntimeShader = new RuntimeShader(sSkSL, false); + mRuntimeShader = new RuntimeShader(sSkSL); mRuntimeShader.setFloatUniform("in_maxRadius", MAX_RADIUS); } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java index 12e338c96aea..2990c9e59fec 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java @@ -69,7 +69,7 @@ public class StretchShaderActivity extends Activity { linearLayout.setOrientation(LinearLayout.VERTICAL); mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap(); - mRuntimeShader = new RuntimeShader(SKSL, false); + mRuntimeShader = new RuntimeShader(SKSL); BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml index c30d76137f76..21256d8c9d0b 100644 --- a/tests/SilkFX/AndroidManifest.xml +++ b/tests/SilkFX/AndroidManifest.xml @@ -20,17 +20,20 @@ <uses-sdk android:minSdkVersion="30"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:label="SilkFX" android:theme="@android:style/Theme.Material"> <activity android:name=".Main" android:label="SilkFX Demos" + android:banner="@drawable/background1" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.LAUNCHER"/> + <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> </intent-filter> </activity> @@ -41,13 +44,16 @@ <activity android:name=".materials.GlassActivity" android:label="Glass Examples" - android:banner="@drawable/background1" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> </intent-filter> </activity> + <activity android:name=".materials.BackgroundBlurActivity" + android:theme="@style/Theme.BackgroundBlurTheme" + android:exported="true"> + </activity> + </application> </manifest> diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml new file mode 100644 index 000000000000..173ca99bdfdf --- /dev/null +++ b/tests/SilkFX/res/drawable/background_blur_drawable.xml @@ -0,0 +1,20 @@ +<!-- + ~ 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#20FFFFFF"/> + <corners android:radius="10dp"/> +</shape> diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml new file mode 100644 index 000000000000..bd8942d46383 --- /dev/null +++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml @@ -0,0 +1,20 @@ +<?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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="10dp"/> +</shape> diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml new file mode 100644 index 000000000000..f13c0883cb01 --- /dev/null +++ b/tests/SilkFX/res/layout/activity_background_blur.xml @@ -0,0 +1,173 @@ +<?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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/background" + android:layout_width="390dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:padding="15dp" + android:orientation="vertical" + tools:context=".materials.BackgroundBlurActivity"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:padding="10dp" + android:textColor="#ffffffff" + android:text="Hello blurry world!"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textColor="#ffffffff" + android:text="Background blur"/> + + <SeekBar + android:id="@+id/set_background_blur" + android:min="0" + android:max="300" + android:layout_width="160dp" + android:layout_height="wrap_content"/> + <TextView + android:id="@+id/background_blur_radius" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="#ffffffff" + android:ems="3" + android:gravity="center" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:text="TODO"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textColor="#ffffffff" + android:text="Background alpha"/> + + <SeekBar + android:id="@+id/set_background_alpha" + android:min="0" + android:max="100" + android:layout_width="160dp" + android:layout_height="wrap_content" /> + <TextView + android:id="@+id/background_alpha" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="#ffffffff" + android:ems="3" + android:gravity="center" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:text="TODO"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textColor="#ffffffff" + android:text="Blur behind"/> + + <SeekBar + android:id="@+id/set_blur_behind" + android:min="0" + android:max="300" + android:layout_width="160dp" + android:layout_height="wrap_content" /> + <TextView + android:id="@+id/blur_behind_radius" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:textColor="#ffffffff" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:ems="3" + android:text="TODO"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textColor="#ffffffff" + android:text="Dim amount"/> + + <SeekBar + android:id="@+id/set_dim_amount" + android:min="0" + android:max="100" + android:layout_width="160dp" + android:layout_height="wrap_content" /> + <TextView + android:id="@+id/dim_amount" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:textColor="#ffffffff" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:ems="3" + android:text="TODO"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginTop="5dp" + android:orientation="vertical" + android:gravity="center"> + + <Button + android:id="@+id/toggle_blur_enabled" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Disable blur" + android:onClick="toggleForceBlurDisabled"/> + + <Button + android:id="@+id/toggle_battery_saving_mode" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="TODO" + android:onClick="toggleBatterySavingMode"/> + </LinearLayout> + <requestFocus/> + +</LinearLayout> diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml new file mode 100644 index 000000000000..66edbb5c9382 --- /dev/null +++ b/tests/SilkFX/res/values/style.xml @@ -0,0 +1,31 @@ +<?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. + --> +<!-- Styles for immersive actions UI. --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog"> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBlurBehindEnabled">true</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:windowElevation">0dp</item> + <item name="buttonStyle">@style/AppTheme.Button</item> + <item name="colorAccent">#bbffffff</item> + </style> + <style name="AppTheme.Button" parent="Widget.AppCompat.Button"> + <item name="android:textColor">#ffffffff</item> + </style> + +</resources> diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt index 9ed8d2f5edf7..7132ae8772ea 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -30,6 +30,7 @@ import com.android.test.silkfx.app.EXTRA_LAYOUT import com.android.test.silkfx.app.EXTRA_TITLE import com.android.test.silkfx.hdr.GlowActivity import com.android.test.silkfx.materials.GlassActivity +import com.android.test.silkfx.materials.BackgroundBlurActivity import kotlin.reflect.KClass class Demo(val name: String, val makeIntent: (Context) -> Intent) { @@ -51,7 +52,8 @@ private val AllDemos = listOf( Demo("Blingy Notifications", R.layout.bling_notifications) )), DemoGroup("Materials", listOf( - Demo("Glass", GlassActivity::class) + Demo("Glass", GlassActivity::class), + Demo("Background Blur", BackgroundBlurActivity::class) )) ) @@ -126,4 +128,4 @@ class Main : Activity() { AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) } } -}
\ No newline at end of file +} diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt new file mode 100644 index 000000000000..9d17d38d4298 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt @@ -0,0 +1,189 @@ +/* + * 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.test.silkfx.materials + +import android.app.Activity +import android.content.Intent +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.PaintDrawable +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.provider.Settings +import android.util.TypedValue +import android.view.View +import android.view.WindowManager +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.Switch +import android.widget.TextView +import com.android.test.silkfx.R +import com.android.internal.graphics.drawable.BackgroundBlurDrawable +import android.widget.LinearLayout +import android.widget.Button + +import android.view.ViewRootImpl + +class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener { + var mBackgroundDrawable = PaintDrawable(Color.WHITE) + var mBackgroundBlurRadius = 50 + var mAlphaWithBlur = 0.2f + var mAlphaNoBlur = 0.5f + + var mBlurBehindRadius = 10 + var mDimAmountWithBlur = 0.2f + var mDimAmountNoBlur = 0.2f + + var mBlurForceDisabled = false + var mBatterySavingModeOn = false + + lateinit var blurBackgroundSeekBar: SeekBar + lateinit var backgroundAlphaSeekBar : SeekBar + lateinit var blurBehindSeekBar : SeekBar + lateinit var dimAmountSeekBar : SeekBar + + val blurEnabledListener = { enabled : Boolean -> + blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius) + blurBehindSeekBar.setProgress(mBlurBehindRadius) + + if (enabled) { + setBackgroundBlur(mBackgroundBlurRadius) + setBackgroundColorAlpha(mAlphaWithBlur) + + setBlurBehind(mBlurBehindRadius) + setDimAmount(mDimAmountWithBlur) + + backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt()) + dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt()) + } else { + setBackgroundColorAlpha(mAlphaNoBlur) + setDimAmount(mDimAmountNoBlur) + + backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt()) + dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt()) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_background_blur) + + window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND) + window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + + mBackgroundDrawable.setCornerRadius(30f) + window.setBackgroundDrawable(mBackgroundDrawable) + + mBatterySavingModeOn = + Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1 + setBatterySavingModeOn(mBatterySavingModeOn) + + blurBackgroundSeekBar = requireViewById(R.id.set_background_blur) + backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha) + blurBehindSeekBar = requireViewById(R.id.set_blur_behind) + dimAmountSeekBar = requireViewById(R.id.set_dim_amount) + + arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar) + .forEach { + it.setOnSeekBarChangeListener(this) + } + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener) + } + + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + when (seekBar) { + blurBackgroundSeekBar -> setBackgroundBlur(progress) + backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f) + blurBehindSeekBar -> setBlurBehind(progress) + dimAmountSeekBar -> setDimAmount(progress / 100.0f) + else -> throw IllegalArgumentException("Unknown seek bar") + } + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) {} + override fun onStopTrackingTouch(seekBar: SeekBar?) {} + + fun setBlurDisabled(disabled: Boolean) { + mBlurForceDisabled = disabled + Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS, + if (mBlurForceDisabled) 1 else 0) + (findViewById(R.id.toggle_blur_enabled) as Button) + .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs") + } + + fun toggleForceBlurDisabled(v: View) { + setBlurDisabled(!mBlurForceDisabled) + } + + fun setBackgroundBlur(radius: Int) { + mBackgroundBlurRadius = radius + (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString()) + window.setBackgroundBlurRadius(mBackgroundBlurRadius) + } + + fun setBlurBehind(radius: Int) { + mBlurBehindRadius = radius + (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString()) + window.getAttributes().setBlurBehindRadius(mBlurBehindRadius) + window.setAttributes(window.getAttributes()) + } + + fun setDimAmount(amount: Float) { + if (getWindowManager().isCrossWindowBlurEnabled()) { + mDimAmountWithBlur = amount + } else { + mDimAmountNoBlur = amount + } + (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount)) + window.getAttributes().dimAmount = amount + window.setAttributes(window.getAttributes()) + } + + fun setBatterySavingModeOn(on: Boolean) { + mBatterySavingModeOn = on + Settings.Global.putInt(getContentResolver(), + Settings.Global.LOW_POWER_MODE, if (on) 1 else 0) + (findViewById(R.id.toggle_battery_saving_mode) as Button).setText( + if (on) "Exit low power mode" else "Enter low power mode") + } + + fun toggleBatterySavingMode(v: View) { + setBatterySavingModeOn(!mBatterySavingModeOn) + } + + fun setBackgroundColorAlpha(alpha: Float) { + if (getWindowManager().isCrossWindowBlurEnabled()) { + mAlphaWithBlur = alpha + } else { + mAlphaNoBlur = alpha + } + (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha)) + mBackgroundDrawable.setAlpha((alpha * 255f).toInt()) + getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes()) + } +} diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp deleted file mode 100644 index f87ca2ef928b..000000000000 --- a/tests/benchmarks/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2015 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. - -// build framework base core benchmarks -// ============================================================ - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library { - name: "networkStatsFactory-benchmarks", - installable: true, - - srcs: ["src/**/*.java"], - - libs: [ - "caliper-api-target", - "services.core", - ], - -} diff --git a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java b/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java deleted file mode 100644 index ef014f0d4e53..000000000000 --- a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2012 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.net; - -import android.net.NetworkStats; -import android.os.SystemClock; -import com.android.server.net.NetworkStatsFactory; -import com.google.caliper.AfterExperiment; -import com.google.caliper.BeforeExperiment; -import java.io.File; - -public class NetworkStatsFactoryBenchmark { - private File mStats; - - // TODO: consider staging stats file with different number of rows - - @BeforeExperiment - protected void setUp() { - mStats = new File("/proc/net/xt_qtaguid/stats"); - } - - @AfterExperiment - protected void tearDown() { - mStats = null; - } - - public void timeReadNetworkStatsDetailJava(int reps) throws Exception { - for (int i = 0; i < reps; i++) { - NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL, - // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70 - // Fixed compilation problem but needs addressing properly. - new String[0], 999); - } - } - - public void timeReadNetworkStatsDetailNative(int reps) { - for (int i = 0; i < reps; i++) { - final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); - NetworkStatsFactory.nativeReadNetworkStatsDetail( - stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL, - // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70 - // Fixed compilation problem but needs addressing properly. - new String[0], 999, false); - } - } -} diff --git a/tests/benchmarks/src/com/android/server/net/OWNERS b/tests/benchmarks/src/com/android/server/net/OWNERS deleted file mode 100644 index aa87958f1d53..000000000000 --- a/tests/benchmarks/src/com/android/server/net/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /services/core/java/com/android/server/net/OWNERS diff --git a/tests/componentalias/AndroidTest-template.xml b/tests/componentalias/AndroidTest-template.xml index 2d4621702329..afdfe79ea4a4 100644 --- a/tests/componentalias/AndroidTest-template.xml +++ b/tests/componentalias/AndroidTest-template.xml @@ -21,8 +21,6 @@ <option name="test-file-name" value="ComponentAliasTests2.apk" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> - <option name="run-command" value="am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android" /> - <!-- Exempt the helper APKs from the BG restriction, so they can start BG services. --> <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests" /> <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.sub1" /> diff --git a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java index 89db2f724302..9658d6f6a698 100644 --- a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java +++ b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java @@ -17,6 +17,7 @@ package android.content.componentalias.tests; import android.content.ComponentName; import android.content.Context; +import android.os.Build; import android.provider.DeviceConfig; import android.util.Log; @@ -27,6 +28,7 @@ import com.android.compatibility.common.util.ShellUtils; import com.android.compatibility.common.util.TestUtils; import org.junit.AfterClass; +import org.junit.Assume; import org.junit.Before; import java.util.function.Consumer; @@ -37,7 +39,11 @@ public class BaseComponentAliasTest { protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER); @Before - public void enableComponentAlias() throws Exception { + public void enableComponentAliasWithCompatFlag() throws Exception { + Assume.assumeTrue(Build.isDebuggable()); + ShellUtils.runShellCommand( + "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android"); + sDeviceConfig.set("enable_experimental_component_alias", ""); sDeviceConfig.set("component_alias_overrides", ""); // Make sure the feature is actually enabled. @@ -49,6 +55,8 @@ public class BaseComponentAliasTest { @AfterClass public static void restoreDeviceConfig() throws Exception { + ShellUtils.runShellCommand( + "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android"); sDeviceConfig.close(); } diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java new file mode 100644 index 000000000000..52c6d5b8ae12 --- /dev/null +++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java @@ -0,0 +1,63 @@ +/* + * 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.content.componentalias.tests; + +import android.os.Build; +import android.provider.DeviceConfig; + +import com.android.compatibility.common.util.DeviceConfigStateHelper; +import com.android.compatibility.common.util.ShellUtils; +import com.android.compatibility.common.util.TestUtils; + +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Test; + +public class ComponentAliasEnableWithDeviceConfigTest { + protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER); + + @AfterClass + public static void restoreDeviceConfig() throws Exception { + sDeviceConfig.close(); + } + + @Test + public void enableComponentAliasWithCompatFlag() throws Exception { + Assume.assumeTrue(Build.isDebuggable()); + + sDeviceConfig.set("component_alias_overrides", ""); + + // First, disable with both compat-id and device config. + ShellUtils.runShellCommand( + "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android"); + sDeviceConfig.set("enable_experimental_component_alias", ""); + + TestUtils.waitUntil("Wait until component alias is actually enabled", () -> { + return ShellUtils.runShellCommand("dumpsys activity component-alias") + .indexOf("Enabled: false") > 0; + }); + + // Then, enable by device config. + sDeviceConfig.set("enable_experimental_component_alias", "true"); + + // Make sure the feature is actually enabled. + TestUtils.waitUntil("Wait until component alias is actually enabled", () -> { + return ShellUtils.runShellCommand("dumpsys activity component-alias") + .indexOf("Enabled: true") > 0; + }); + } +} diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java new file mode 100644 index 000000000000..7935476d6156 --- /dev/null +++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java @@ -0,0 +1,60 @@ +/* + * 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.content.componentalias.tests; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.provider.DeviceConfig; + +import com.android.compatibility.common.util.DeviceConfigStateHelper; +import com.android.compatibility.common.util.ShellUtils; + +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Test; + +/** + * Test to make sure component-alias can't be enabled on user builds. + */ +public class ComponentAliasNotSupportedOnUserBuildTest { + protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER); + + @AfterClass + public static void restoreDeviceConfig() throws Exception { + sDeviceConfig.close(); + } + + @Test + public void enableComponentAliasWithCompatFlag() throws Exception { + Assume.assumeFalse(Build.isDebuggable()); + + // Try to enable it by both the device config and compat-id. + sDeviceConfig.set("enable_experimental_component_alias", "true"); + ShellUtils.runShellCommand( + "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android"); + + // Sleep for an arbitrary amount of time, so the config would sink in, if there was + // no "not on user builds" check. + + Thread.sleep(5000); + + // Make sure the feature is still disabled. + assertThat(ShellUtils.runShellCommand("dumpsys activity component-alias") + .indexOf("Enabled: false") > 0).isTrue(); + } +} |