diff options
628 files changed, 17452 insertions, 8023 deletions
diff --git a/Android.bp b/Android.bp index e03f8449891a..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: [ @@ -308,6 +285,8 @@ java_defaults { include_dirs: [ "frameworks/av/aidl", "frameworks/native/libs/permission/aidl", + // TODO: remove when moved to the below package + "frameworks/base/packages/ConnectivityT/framework-t/aidl-export", "packages/modules/Connectivity/framework/aidl-export", ], }, @@ -416,7 +395,6 @@ java_library { static_libs: [ "app-compat-annotations", "framework-minus-apex", - "framework-appsearch.impl", // TODO(b/146218515): should be removed "framework-updatable-stubs-module_libs_api", ], sdk_version: "core_platform", @@ -445,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", @@ -557,6 +534,9 @@ stubs_defaults { include_dirs: [ "frameworks/av/aidl", "frameworks/native/libs/permission/aidl", + // TODO: remove when moved to the below package + "frameworks/base/packages/ConnectivityT/framework-t/aidl-export", + "packages/modules/Connectivity/framework/aidl-export", ], }, // These are libs from framework-internal-utils that are required (i.e. being referenced) @@ -602,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/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index afad29c7eebb..c4795f55fc8a 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -364,6 +364,11 @@ public class PowerExemptionManager { * @hide */ public static final int REASON_SYSTEM_MODULE = 320; + /** + * Carrier privileged app. + * @hide + */ + public static final int REASON_CARRIER_PRIVILEGED_APP = 321; /** @hide The app requests out-out. */ public static final int REASON_OPT_OUT_REQUESTED = 1000; @@ -440,6 +445,7 @@ public class PowerExemptionManager { REASON_ROLE_DIALER, REASON_ROLE_EMERGENCY, REASON_SYSTEM_MODULE, + REASON_CARRIER_PRIVILEGED_APP, REASON_OPT_OUT_REQUESTED, }) @Retention(RetentionPolicy.SOURCE) @@ -749,6 +755,8 @@ public class PowerExemptionManager { return "ROLE_EMERGENCY"; case REASON_SYSTEM_MODULE: return "SYSTEM_MODULE"; + case REASON_CARRIER_PRIVILEGED_APP: + return "CARRIER_PRIVILEGED_APP"; case REASON_OPT_OUT_REQUESTED: return "REASON_OPT_OUT_REQUESTED"; default: diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index f098e10ef020..cea19451f005 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -2768,7 +2768,7 @@ public class JobSchedulerService extends com.android.server.SystemService return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS); - } else if (job.getEffectivePriority() == JobInfo.PRIORITY_HIGH) { + } else if (job.getEffectivePriority() >= JobInfo.PRIORITY_HIGH) { return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS; } else { return mConstants.RUNTIME_MIN_GUARANTEE_MS; diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index b4edd39894f9..2c2af28c7560 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -66,8 +66,8 @@ java_library { }, visibility: [ "//frameworks/av/apex:__subpackages__", - "//frameworks/base", // For framework-all "//frameworks/base/apex/media/service", + "//frameworks/base/api", // For framework-all ], } diff --git a/api/api.go b/api/api.go index 17649e80e81a..5e5f60ee993f 100644 --- a/api/api.go +++ b/api/api.go @@ -27,6 +27,7 @@ import ( const art = "art.module.public.api" const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" +var core_libraries_modules = []string{art, conscrypt, i18n} // The intention behind this soong plugin is to generate a number of "merged" // API-related modules that would otherwise require a large amount of very @@ -199,9 +200,28 @@ func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { ctx.CreateModule(java.LibraryFactory, &props) } -func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) { +func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { + // This module is for the "framework-all" module, which should not include the core libraries. + modules = removeAll(modules, core_libraries_modules) + // TODO(b/214988855): remove the line below when framework-bluetooth has an impl jar. + modules = remove(modules, "framework-bluetooth") + props := libraryProps{} + props.Name = proptools.StringPtr("all-framework-module-impl") + props.Static_libs = transformArray(modules, "", ".impl") + // Media module's impl jar is called "updatable-media" + for i, v := range props.Static_libs { + if v == "framework-media.impl" { + props.Static_libs[i] = "updatable-media" + } + } + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) +} + +func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) { // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes. - modules = removeAll(modules, []string{art, conscrypt, i18n}) + modules = removeAll(modules, core_libraries_modules) props := libraryProps{} props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api") props.Static_libs = transformArray(modules, "", ".stubs.module_lib") @@ -269,7 +289,8 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { createMergedPublicStubs(ctx, bootclasspath) createMergedSystemStubs(ctx, bootclasspath) - createMergedModuleLibStubs(ctx, bootclasspath) + createMergedFrameworkModuleLibStubs(ctx, bootclasspath) + createMergedFrameworkImpl(ctx, bootclasspath) createMergedAnnotations(ctx, bootclasspath) diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 9153426b29ab..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; @@ -35446,51 +35367,6 @@ Landroid/net/IIpConnectivityMetrics$Stub;->TRANSACTION_removeNetdEventCallback:I Landroid/net/IIpConnectivityMetrics;->addNetdEventCallback(ILandroid/net/INetdEventCallback;)Z Landroid/net/IIpConnectivityMetrics;->logEvent(Landroid/net/ConnectivityMetricsEvent;)I Landroid/net/IIpConnectivityMetrics;->removeNetdEventCallback(I)Z -Landroid/net/IIpSecService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/IIpSecService$Stub$Proxy;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse; -Landroid/net/IIpSecService$Stub$Proxy;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V -Landroid/net/IIpSecService$Stub$Proxy;->applyTunnelModeTransform(IIILjava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->closeUdpEncapsulationSocket(I)V -Landroid/net/IIpSecService$Stub$Proxy;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse; -Landroid/net/IIpSecService$Stub$Proxy;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse; -Landroid/net/IIpSecService$Stub$Proxy;->deleteTransform(I)V -Landroid/net/IIpSecService$Stub$Proxy;->deleteTunnelInterface(ILjava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/IIpSecService$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/IIpSecService$Stub$Proxy;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse; -Landroid/net/IIpSecService$Stub$Proxy;->releaseSecurityParameterIndex(I)V -Landroid/net/IIpSecService$Stub$Proxy;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService$Stub$Proxy;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V -Landroid/net/IIpSecService$Stub;-><init>()V -Landroid/net/IIpSecService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IIpSecService; -Landroid/net/IIpSecService$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/IIpSecService$Stub;->TRANSACTION_addAddressToTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_allocateSecurityParameterIndex:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTransportModeTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTunnelModeTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_closeUdpEncapsulationSocket:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_createTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_createTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTransform:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_openUdpEncapsulationSocket:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_releaseSecurityParameterIndex:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_removeAddressFromTunnelInterface:I -Landroid/net/IIpSecService$Stub;->TRANSACTION_removeTransportModeTransforms:I -Landroid/net/IIpSecService;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse; -Landroid/net/IIpSecService;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V -Landroid/net/IIpSecService;->applyTunnelModeTransform(IIILjava/lang/String;)V -Landroid/net/IIpSecService;->closeUdpEncapsulationSocket(I)V -Landroid/net/IIpSecService;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse; -Landroid/net/IIpSecService;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse; -Landroid/net/IIpSecService;->deleteTransform(I)V -Landroid/net/IIpSecService;->deleteTunnelInterface(ILjava/lang/String;)V -Landroid/net/IIpSecService;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse; -Landroid/net/IIpSecService;->releaseSecurityParameterIndex(I)V -Landroid/net/IIpSecService;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V -Landroid/net/IIpSecService;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V Landroid/net/INetd$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/INetd$Stub$Proxy;->addVirtualTunnelInterface(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V Landroid/net/INetd$Stub$Proxy;->bandwidthEnableDataSaver(Z)Z @@ -35838,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; @@ -35914,174 +35728,6 @@ Landroid/net/InterfaceConfiguration;->mFlags:Ljava/util/HashSet; Landroid/net/InterfaceConfiguration;->mHwAddr:Ljava/lang/String; Landroid/net/InterfaceConfiguration;->setHardwareAddress(Ljava/lang/String;)V Landroid/net/InterfaceConfiguration;->validateFlag(Ljava/lang/String;)V -Landroid/net/IpSecAlgorithm;->checkValidOrThrow(Ljava/lang/String;II)V -Landroid/net/IpSecAlgorithm;->CRYPT_NULL:Ljava/lang/String; -Landroid/net/IpSecAlgorithm;->equals(Landroid/net/IpSecAlgorithm;Landroid/net/IpSecAlgorithm;)Z -Landroid/net/IpSecAlgorithm;->isAead()Z -Landroid/net/IpSecAlgorithm;->isAuthentication()Z -Landroid/net/IpSecAlgorithm;->isEncryption()Z -Landroid/net/IpSecAlgorithm;->isUnsafeBuild()Z -Landroid/net/IpSecAlgorithm;->mKey:[B -Landroid/net/IpSecAlgorithm;->mName:Ljava/lang/String; -Landroid/net/IpSecAlgorithm;->mTruncLenBits:I -Landroid/net/IpSecAlgorithm;->TAG:Ljava/lang/String; -Landroid/net/IpSecConfig;-><init>()V -Landroid/net/IpSecConfig;-><init>(Landroid/net/IpSecConfig;)V -Landroid/net/IpSecConfig;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecConfig;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecConfig;->equals(Landroid/net/IpSecConfig;Landroid/net/IpSecConfig;)Z -Landroid/net/IpSecConfig;->getAuthenticatedEncryption()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getAuthentication()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getDestinationAddress()Ljava/lang/String; -Landroid/net/IpSecConfig;->getEncapRemotePort()I -Landroid/net/IpSecConfig;->getEncapSocketResourceId()I -Landroid/net/IpSecConfig;->getEncapType()I -Landroid/net/IpSecConfig;->getEncryption()Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->getMarkMask()I -Landroid/net/IpSecConfig;->getMarkValue()I -Landroid/net/IpSecConfig;->getMode()I -Landroid/net/IpSecConfig;->getNattKeepaliveInterval()I -Landroid/net/IpSecConfig;->getNetwork()Landroid/net/Network; -Landroid/net/IpSecConfig;->getSourceAddress()Ljava/lang/String; -Landroid/net/IpSecConfig;->getSpiResourceId()I -Landroid/net/IpSecConfig;->mAuthenticatedEncryption:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mAuthentication:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mDestinationAddress:Ljava/lang/String; -Landroid/net/IpSecConfig;->mEncapRemotePort:I -Landroid/net/IpSecConfig;->mEncapSocketResourceId:I -Landroid/net/IpSecConfig;->mEncapType:I -Landroid/net/IpSecConfig;->mEncryption:Landroid/net/IpSecAlgorithm; -Landroid/net/IpSecConfig;->mMarkMask:I -Landroid/net/IpSecConfig;->mMarkValue:I -Landroid/net/IpSecConfig;->mMode:I -Landroid/net/IpSecConfig;->mNattKeepaliveInterval:I -Landroid/net/IpSecConfig;->mNetwork:Landroid/net/Network; -Landroid/net/IpSecConfig;->mSourceAddress:Ljava/lang/String; -Landroid/net/IpSecConfig;->mSpiResourceId:I -Landroid/net/IpSecConfig;->setAuthenticatedEncryption(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setAuthentication(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setDestinationAddress(Ljava/lang/String;)V -Landroid/net/IpSecConfig;->setEncapRemotePort(I)V -Landroid/net/IpSecConfig;->setEncapSocketResourceId(I)V -Landroid/net/IpSecConfig;->setEncapType(I)V -Landroid/net/IpSecConfig;->setEncryption(Landroid/net/IpSecAlgorithm;)V -Landroid/net/IpSecConfig;->setMarkMask(I)V -Landroid/net/IpSecConfig;->setMarkValue(I)V -Landroid/net/IpSecConfig;->setMode(I)V -Landroid/net/IpSecConfig;->setNattKeepaliveInterval(I)V -Landroid/net/IpSecConfig;->setNetwork(Landroid/net/Network;)V -Landroid/net/IpSecConfig;->setSourceAddress(Ljava/lang/String;)V -Landroid/net/IpSecConfig;->setSpiResourceId(I)V -Landroid/net/IpSecConfig;->TAG:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)V -Landroid/net/IpSecManager$IpSecTunnelInterface;->addAddress(Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$IpSecTunnelInterface;->getInterfaceName()Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->getResourceId()I -Landroid/net/IpSecManager$IpSecTunnelInterface;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mInterfaceName:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mLocalAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mOpPackageName:Ljava/lang/String; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mRemoteAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mResourceId:I -Landroid/net/IpSecManager$IpSecTunnelInterface;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager$IpSecTunnelInterface;->mUnderlyingNetwork:Landroid/net/Network; -Landroid/net/IpSecManager$IpSecTunnelInterface;->removeAddress(Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$ResourceUnavailableException;-><init>(Ljava/lang/String;)V -Landroid/net/IpSecManager$SecurityParameterIndex;-><init>(Landroid/net/IIpSecService;Ljava/net/InetAddress;I)V -Landroid/net/IpSecManager$SecurityParameterIndex;->getResourceId()I -Landroid/net/IpSecManager$SecurityParameterIndex;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$SecurityParameterIndex;->mDestinationAddress:Ljava/net/InetAddress; -Landroid/net/IpSecManager$SecurityParameterIndex;->mResourceId:I -Landroid/net/IpSecManager$SecurityParameterIndex;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager$SecurityParameterIndex;->mSpi:I -Landroid/net/IpSecManager$SpiUnavailableException;-><init>(Ljava/lang/String;I)V -Landroid/net/IpSecManager$SpiUnavailableException;->mSpi:I -Landroid/net/IpSecManager$Status;->OK:I -Landroid/net/IpSecManager$Status;->RESOURCE_UNAVAILABLE:I -Landroid/net/IpSecManager$Status;->SPI_UNAVAILABLE:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;-><init>(Landroid/net/IIpSecService;I)V -Landroid/net/IpSecManager$UdpEncapsulationSocket;->getResourceId()I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPfd:Landroid/os/ParcelFileDescriptor; -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPort:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mResourceId:I -Landroid/net/IpSecManager$UdpEncapsulationSocket;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;)V -Landroid/net/IpSecManager;->applyTunnelModeTransform(Landroid/net/IpSecManager$IpSecTunnelInterface;ILandroid/net/IpSecTransform;)V -Landroid/net/IpSecManager;->createIpSecTunnelInterface(Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)Landroid/net/IpSecManager$IpSecTunnelInterface; -Landroid/net/IpSecManager;->INVALID_RESOURCE_ID:I -Landroid/net/IpSecManager;->maybeHandleServiceSpecificException(Landroid/os/ServiceSpecificException;)V -Landroid/net/IpSecManager;->mContext:Landroid/content/Context; -Landroid/net/IpSecManager;->mService:Landroid/net/IIpSecService; -Landroid/net/IpSecManager;->removeTunnelModeTransform(Landroid/net/Network;Landroid/net/IpSecTransform;)V -Landroid/net/IpSecManager;->rethrowCheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/io/IOException; -Landroid/net/IpSecManager;->rethrowUncheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/lang/RuntimeException; -Landroid/net/IpSecManager;->TAG:Ljava/lang/String; -Landroid/net/IpSecSpiResponse;-><init>(I)V -Landroid/net/IpSecSpiResponse;-><init>(III)V -Landroid/net/IpSecSpiResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecSpiResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecSpiResponse;->resourceId:I -Landroid/net/IpSecSpiResponse;->spi:I -Landroid/net/IpSecSpiResponse;->status:I -Landroid/net/IpSecSpiResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecTransform$Builder;->buildTunnelModeTransform(Ljava/net/InetAddress;Landroid/net/IpSecManager$SecurityParameterIndex;)Landroid/net/IpSecTransform; -Landroid/net/IpSecTransform$Builder;->mConfig:Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform$Builder;->mContext:Landroid/content/Context; -Landroid/net/IpSecTransform$NattKeepaliveCallback;-><init>()V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_ERROR:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_UNSUPPORTED:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_INVALID_NETWORK:I -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onError(I)V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStarted()V -Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStopped()V -Landroid/net/IpSecTransform;-><init>(Landroid/content/Context;Landroid/net/IpSecConfig;)V -Landroid/net/IpSecTransform;->activate()Landroid/net/IpSecTransform; -Landroid/net/IpSecTransform;->checkResultStatus(I)V -Landroid/net/IpSecTransform;->ENCAP_ESPINUDP:I -Landroid/net/IpSecTransform;->ENCAP_ESPINUDP_NON_IKE:I -Landroid/net/IpSecTransform;->ENCAP_NONE:I -Landroid/net/IpSecTransform;->equals(Landroid/net/IpSecTransform;Landroid/net/IpSecTransform;)Z -Landroid/net/IpSecTransform;->getConfig()Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform;->getIpSecService()Landroid/net/IIpSecService; -Landroid/net/IpSecTransform;->getResourceId()I -Landroid/net/IpSecTransform;->mCallbackHandler:Landroid/os/Handler; -Landroid/net/IpSecTransform;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/net/IpSecTransform;->mConfig:Landroid/net/IpSecConfig; -Landroid/net/IpSecTransform;->mContext:Landroid/content/Context; -Landroid/net/IpSecTransform;->mKeepalive:Landroid/net/ConnectivityManager$PacketKeepalive; -Landroid/net/IpSecTransform;->mKeepaliveCallback:Landroid/net/ConnectivityManager$PacketKeepaliveCallback; -Landroid/net/IpSecTransform;->MODE_TRANSPORT:I -Landroid/net/IpSecTransform;->MODE_TUNNEL:I -Landroid/net/IpSecTransform;->mResourceId:I -Landroid/net/IpSecTransform;->mUserKeepaliveCallback:Landroid/net/IpSecTransform$NattKeepaliveCallback; -Landroid/net/IpSecTransform;->startNattKeepalive(Landroid/net/IpSecTransform$NattKeepaliveCallback;ILandroid/os/Handler;)V -Landroid/net/IpSecTransform;->stopNattKeepalive()V -Landroid/net/IpSecTransform;->TAG:Ljava/lang/String; -Landroid/net/IpSecTransformResponse;-><init>(I)V -Landroid/net/IpSecTransformResponse;-><init>(II)V -Landroid/net/IpSecTransformResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecTransformResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecTransformResponse;->resourceId:I -Landroid/net/IpSecTransformResponse;->status:I -Landroid/net/IpSecTransformResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(I)V -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(IILjava/lang/String;)V -Landroid/net/IpSecTunnelInterfaceResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecTunnelInterfaceResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecTunnelInterfaceResponse;->interfaceName:Ljava/lang/String; -Landroid/net/IpSecTunnelInterfaceResponse;->resourceId:I -Landroid/net/IpSecTunnelInterfaceResponse;->status:I -Landroid/net/IpSecTunnelInterfaceResponse;->TAG:Ljava/lang/String; -Landroid/net/IpSecUdpEncapResponse;-><init>(I)V -Landroid/net/IpSecUdpEncapResponse;-><init>(IIILjava/io/FileDescriptor;)V -Landroid/net/IpSecUdpEncapResponse;-><init>(Landroid/os/Parcel;)V -Landroid/net/IpSecUdpEncapResponse;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpSecUdpEncapResponse;->fileDescriptor:Landroid/os/ParcelFileDescriptor; -Landroid/net/IpSecUdpEncapResponse;->port:I -Landroid/net/IpSecUdpEncapResponse;->resourceId:I -Landroid/net/IpSecUdpEncapResponse;->status:I -Landroid/net/IpSecUdpEncapResponse;->TAG:Ljava/lang/String; Landroid/net/ITetheringStatsProvider$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getTetherStats(I)Landroid/net/NetworkStats; @@ -36742,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 40f589d6f169..0192ff063ac0 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); @@ -7611,7 +7621,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE"; field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME"; - field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; + field @Deprecated public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field @Deprecated public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; field public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT"; @@ -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(); @@ -9824,7 +9777,6 @@ package android.content { field public static final String STATUS_BAR_SERVICE = "statusbar"; field public static final String STORAGE_SERVICE = "storage"; field public static final String STORAGE_STATS_SERVICE = "storagestats"; - field public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process"; field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final String TELECOM_SERVICE = "telecom"; field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; @@ -11929,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"; @@ -15467,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); @@ -15766,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; @@ -15806,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); @@ -15970,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; @@ -16868,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"; @@ -16897,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 @@ -16905,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 @@ -17335,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; @@ -17434,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); @@ -17458,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); @@ -17669,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 @@ -18079,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(); @@ -18135,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(); @@ -18142,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 @@ -19677,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 } @@ -20196,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 { @@ -26392,75 +26373,6 @@ package android.net { method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); } - public final class IpSecAlgorithm implements android.os.Parcelable { - ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]); - ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int); - method public int describeContents(); - method @NonNull public byte[] getKey(); - method @NonNull public String getName(); - method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms(); - method public int getTruncationLengthBits(); - method public void writeToParcel(android.os.Parcel, int); - field public static final String AUTH_AES_CMAC = "cmac(aes)"; - field public static final String AUTH_AES_XCBC = "xcbc(aes)"; - field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; - field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)"; - field public static final String AUTH_HMAC_MD5 = "hmac(md5)"; - field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; - field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; - field public static final String AUTH_HMAC_SHA384 = "hmac(sha384)"; - field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; - field public static final String CRYPT_AES_CBC = "cbc(aes)"; - field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))"; - } - - public final class IpSecManager { - method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException; - method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException; - method public void removeTransportModeTransforms(@NonNull java.net.DatagramSocket) throws java.io.IOException; - method public void removeTransportModeTransforms(@NonNull java.io.FileDescriptor) throws java.io.IOException; - field public static final int DIRECTION_IN = 0; // 0x0 - field public static final int DIRECTION_OUT = 1; // 0x1 - } - - public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException { - } - - public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable { - method public void close(); - method public int getSpi(); - } - - public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException { - method public int getSpi(); - } - - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public void close() throws java.io.IOException; - method public java.io.FileDescriptor getFileDescriptor(); - method public int getPort(); - } - - public final class IpSecTransform implements java.lang.AutoCloseable { - method public void close(); - } - - public static class IpSecTransform.Builder { - ctor public IpSecTransform.Builder(@NonNull android.content.Context); - method @NonNull public android.net.IpSecTransform buildTransportModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - method @NonNull public android.net.IpSecTransform.Builder setAuthenticatedEncryption(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setAuthentication(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setEncryption(@NonNull android.net.IpSecAlgorithm); - method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int); - } - public class LocalServerSocket implements java.io.Closeable { ctor public LocalServerSocket(String) throws java.io.IOException; ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException; @@ -26587,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); @@ -39437,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); @@ -39444,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 { @@ -39461,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(); @@ -39469,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 { @@ -39524,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"; @@ -43010,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"; @@ -43104,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"; @@ -43287,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(); @@ -43347,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(); @@ -43392,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); @@ -43428,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 @@ -43502,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 @@ -47614,16 +47514,16 @@ package android.view { } public abstract class ActionProvider { - ctor public ActionProvider(android.content.Context); + ctor public ActionProvider(@NonNull android.content.Context); method public boolean hasSubMenu(); method public boolean isVisible(); - method @Deprecated public abstract android.view.View onCreateActionView(); - method public android.view.View onCreateActionView(android.view.MenuItem); + method @Deprecated @NonNull public abstract android.view.View onCreateActionView(); + method @NonNull public android.view.View onCreateActionView(@NonNull android.view.MenuItem); method public boolean onPerformDefaultAction(); - method public void onPrepareSubMenu(android.view.SubMenu); + method public void onPrepareSubMenu(@NonNull android.view.SubMenu); method public boolean overridesItemVisibility(); method public void refreshVisibility(); - method public void setVisibilityListener(android.view.ActionProvider.VisibilityListener); + method public void setVisibilityListener(@Nullable android.view.ActionProvider.VisibilityListener); } public static interface ActionProvider.VisibilityListener { @@ -47893,60 +47793,60 @@ package android.view { } public class GestureDetector { - ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener, android.os.Handler); - ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener); - ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener); - ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler); - ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean); + ctor @Deprecated public GestureDetector(@NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler); + ctor @Deprecated public GestureDetector(@NonNull android.view.GestureDetector.OnGestureListener); + ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener); + ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler); + ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler, boolean); method public boolean isLongpressEnabled(); - method public boolean onGenericMotionEvent(android.view.MotionEvent); - method public boolean onTouchEvent(android.view.MotionEvent); - method public void setContextClickListener(android.view.GestureDetector.OnContextClickListener); + method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent); + method public boolean onTouchEvent(@NonNull android.view.MotionEvent); + method public void setContextClickListener(@Nullable android.view.GestureDetector.OnContextClickListener); method public void setIsLongpressEnabled(boolean); - method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener); + method public void setOnDoubleTapListener(@Nullable android.view.GestureDetector.OnDoubleTapListener); } public static interface GestureDetector.OnContextClickListener { - method public boolean onContextClick(android.view.MotionEvent); + method public boolean onContextClick(@NonNull android.view.MotionEvent); } public static interface GestureDetector.OnDoubleTapListener { - method public boolean onDoubleTap(android.view.MotionEvent); - method public boolean onDoubleTapEvent(android.view.MotionEvent); - method public boolean onSingleTapConfirmed(android.view.MotionEvent); + method public boolean onDoubleTap(@NonNull android.view.MotionEvent); + method public boolean onDoubleTapEvent(@NonNull android.view.MotionEvent); + method public boolean onSingleTapConfirmed(@NonNull android.view.MotionEvent); } public static interface GestureDetector.OnGestureListener { - method public boolean onDown(android.view.MotionEvent); - method public boolean onFling(android.view.MotionEvent, android.view.MotionEvent, float, float); - method public void onLongPress(android.view.MotionEvent); - method public boolean onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float); - method public void onShowPress(android.view.MotionEvent); - method public boolean onSingleTapUp(android.view.MotionEvent); + method public boolean onDown(@NonNull android.view.MotionEvent); + method public boolean onFling(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float); + method public void onLongPress(@NonNull android.view.MotionEvent); + method public boolean onScroll(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float); + method public void onShowPress(@NonNull android.view.MotionEvent); + method public boolean onSingleTapUp(@NonNull android.view.MotionEvent); } public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnContextClickListener android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener { ctor public GestureDetector.SimpleOnGestureListener(); - method public boolean onContextClick(android.view.MotionEvent); - method public boolean onDoubleTap(android.view.MotionEvent); - method public boolean onDoubleTapEvent(android.view.MotionEvent); - method public boolean onDown(android.view.MotionEvent); - method public boolean onFling(android.view.MotionEvent, android.view.MotionEvent, float, float); - method public void onLongPress(android.view.MotionEvent); - method public boolean onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float); - method public void onShowPress(android.view.MotionEvent); - method public boolean onSingleTapConfirmed(android.view.MotionEvent); - method public boolean onSingleTapUp(android.view.MotionEvent); + method public boolean onContextClick(@NonNull android.view.MotionEvent); + method public boolean onDoubleTap(@NonNull android.view.MotionEvent); + method public boolean onDoubleTapEvent(@NonNull android.view.MotionEvent); + method public boolean onDown(@NonNull android.view.MotionEvent); + method public boolean onFling(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float); + method public void onLongPress(@NonNull android.view.MotionEvent); + method public boolean onScroll(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float); + method public void onShowPress(@NonNull android.view.MotionEvent); + method public boolean onSingleTapConfirmed(@NonNull android.view.MotionEvent); + method public boolean onSingleTapUp(@NonNull android.view.MotionEvent); } public class Gravity { ctor public Gravity(); method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect); - method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int); - method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect); - method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int); - method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect); - method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int); + method public static void apply(int, int, int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect, int); + method public static void apply(int, int, int, @NonNull android.graphics.Rect, int, int, @NonNull android.graphics.Rect); + method public static void apply(int, int, int, @NonNull android.graphics.Rect, int, int, @NonNull android.graphics.Rect, int); + method public static void applyDisplay(int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect); + method public static void applyDisplay(int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect, int); method public static int getAbsoluteGravity(int, int); method public static boolean isHorizontal(int); method public static boolean isVertical(int); @@ -48678,60 +48578,60 @@ package android.view { public interface MenuItem { method public boolean collapseActionView(); method public boolean expandActionView(); - method public android.view.ActionProvider getActionProvider(); - method public android.view.View getActionView(); + method @Nullable public android.view.ActionProvider getActionProvider(); + method @Nullable public android.view.View getActionView(); method public default int getAlphabeticModifiers(); method public char getAlphabeticShortcut(); - method public default CharSequence getContentDescription(); + method @Nullable public default CharSequence getContentDescription(); method public int getGroupId(); - method public android.graphics.drawable.Drawable getIcon(); + method @Nullable public android.graphics.drawable.Drawable getIcon(); method @Nullable public default android.graphics.BlendMode getIconTintBlendMode(); method @Nullable public default android.content.res.ColorStateList getIconTintList(); method @Nullable public default android.graphics.PorterDuff.Mode getIconTintMode(); - method public android.content.Intent getIntent(); + method @Nullable public android.content.Intent getIntent(); method public int getItemId(); - method public android.view.ContextMenu.ContextMenuInfo getMenuInfo(); + method @Nullable public android.view.ContextMenu.ContextMenuInfo getMenuInfo(); method public default int getNumericModifiers(); method public char getNumericShortcut(); method public int getOrder(); - method public android.view.SubMenu getSubMenu(); - method public CharSequence getTitle(); - method public CharSequence getTitleCondensed(); - method public default CharSequence getTooltipText(); + method @Nullable public android.view.SubMenu getSubMenu(); + method @Nullable public CharSequence getTitle(); + method @Nullable public CharSequence getTitleCondensed(); + method @Nullable public default CharSequence getTooltipText(); method public boolean hasSubMenu(); method public boolean isActionViewExpanded(); method public boolean isCheckable(); method public boolean isChecked(); method public boolean isEnabled(); method public boolean isVisible(); - method public android.view.MenuItem setActionProvider(android.view.ActionProvider); - method public android.view.MenuItem setActionView(android.view.View); - method public android.view.MenuItem setActionView(@LayoutRes int); - method public android.view.MenuItem setAlphabeticShortcut(char); - method public default android.view.MenuItem setAlphabeticShortcut(char, int); - method public android.view.MenuItem setCheckable(boolean); - method public android.view.MenuItem setChecked(boolean); - method public default android.view.MenuItem setContentDescription(CharSequence); - method public android.view.MenuItem setEnabled(boolean); - method public android.view.MenuItem setIcon(android.graphics.drawable.Drawable); - method public android.view.MenuItem setIcon(@DrawableRes int); + method @NonNull public android.view.MenuItem setActionProvider(@Nullable android.view.ActionProvider); + method @NonNull public android.view.MenuItem setActionView(@Nullable android.view.View); + method @NonNull public android.view.MenuItem setActionView(@LayoutRes int); + method @NonNull public android.view.MenuItem setAlphabeticShortcut(char); + method @NonNull public default android.view.MenuItem setAlphabeticShortcut(char, int); + method @NonNull public android.view.MenuItem setCheckable(boolean); + method @NonNull public android.view.MenuItem setChecked(boolean); + method @NonNull public default android.view.MenuItem setContentDescription(@Nullable CharSequence); + method @NonNull public android.view.MenuItem setEnabled(boolean); + method @NonNull public android.view.MenuItem setIcon(@Nullable android.graphics.drawable.Drawable); + method @NonNull public android.view.MenuItem setIcon(@DrawableRes int); method @NonNull public default android.view.MenuItem setIconTintBlendMode(@Nullable android.graphics.BlendMode); - method public default android.view.MenuItem setIconTintList(@Nullable android.content.res.ColorStateList); + method @NonNull public default android.view.MenuItem setIconTintList(@Nullable android.content.res.ColorStateList); method @NonNull public default android.view.MenuItem setIconTintMode(@Nullable android.graphics.PorterDuff.Mode); - method public android.view.MenuItem setIntent(android.content.Intent); - method public android.view.MenuItem setNumericShortcut(char); - method public default android.view.MenuItem setNumericShortcut(char, int); - method public android.view.MenuItem setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener); - method public android.view.MenuItem setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener); - method public android.view.MenuItem setShortcut(char, char); - method public default android.view.MenuItem setShortcut(char, char, int, int); + method @NonNull public android.view.MenuItem setIntent(@Nullable android.content.Intent); + method @NonNull public android.view.MenuItem setNumericShortcut(char); + method @NonNull public default android.view.MenuItem setNumericShortcut(char, int); + method @NonNull public android.view.MenuItem setOnActionExpandListener(@Nullable android.view.MenuItem.OnActionExpandListener); + method @NonNull public android.view.MenuItem setOnMenuItemClickListener(@Nullable android.view.MenuItem.OnMenuItemClickListener); + method @NonNull public android.view.MenuItem setShortcut(char, char); + method @NonNull public default android.view.MenuItem setShortcut(char, char, int, int); method public void setShowAsAction(int); - method public android.view.MenuItem setShowAsActionFlags(int); - method public android.view.MenuItem setTitle(CharSequence); - method public android.view.MenuItem setTitle(@StringRes int); - method public android.view.MenuItem setTitleCondensed(CharSequence); - method public default android.view.MenuItem setTooltipText(CharSequence); - method public android.view.MenuItem setVisible(boolean); + method @NonNull public android.view.MenuItem setShowAsActionFlags(int); + method @NonNull public android.view.MenuItem setTitle(@Nullable CharSequence); + method @NonNull public android.view.MenuItem setTitle(@StringRes int); + method @NonNull public android.view.MenuItem setTitleCondensed(@Nullable CharSequence); + method @NonNull public default android.view.MenuItem setTooltipText(@Nullable CharSequence); + method @NonNull public android.view.MenuItem setVisible(boolean); field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2 field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8 field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1 @@ -48740,12 +48640,12 @@ package android.view { } public static interface MenuItem.OnActionExpandListener { - method public boolean onMenuItemActionCollapse(android.view.MenuItem); - method public boolean onMenuItemActionExpand(android.view.MenuItem); + method public boolean onMenuItemActionCollapse(@NonNull android.view.MenuItem); + method public boolean onMenuItemActionExpand(@NonNull android.view.MenuItem); } public static interface MenuItem.OnMenuItemClickListener { - method public boolean onMenuItemClick(android.view.MenuItem); + method public boolean onMenuItemClick(@NonNull android.view.MenuItem); } public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable { @@ -48964,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 { @@ -49021,10 +48920,10 @@ package android.view { } public final class PointerIcon implements android.os.Parcelable { - method public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float); + method @NonNull public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float); method public int describeContents(); - method public static android.view.PointerIcon getSystemIcon(@NonNull android.content.Context, int); - method public static android.view.PointerIcon load(@NonNull android.content.res.Resources, @XmlRes int); + method @NonNull public static android.view.PointerIcon getSystemIcon(@NonNull android.content.Context, int); + method @NonNull public static android.view.PointerIcon load(@NonNull android.content.res.Resources, @XmlRes int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR; field public static final int TYPE_ALIAS = 1010; // 0x3f2 @@ -49067,8 +48966,8 @@ package android.view { } public class ScaleGestureDetector { - ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener); - ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler); + ctor public ScaleGestureDetector(@NonNull android.content.Context, @NonNull android.view.ScaleGestureDetector.OnScaleGestureListener); + ctor public ScaleGestureDetector(@NonNull android.content.Context, @NonNull android.view.ScaleGestureDetector.OnScaleGestureListener, @Nullable android.os.Handler); method public float getCurrentSpan(); method public float getCurrentSpanX(); method public float getCurrentSpanY(); @@ -49083,22 +48982,22 @@ package android.view { method public boolean isInProgress(); method public boolean isQuickScaleEnabled(); method public boolean isStylusScaleEnabled(); - method public boolean onTouchEvent(android.view.MotionEvent); + method public boolean onTouchEvent(@NonNull android.view.MotionEvent); method public void setQuickScaleEnabled(boolean); method public void setStylusScaleEnabled(boolean); } public static interface ScaleGestureDetector.OnScaleGestureListener { - method public boolean onScale(android.view.ScaleGestureDetector); - method public boolean onScaleBegin(android.view.ScaleGestureDetector); - method public void onScaleEnd(android.view.ScaleGestureDetector); + method public boolean onScale(@NonNull android.view.ScaleGestureDetector); + method public boolean onScaleBegin(@NonNull android.view.ScaleGestureDetector); + method public void onScaleEnd(@NonNull android.view.ScaleGestureDetector); } public static class ScaleGestureDetector.SimpleOnScaleGestureListener implements android.view.ScaleGestureDetector.OnScaleGestureListener { ctor public ScaleGestureDetector.SimpleOnScaleGestureListener(); - method public boolean onScale(android.view.ScaleGestureDetector); - method public boolean onScaleBegin(android.view.ScaleGestureDetector); - method public void onScaleEnd(android.view.ScaleGestureDetector); + method public boolean onScale(@NonNull android.view.ScaleGestureDetector); + method public boolean onScaleBegin(@NonNull android.view.ScaleGestureDetector); + method public void onScaleEnd(@NonNull android.view.ScaleGestureDetector); } @UiThread public interface ScrollCaptureCallback { @@ -49216,7 +49115,7 @@ package android.view { public static class SurfaceControl.Transaction implements java.io.Closeable android.os.Parcelable { ctor public SurfaceControl.Transaction(); - method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.TransactionCommittedListener); + method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.SurfaceControl.TransactionCommittedListener); method public void apply(); method public void close(); method public int describeContents(); @@ -49240,6 +49139,10 @@ package android.view { field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR; } + public static interface SurfaceControl.TransactionCommittedListener { + method public void onTransactionCommitted(); + } + public class SurfaceControlViewHost { ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder); method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage(); @@ -49352,10 +49255,6 @@ package android.view { field public static final int TO_RIGHT = 8; // 0x8 } - public interface TransactionCommittedListener { - method public void onTransactionCommitted(); - } - public final class VelocityTracker { method public void addMovement(android.view.MotionEvent); method public void clear(); @@ -49401,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); @@ -49469,7 +49368,7 @@ package android.view { method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent); method public boolean dispatchNestedFling(float, float, boolean); method public boolean dispatchNestedPreFling(float, float); - method public boolean dispatchNestedPrePerformAccessibilityAction(int, android.os.Bundle); + method public boolean dispatchNestedPrePerformAccessibilityAction(int, @Nullable android.os.Bundle); method public boolean dispatchNestedPreScroll(int, int, @Nullable @Size(2) int[], @Nullable @Size(2) int[]); method public boolean dispatchNestedScroll(int, int, int, int, @Nullable @Size(2) int[]); method public void dispatchPointerCaptureChanged(boolean); @@ -49605,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(); @@ -49831,7 +49729,7 @@ package android.view { method @Deprecated public void onWindowSystemUiVisibilityChanged(int); method protected void onWindowVisibilityChanged(int); method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean); - method public boolean performAccessibilityAction(int, android.os.Bundle); + method public boolean performAccessibilityAction(int, @Nullable android.os.Bundle); method public boolean performClick(); method public boolean performContextClick(float, float); method public boolean performContextClick(); @@ -50242,15 +50140,15 @@ package android.view { public static class View.AccessibilityDelegate { ctor public View.AccessibilityDelegate(); method public void addExtraDataToAccessibilityNodeInfo(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityNodeInfo, @NonNull String, @Nullable android.os.Bundle); - method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); - method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View); - method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); - method public void onInitializeAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo); - method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); - method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent); - method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle); - method public void sendAccessibilityEvent(android.view.View, int); - method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent); + method public boolean dispatchPopulateAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent); + method @Nullable public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(@NonNull android.view.View); + method public void onInitializeAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent); + method public void onInitializeAccessibilityNodeInfo(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityNodeInfo); + method public void onPopulateAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent); + method public boolean onRequestSendAccessibilityEvent(@NonNull android.view.ViewGroup, @NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent); + method public boolean performAccessibilityAction(@NonNull android.view.View, int, @Nullable android.os.Bundle); + method public void sendAccessibilityEvent(@NonNull android.view.View, int); + method public void sendAccessibilityEventUnchecked(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent); } public static class View.BaseSavedState extends android.view.AbsSavedState { @@ -50280,12 +50178,12 @@ package android.view { } public static interface View.OnApplyWindowInsetsListener { - method public android.view.WindowInsets onApplyWindowInsets(android.view.View, android.view.WindowInsets); + method @NonNull public android.view.WindowInsets onApplyWindowInsets(@NonNull android.view.View, @NonNull android.view.WindowInsets); } public static interface View.OnAttachStateChangeListener { - method public void onViewAttachedToWindow(android.view.View); - method public void onViewDetachedFromWindow(android.view.View); + method public void onViewAttachedToWindow(@NonNull android.view.View); + method public void onViewDetachedFromWindow(@NonNull android.view.View); } public static interface View.OnCapturedPointerListener { @@ -50354,7 +50252,7 @@ package android.view { public class ViewConfiguration { ctor @Deprecated public ViewConfiguration(); - method public static android.view.ViewConfiguration get(@UiContext android.content.Context); + method public static android.view.ViewConfiguration get(@NonNull @UiContext android.content.Context); method @Deprecated @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier(); method public static long getDefaultActionModeHideDuration(); method public static int getDoubleTapTimeout(); @@ -50673,8 +50571,8 @@ package android.view { method public boolean canResolveLayoutDirection(); method public boolean canResolveTextAlignment(); method public boolean canResolveTextDirection(); - method public void childDrawableStateChanged(android.view.View); - method public void childHasTransientStateChanged(android.view.View, boolean); + method public void childDrawableStateChanged(@NonNull android.view.View); + method public void childHasTransientStateChanged(@NonNull android.view.View, boolean); method public void clearChildFocus(android.view.View); method public void createContextMenu(android.view.ContextMenu); method public android.view.View focusSearch(android.view.View, int); @@ -50692,23 +50590,23 @@ package android.view { method public boolean isTextAlignmentResolved(); method public boolean isTextDirectionResolved(); method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); - method public void notifySubtreeAccessibilityStateChanged(android.view.View, @NonNull android.view.View, int); + method public void notifySubtreeAccessibilityStateChanged(@NonNull android.view.View, @NonNull android.view.View, int); method public default void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View); - method public boolean onNestedFling(android.view.View, float, float, boolean); - method public boolean onNestedPreFling(android.view.View, float, float); - method public boolean onNestedPrePerformAccessibilityAction(android.view.View, int, android.os.Bundle); - method public void onNestedPreScroll(android.view.View, int, int, int[]); - method public void onNestedScroll(android.view.View, int, int, int, int); - method public void onNestedScrollAccepted(android.view.View, android.view.View, int); - method public boolean onStartNestedScroll(android.view.View, android.view.View, int); - method public void onStopNestedScroll(android.view.View); + method public boolean onNestedFling(@NonNull android.view.View, float, float, boolean); + method public boolean onNestedPreFling(@NonNull android.view.View, float, float); + method public boolean onNestedPrePerformAccessibilityAction(@NonNull android.view.View, int, @Nullable android.os.Bundle); + method public void onNestedPreScroll(@NonNull android.view.View, int, int, @NonNull int[]); + method public void onNestedScroll(@NonNull android.view.View, int, int, int, int); + method public void onNestedScrollAccepted(@NonNull android.view.View, @NonNull android.view.View, int); + method public boolean onStartNestedScroll(@NonNull android.view.View, @NonNull android.view.View, int); + method public void onStopNestedScroll(@NonNull android.view.View); method public void recomputeViewAttributes(android.view.View); method public void requestChildFocus(android.view.View, android.view.View); - method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean); + method public boolean requestChildRectangleOnScreen(@NonNull android.view.View, android.graphics.Rect, boolean); method public void requestDisallowInterceptTouchEvent(boolean); method public void requestFitSystemWindows(); method public void requestLayout(); - method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); + method public boolean requestSendAccessibilityEvent(@NonNull android.view.View, android.view.accessibility.AccessibilityEvent); method public void requestTransparentRegion(android.view.View); method public boolean showContextMenuForChild(android.view.View); method public boolean showContextMenuForChild(android.view.View, float, float); @@ -50717,43 +50615,43 @@ package android.view { } public class ViewPropertyAnimator { - method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float); - method public android.view.ViewPropertyAnimator alphaBy(float); + method @NonNull public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float); + method @NonNull public android.view.ViewPropertyAnimator alphaBy(float); method public void cancel(); method public long getDuration(); - method public android.animation.TimeInterpolator getInterpolator(); + method @Nullable public android.animation.TimeInterpolator getInterpolator(); method public long getStartDelay(); - method public android.view.ViewPropertyAnimator rotation(float); - method public android.view.ViewPropertyAnimator rotationBy(float); - method public android.view.ViewPropertyAnimator rotationX(float); - method public android.view.ViewPropertyAnimator rotationXBy(float); - method public android.view.ViewPropertyAnimator rotationY(float); - method public android.view.ViewPropertyAnimator rotationYBy(float); - method public android.view.ViewPropertyAnimator scaleX(float); - method public android.view.ViewPropertyAnimator scaleXBy(float); - method public android.view.ViewPropertyAnimator scaleY(float); - method public android.view.ViewPropertyAnimator scaleYBy(float); - method public android.view.ViewPropertyAnimator setDuration(long); - method public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator); - method public android.view.ViewPropertyAnimator setListener(android.animation.Animator.AnimatorListener); - method public android.view.ViewPropertyAnimator setStartDelay(long); - method public android.view.ViewPropertyAnimator setUpdateListener(android.animation.ValueAnimator.AnimatorUpdateListener); + method @NonNull public android.view.ViewPropertyAnimator rotation(float); + method @NonNull public android.view.ViewPropertyAnimator rotationBy(float); + method @NonNull public android.view.ViewPropertyAnimator rotationX(float); + method @NonNull public android.view.ViewPropertyAnimator rotationXBy(float); + method @NonNull public android.view.ViewPropertyAnimator rotationY(float); + method @NonNull public android.view.ViewPropertyAnimator rotationYBy(float); + method @NonNull public android.view.ViewPropertyAnimator scaleX(float); + method @NonNull public android.view.ViewPropertyAnimator scaleXBy(float); + method @NonNull public android.view.ViewPropertyAnimator scaleY(float); + method @NonNull public android.view.ViewPropertyAnimator scaleYBy(float); + method @NonNull public android.view.ViewPropertyAnimator setDuration(long); + method @NonNull public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator); + method @NonNull public android.view.ViewPropertyAnimator setListener(@Nullable android.animation.Animator.AnimatorListener); + method @NonNull public android.view.ViewPropertyAnimator setStartDelay(long); + method @NonNull public android.view.ViewPropertyAnimator setUpdateListener(@Nullable android.animation.ValueAnimator.AnimatorUpdateListener); method public void start(); - method public android.view.ViewPropertyAnimator translationX(float); - method public android.view.ViewPropertyAnimator translationXBy(float); - method public android.view.ViewPropertyAnimator translationY(float); - method public android.view.ViewPropertyAnimator translationYBy(float); - method public android.view.ViewPropertyAnimator translationZ(float); - method public android.view.ViewPropertyAnimator translationZBy(float); - method public android.view.ViewPropertyAnimator withEndAction(Runnable); - method public android.view.ViewPropertyAnimator withLayer(); - method public android.view.ViewPropertyAnimator withStartAction(Runnable); - method public android.view.ViewPropertyAnimator x(float); - method public android.view.ViewPropertyAnimator xBy(float); - method public android.view.ViewPropertyAnimator y(float); - method public android.view.ViewPropertyAnimator yBy(float); - method public android.view.ViewPropertyAnimator z(float); - method public android.view.ViewPropertyAnimator zBy(float); + method @NonNull public android.view.ViewPropertyAnimator translationX(float); + method @NonNull public android.view.ViewPropertyAnimator translationXBy(float); + method @NonNull public android.view.ViewPropertyAnimator translationY(float); + method @NonNull public android.view.ViewPropertyAnimator translationYBy(float); + method @NonNull public android.view.ViewPropertyAnimator translationZ(float); + method @NonNull public android.view.ViewPropertyAnimator translationZBy(float); + method @NonNull public android.view.ViewPropertyAnimator withEndAction(Runnable); + method @NonNull public android.view.ViewPropertyAnimator withLayer(); + method @NonNull public android.view.ViewPropertyAnimator withStartAction(Runnable); + method @NonNull public android.view.ViewPropertyAnimator x(float); + method @NonNull public android.view.ViewPropertyAnimator xBy(float); + method @NonNull public android.view.ViewPropertyAnimator y(float); + method @NonNull public android.view.ViewPropertyAnimator yBy(float); + method @NonNull public android.view.ViewPropertyAnimator z(float); + method @NonNull public android.view.ViewPropertyAnimator zBy(float); } public abstract class ViewStructure { @@ -51498,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); @@ -51509,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 @@ -51524,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 @@ -51944,10 +51849,10 @@ package android.view.accessibility { public abstract class AccessibilityNodeProvider { ctor public AccessibilityNodeProvider(); method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, String, android.os.Bundle); - method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int); - method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String, int); - method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); - method public boolean performAction(int, int, android.os.Bundle); + method @Nullable public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int); + method @Nullable public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String, int); + method @Nullable public android.view.accessibility.AccessibilityNodeInfo findFocus(int); + method public boolean performAction(int, int, @Nullable android.os.Bundle); field public static final int HOST_VIEW_ID = -1; // 0xffffffff } @@ -51955,23 +51860,23 @@ package android.view.accessibility { ctor public AccessibilityRecord(); ctor public AccessibilityRecord(@NonNull android.view.accessibility.AccessibilityRecord); method public int getAddedCount(); - method public CharSequence getBeforeText(); - method public CharSequence getClassName(); - method public CharSequence getContentDescription(); + method @Nullable public CharSequence getBeforeText(); + method @Nullable public CharSequence getClassName(); + method @Nullable public CharSequence getContentDescription(); method public int getCurrentItemIndex(); method public int getDisplayId(); method public int getFromIndex(); method public int getItemCount(); method public int getMaxScrollX(); method public int getMaxScrollY(); - method public android.os.Parcelable getParcelableData(); + method @Nullable public android.os.Parcelable getParcelableData(); method public int getRemovedCount(); method public int getScrollDeltaX(); method public int getScrollDeltaY(); method public int getScrollX(); method public int getScrollY(); - method public android.view.accessibility.AccessibilityNodeInfo getSource(); - method public java.util.List<java.lang.CharSequence> getText(); + method @Nullable public android.view.accessibility.AccessibilityNodeInfo getSource(); + method @NonNull public java.util.List<java.lang.CharSequence> getText(); method public int getToIndex(); method public int getWindowId(); method public boolean isChecked(); @@ -51979,14 +51884,14 @@ package android.view.accessibility { method public boolean isFullScreen(); method public boolean isPassword(); method public boolean isScrollable(); - method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord); - method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain(); + method @Deprecated @NonNull public static android.view.accessibility.AccessibilityRecord obtain(@NonNull android.view.accessibility.AccessibilityRecord); + method @Deprecated @NonNull public static android.view.accessibility.AccessibilityRecord obtain(); method @Deprecated public void recycle(); method public void setAddedCount(int); - method public void setBeforeText(CharSequence); + method public void setBeforeText(@Nullable CharSequence); method public void setChecked(boolean); - method public void setClassName(CharSequence); - method public void setContentDescription(CharSequence); + method public void setClassName(@Nullable CharSequence); + method public void setContentDescription(@Nullable CharSequence); method public void setCurrentItemIndex(int); method public void setEnabled(boolean); method public void setFromIndex(int); @@ -51994,7 +51899,7 @@ package android.view.accessibility { method public void setItemCount(int); method public void setMaxScrollX(int); method public void setMaxScrollY(int); - method public void setParcelableData(android.os.Parcelable); + method public void setParcelableData(@Nullable android.os.Parcelable); method public void setPassword(boolean); method public void setRemovedCount(int); method public void setScrollDeltaX(int); @@ -52002,7 +51907,7 @@ package android.view.accessibility { method public void setScrollX(int); method public void setScrollY(int); method public void setScrollable(boolean); - method public void setSource(android.view.View); + method public void setSource(@Nullable android.view.View); method public void setSource(@Nullable android.view.View, int); method public void setToIndex(int); } @@ -52325,7 +52230,7 @@ package android.view.animation { } public class PathInterpolator extends android.view.animation.BaseInterpolator { - ctor public PathInterpolator(android.graphics.Path); + ctor public PathInterpolator(@NonNull android.graphics.Path); ctor public PathInterpolator(float, float); ctor public PathInterpolator(float, float, float, float); ctor public PathInterpolator(android.content.Context, android.util.AttributeSet); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 6e7bc765c157..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 } } @@ -192,7 +172,7 @@ package android.media { method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp(); method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio(); - method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); @@ -202,18 +182,18 @@ package android.media { field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } - public final class BtProfileConnectionInfo implements android.os.Parcelable { - method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int); - method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int); + public final class BluetoothProfileConnectionInfo implements android.os.Parcelable { + method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpInfo(boolean, int); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpSinkInfo(int); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createHearingAidInfo(boolean); + method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioInfo(boolean, boolean); method public int describeContents(); - method public boolean getIsLeOutput(); method public int getProfile(); - method public boolean getSuppressNoisyIntent(); method public int getVolume(); - method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean); - method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean); + method public boolean isLeOutput(); + method public boolean isSuppressNoisyIntent(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.media.BluetoothProfileConnectionInfo> CREATOR; } public class MediaMetadataRetriever implements java.lang.AutoCloseable { @@ -278,44 +258,10 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR; } - public final class IpSecManager { - field public static final int DIRECTION_FWD = 2; // 0x2 - } - - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { - method public int getResourceId(); - } - public class LocalSocket implements java.io.Closeable { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } - 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); @@ -331,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(); } @@ -437,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 dbaf47c0a97c..02f05d255eb8 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"; @@ -910,6 +911,19 @@ package android.app { method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info); + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable); + field public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; // 0x0 + field public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; // 0x1 + field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; // 0x1 + field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; // 0x0 + field public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; // 0x8 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; // 0x6 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; // 0x4 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; // 0x2 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; // 0x7 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; // 0x5 + field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; // 0x3 field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1 field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0 } @@ -1071,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(); @@ -1083,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); @@ -1161,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"; @@ -2237,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; } @@ -2248,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 { @@ -2266,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; } @@ -2283,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; } @@ -2308,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; } @@ -2326,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); } @@ -2389,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 { @@ -2498,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(); @@ -2728,6 +2761,7 @@ package android.companion.virtual { method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.LaunchCallback); method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener); + method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean); } public final class VirtualDeviceParams implements android.os.Parcelable { @@ -2813,8 +2847,8 @@ package android.content { field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; - field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 - field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 + field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 + field @Deprecated public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String CLOUDSEARCH_SERVICE = "cloudsearch"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -5912,11 +5946,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); @@ -6073,6 +6116,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(); @@ -7019,8 +7066,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(); @@ -7939,7 +7986,7 @@ package android.media.tv.tuner.frontend { public class FrontendStatus { method public int getAgc(); - method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpInfo[] getAllAtsc3PlpInfo(); + method @NonNull public java.util.List<android.media.tv.tuner.frontend.Atsc3PlpInfo> getAllAtsc3PlpInfo(); method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo(); method public int getBandwidth(); method public int getBer(); @@ -8323,23 +8370,6 @@ package android.net { method public void release(); } - public final class IpSecManager { - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - } - - public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException; - method public void close(); - method @NonNull public String getInterfaceName(); - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void setUnderlyingNetwork(@NonNull android.net.Network) throws java.io.IOException; - } - - public static class IpSecTransform.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; - } - public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); @@ -8401,48 +8431,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); @@ -8474,19 +8462,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(); } @@ -8667,23 +8642,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 { @@ -9655,7 +9613,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(); @@ -9951,7 +9909,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 @@ -10400,8 +10359,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"; @@ -11125,6 +11082,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 @@ -11201,6 +11159,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); } @@ -11210,6 +11169,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); @@ -12238,6 +12202,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"; @@ -13181,7 +13146,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>); @@ -13227,7 +13191,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(); @@ -13282,7 +13245,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); @@ -13332,10 +13294,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 @@ -13375,26 +13335,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 @@ -14721,6 +14661,7 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR; field public static final String RCS_PROFILE_1_0 = "UP_1.0"; field public static final String RCS_PROFILE_2_3 = "UP_2.3"; + field public static final String RCS_PROFILE_2_4 = "UP_2.4"; } public final class RcsContactPresenceTuple implements android.os.Parcelable { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index e17a9bb1512c..1b45e88584fe 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1,11 +1,5 @@ // Baseline format: 1.0 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): - - - -BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex): - Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex) - ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener` @@ -15,8 +9,6 @@ GenericException: android.app.prediction.AppPredictor#finalize(): GenericException: android.hardware.location.ContextHubClient#finalize(): -GenericException: android.net.IpSecManager.IpSecTunnelInterface#finalize(): - GenericException: android.service.autofill.augmented.FillWindow#finalize(): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 82742bc8e437..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 { @@ -1607,10 +1606,6 @@ package android.net { method public void setIncludeTestInterfaces(boolean); } - public final class IpSecManager { - field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 - } - public class NetworkPolicyManager { method public boolean getRestrictBackground(); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); @@ -1627,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 { @@ -2522,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); @@ -2744,6 +2731,7 @@ package android.view { method @NonNull public android.view.Display.Mode getDefaultMode(); method @NonNull public int[] getReportedHdrTypes(); method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); + method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode(); method public int getType(); method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode(); method public boolean hasAccess(int); @@ -2803,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 ""; } @@ -2819,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..e0227006aef8 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 @@ -7171,7 +7209,7 @@ public class Activity extends ContextThemeWrapper // Handle special cases switch (args[0]) { case "--autofill": - getAutofillClientController().dumpAutofillManager(prefix, writer); + dumpAutofillManager(prefix, writer, args); return; case "--contentcapture": dumpContentCaptureManager(prefix, writer); @@ -7239,10 +7277,6 @@ public class Activity extends ContextThemeWrapper mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); - getAutofillClientController().dumpAutofillManager(prefix, writer); - dumpContentCaptureManager(prefix, writer); - dumpUiTranslation(prefix, writer); - ResourcesManager.getInstance().dump(prefix, writer); if (mDumpableContainer != null) { @@ -7250,21 +7284,26 @@ public class Activity extends ContextThemeWrapper } } - void dumpContentCaptureManager(String prefix, PrintWriter writer) { - final ContentCaptureManager cm = getContentCaptureManager(); - if (cm != null) { - cm.dump(prefix, writer); - } else { - writer.print(prefix); writer.println("No ContentCaptureManager"); - } + private void dumpContentCaptureManager(String prefix, PrintWriter writer) { + getContentCaptureManager(); + dumpLegacyDumpable(prefix, writer, ContentCaptureManager.DUMPABLE_NAME, /* args= */ null); } - void dumpUiTranslation(String prefix, PrintWriter writer) { - if (mUiTranslationController != null) { - mUiTranslationController.dump(prefix, writer); - } else { - writer.print(prefix); writer.println("No UiTranslationController"); + private void dumpUiTranslation(String prefix, PrintWriter writer) { + dumpLegacyDumpable(prefix, writer, UiTranslationController.DUMPABLE_NAME, /* args= */ null); + } + + private void dumpAutofillManager(String prefix, PrintWriter writer, String[] args) { + dumpLegacyDumpable(prefix, writer, AutofillClientController.DUMPABLE_NAME, args); + } + + private void dumpLegacyDumpable(@NonNull String prefix, @NonNull PrintWriter writer, + @NonNull String dumpableName, @Nullable String[] args) { + if (mDumpableContainer == null) { + writer.print(prefix); writer.print("no "); writer.println(dumpableName); + return; } + mDumpableContainer.dumpOneDumpable(prefix, writer, dumpableName, args); } /** @@ -8742,17 +8781,15 @@ public class Activity extends ContextThemeWrapper * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this * activity is attached to. * - * Returns null if the activity is not attached to a window with a decor. + * @throws IllegalStateException if this Activity is not visual. */ - @Nullable + @NonNull @Override public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - if (mWindow != null) { - View decorView = mWindow.getDecorView(); - if (decorView != null) { - return decorView.getOnBackInvokedDispatcher(); - } + if (mWindow == null) { + throw new IllegalStateException("OnBackInvokedDispatcher are not available on " + + "non-visual activities"); } - return null; + return ((OnBackInvokedDispatcherOwner) mWindow).getOnBackInvokedDispatcher(); } } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index cce7dd338b3d..a58ceaa99022 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -215,6 +215,14 @@ public abstract class ActivityManagerInternal { public abstract boolean isSystemReady(); /** + * Returns package name given pid. + * + * @param pid The pid we are searching package name for. + */ + @Nullable + public abstract String getPackageNameByPid(int pid); + + /** * Sets if the given pid has an overlay UI or not. * * @param pid The pid we are setting overlay UI for. diff --git a/core/java/android/app/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 56c301f30d5f..da1ba5265c4a 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,6 +28,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.Icon; +import android.media.MediaRoute2Info; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -39,6 +41,7 @@ import android.view.View; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.NotificationVisibility; import java.lang.annotation.Retention; @@ -177,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; @@ -338,6 +343,166 @@ public class StatusBarManager { @Retention(RetentionPolicy.SOURCE) public @interface NavBarModeOverride {} + /** + * State indicating that this sender device is close to a receiver device, so the user can + * potentially *start* a cast to the receiver device if the user moves their device a bit + * closer. + * <p> + * Important notes: + * <ul> + * <li>This state represents that the device is close enough to inform the user that + * transferring is an option, but the device is *not* close enough to actually initiate a + * transfer yet.</li> + * <li>This state is for *starting* a cast. It should be used when this device is currently + * playing media locally and the media should be transferred to be played on the receiver + * device instead.</li> + * </ul> + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; + + /** + * State indicating that this sender device is close to a receiver device, so the user can + * potentially *end* a cast on the receiver device if the user moves this device a bit closer. + * <p> + * Important notes: + * <ul> + * <li>This state represents that the device is close enough to inform the user that + * transferring is an option, but the device is *not* close enough to actually initiate a + * transfer yet.</li> + * <li>This state is for *ending* a cast. It should be used when media is currently being + * played on the receiver device and the media should be transferred to play locally + * instead.</li> + * </ul> + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; + + /** + * State indicating that a media transfer from this sender device to a receiver device has been + * started. + * <p> + * Important note: This state is for *starting* a cast. It should be used when this device is + * currently playing media locally and the media has started being transferred to the receiver + * device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; + + /** + * State indicating that a media transfer from the receiver and back to this sender device + * has been started. + * <p> + * Important note: This state is for *ending* a cast. It should be used when media is currently + * being played on the receiver device and the media has started being transferred to play + * locally instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; + + /** + * State indicating that a media transfer from this sender device to a receiver device has + * finished successfully. + * <p> + * Important note: This state is for *starting* a cast. It should be used when this device had + * previously been playing media locally and the media has successfully been transferred to the + * receiver device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; + + /** + * State indicating that a media transfer from the receiver and back to this sender device has + * finished successfully. + * <p> + * Important note: This state is for *ending* a cast. It should be used when media was + * previously being played on the receiver device and has been successfully transferred to play + * locally on this device instead. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; + + /** + * State indicating that the attempted transfer to the receiver device has failed. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; + + /** + * State indicating that the attempted transfer back to this device has failed. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; + + /** + * State indicating that this sender device is no longer close to the receiver device. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; + + /** @hide */ + @IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = { + MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, + MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED, + MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED, + MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaTransferSenderState {} + + /** + * State indicating that this receiver device is close to a sender device, so the user can + * potentially start or end a cast to the receiver device if the user moves the sender device a + * bit closer. + * <p> + * Important note: This state represents that the device is close enough to inform the user that + * transferring is an option, but the device is *not* close enough to actually initiate a + * transfer yet. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; + + /** + * State indicating that this receiver device is no longer close to the sender device. + * + * @hide + */ + @SystemApi + public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; + + /** @hide */ + @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = { + MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaTransferReceiverState {} + @UnsupportedAppUsage private Context mContext; private IStatusBarService mService; @@ -789,6 +954,81 @@ public class StatusBarManager { return navBarModeOverride; } + /** + * Notifies the system of a new media tap-to-transfer state for the <b>sender</b> device. + * + * <p>The callback should only be provided for the {@link + * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link + * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the + * only states where an action can be un-done. + * + * @param displayState the new state for media tap-to-transfer. + * @param routeInfo the media route information for the media being transferred. + * @param undoExecutor an executor to run the callback on and must be provided if the + * callback is non-null. + * @param undoCallback a callback that will be triggered if the user elects to undo a media + * transfer. + * + * @throws IllegalArgumentException if an undo callback is provided for states that are not a + * succeeded state. + * @throws IllegalArgumentException if an executor is not provided when a callback is. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void updateMediaTapToTransferSenderDisplay( + @MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable Executor undoExecutor, + @Nullable Runnable undoCallback + ) { + Objects.requireNonNull(routeInfo); + if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED + && displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED + && undoCallback != null) { + throw new IllegalArgumentException( + "The undoCallback should only be provided when the state is a " + + "transfer succeeded state"); + } + if (undoCallback != null && undoExecutor == null) { + throw new IllegalArgumentException( + "You must pass an executor when you pass an undo callback"); + } + IStatusBarService svc = getService(); + try { + UndoCallback callbackProxy = null; + if (undoExecutor != null) { + callbackProxy = new UndoCallback(undoExecutor, undoCallback); + } + svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Notifies the system of a new media tap-to-transfer state for the <b>receiver</b> device. + * + * @param displayState the new state for media tap-to-transfer. + * @param routeInfo the media route information for the media being transferred. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + public void updateMediaTapToTransferReceiverDisplay( + @MediaTransferReceiverState int displayState, + @NonNull MediaRoute2Info routeInfo) { + Objects.requireNonNull(routeInfo); + IStatusBarService svc = getService(); + try { + svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /** @hide */ public static String windowStateToString(int state) { if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING"; @@ -1071,4 +1311,29 @@ public class StatusBarManager { mExecutor.execute(() -> mCallback.accept(userResponse)); } } + + /** + * @hide + */ + static final class UndoCallback extends IUndoMediaTransferCallback.Stub { + @NonNull + private final Executor mExecutor; + @NonNull + private final Runnable mCallback; + + UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onUndoTriggered() { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(mCallback); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 79180cbc57fd..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; @@ -139,12 +138,9 @@ import android.net.ConnectivityFrameworkInitializer; import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.EthernetManager; import android.net.IEthernetManager; -import android.net.IIpSecService; import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.IPacProxyManager; import android.net.IVpnManager; -import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; import android.net.NetworkWatchlistManager; @@ -441,15 +437,6 @@ public final class SystemServiceRegistry { return new VcnManager(ctx, service); }}); - registerService(Context.IPSEC_SERVICE, IpSecManager.class, - new CachedServiceFetcher<IpSecManager>() { - @Override - public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE); - IIpSecService service = IIpSecService.Stub.asInterface(b); - return new IpSecManager(ctx, service); - }}); - registerService(Context.COUNTRY_DETECTOR, CountryDetector.class, new StaticServiceFetcher<CountryDetector>() { @Override @@ -1024,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 83265800c04f..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 @@ -1330,7 +1351,10 @@ public class DevicePolicyManager { * * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or * {@link #ACTION_PROVISION_MANAGED_DEVICE} + * + * @deprecated Logo customization is no longer supported in the provisioning flow. */ + @Deprecated public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; @@ -3254,6 +3278,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; /** @@ -3261,6 +3286,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi public static final int DEVICE_OWNER_TYPE_FINANCED = 1; /** @@ -10133,7 +10159,9 @@ public class DevicePolicyManager { /** * Called by a profile owner of secondary user that is affiliated with the device to stop the - * calling user and switch back to primary user. + * calling user and switch back to primary user (when the user was + * {@link #switchUser(ComponentName, UserHandle)} switched to) or stop the user (when it was + * {@link #startUserInBackground(ComponentName, UserHandle) started in background}. * * <p>Notice that on devices running with * {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, there is no primary @@ -10161,7 +10189,12 @@ public class DevicePolicyManager { } /** - * Same as {@link #logoutUser(ComponentName)}, but called by system (like Settings), not admin. + * Similar to {@link #logoutUser(ComponentName)}, except: + * + * <ul> + * <li>Called by system (like Settings), not admin. + * <li>It logs out the current user, not the caller. + * </ul> * * @hide */ @@ -10178,7 +10211,10 @@ public class DevicePolicyManager { } /** * Gets the user a {@link #logoutUser(ComponentName)} call would switch to, - * or {@code null} if the current user is not in a session. + * or {@code null} if the current user is not in a session (i.e., if it was not + * {@link #switchUser(ComponentName, UserHandle) switched} or + * {@link #startUserInBackground(ComponentName, UserHandle) started in background} by the + * device admin. * * @hide */ @@ -14896,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) { @@ -14925,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(); @@ -15193,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 @@ -15354,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/companion/TEST_MAPPING b/core/java/android/companion/TEST_MAPPING index 63f54fa35158..b561c29c37c4 100644 --- a/core/java/android/companion/TEST_MAPPING +++ b/core/java/android/companion/TEST_MAPPING @@ -1,12 +1,7 @@ { - "presubmit": [ + "imports": [ { - "name": "CtsOsTestCases", - "options": [ - { - "include-filter": "android.os.cts.CompanionDeviceManagerTest" - } - ] + "path": "frameworks/base/services/companion" } ] } diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 339e9a2ff1bc..8fc24fd2d7f2 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -77,4 +77,7 @@ interface IVirtualDevice { void launchPendingIntent( int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver); PointF getCursorPosition(IBinder token); + + /** Sets whether to show or hide the cursor while this virtual device is active. */ + void setShowPointerIcon(boolean showPointerIcon); } diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index bb9bb094429a..69033a686351 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -338,6 +338,22 @@ public final class VirtualDeviceManager { } /** + * Sets the visibility of the pointer icon for this VirtualDevice's associated displays. + * + * @param showPointerIcon True if the pointer should be shown; false otherwise. The default + * visibility is true. + */ + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + @NonNull + public void setShowPointerIcon(boolean showPointerIcon) { + try { + mVirtualDevice.setShowPointerIcon(showPointerIcon); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the display flags that should be added to a particular virtual display. * Additional device-level flags from {@link * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will 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 52681630dab0..207412511198 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -408,6 +408,7 @@ public abstract class Context { * @hide */ @SystemApi + @Deprecated public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000; /** @@ -421,12 +422,13 @@ public abstract class Context { public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000; /** - * This flag has never been used. + * Flag for {@link #bindService}: allow background activity starts from the bound service's + * process. + * This flag is only respected if the caller is holding + * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}. * @hide - * @deprecated This flag has never been used. */ @SystemApi - @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000; /** @@ -3888,7 +3890,6 @@ public abstract class Context { //@hide: SPEECH_RECOGNITION_SERVICE, UWB_SERVICE, MEDIA_METRICS_SERVICE, - SUPPLEMENTAL_PROCESS_SERVICE, //@hide: ATTESTATION_VERIFICATION_SERVICE, //@hide: SAFETY_CENTER_SERVICE, }) @@ -5970,13 +5971,6 @@ public abstract class Context { public static final String LOCALE_SERVICE = "locale"; /** - * Use with {@link #getSystemService(String)} to retrieve a Supplemental Process Manager. - * - * @see #getSystemService(String) - */ - public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process"; - - /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.safetycenter.SafetyCenterManager} instance for interacting with the safety center. * @@ -6510,15 +6504,26 @@ public abstract class Context { * <li>Each permission in {@code permissions} must be a runtime permission. * </ul> * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. The revocation happens asynchronously and kills all processes running in the - * calling UID. It will be triggered once it is safe to do so. In particular, it will not be - * triggered as long as the package remains in the foreground, or has any active manifest - * components (e.g. when another app is accessing a content provider in the package). + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * The revocation happens asynchronously and kills all processes running in the calling UID. It + * will be triggered once it is safe to do so. In particular, it will not be triggered as long + * as the package remains in the foreground, or has any active manifest components (e.g. when + * another app is accessing a content provider in the package). * <p> * If you want to revoke the permissions right away, you could call {@code System.exit()}, but * this could affect other apps that are accessing your app at the moment. For example, apps * accessing a content provider in your app will all crash. + * <p> + * Note that the settings UI shows a permission group as granted as long as at least one + * permission in the group is granted. If you want the user to observe the revocation in the + * settings, you should revoke every permission in the target group. To learn the current list + * of permissions in a group, you may use + * {@link PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)} and + * {@link PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)}. This list + * of permissions may evolve over time, so it is recommended to check whether it contains any + * permission you wish to retain before trying to revoke an entire group. * * @param permissions Collection of permissions to be revoked. * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3e527f8d5215..28bef566b59c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5576,6 +5576,7 @@ public class Intent implements Parcelable, Cloneable { /** * A String[] holding attribution tags when used with * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE * * E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc. */ @@ -5584,17 +5585,20 @@ public class Intent implements Parcelable, Cloneable { /** * A long representing the start timestamp (epoch time in millis) of the permission usage * when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE */ public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME"; /** * A long representing the end timestamp (epoch time in millis) of the permission usage when * used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and ACTION_MANAGE_PERMISSION_USAGE */ public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME"; /** - * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}, + * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD} + * and {@link #ACTION_MANAGE_PERMISSION_USAGE}, * that specifies whether the permission usage system UI is showing attribution information * for the chosen entry. * diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9e9dd1edd577..567f649ea762 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -19,6 +19,7 @@ package android.content.pm; import static android.os.Build.VERSION_CODES.DONUT; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -48,6 +49,7 @@ import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -62,58 +64,58 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class); /** - * Default task affinity of all activities in this application. See - * {@link ActivityInfo#taskAffinity} for more information. This comes - * from the "taskAffinity" attribute. + * Default task affinity of all activities in this application. See + * {@link ActivityInfo#taskAffinity} for more information. This comes + * from the "taskAffinity" attribute. */ public String taskAffinity; - + /** * Optional name of a permission required to be able to access this * application's components. From the "permission" attribute. */ public String permission; - + /** * The name of the process this application should run in. From the * "process" attribute or, if not set, the same as * <var>packageName</var>. */ public String processName; - + /** * Class implementing the Application object. From the "class" * attribute. */ public String className; - + /** * A style resource identifier (in the package's resources) of the * description of an application. From the "description" attribute * or, if not set, 0. */ - public int descriptionRes; - + public int descriptionRes; + /** * A style resource identifier (in the package's resources) of the * default visual theme of the application. From the "theme" attribute * or, if not set, 0. */ public int theme; - + /** * Class implementing the Application's manage space * functionality. From the "manageSpaceActivity" * attribute. This is an optional attribute and will be null if * applications don't specify it in their manifest */ - public String manageSpaceActivityName; - + public String manageSpaceActivityName; + /** * Class implementing the Application's backup functionality. From * the "backupAgent" attribute. This is an optional attribute and * will be null if the application does not specify it in its manifest. - * + * * <p>If android:allowBackup is set to false, this attribute is ignored. */ public String backupAgentName; @@ -174,7 +176,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@code signatureOrSystem}. */ public static final int FLAG_SYSTEM = 1<<0; - + /** * Value for {@link #flags}: set to true if this application would like to * allow debugging of its @@ -183,7 +185,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:debuggable} of the <application> tag. */ public static final int FLAG_DEBUGGABLE = 1<<1; - + /** * Value for {@link #flags}: set to true if this application has code * associated with it. Comes @@ -191,7 +193,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:hasCode} of the <application> tag. */ public static final int FLAG_HAS_CODE = 1<<2; - + /** * Value for {@link #flags}: set to true if this application is persistent. * Comes from {@link android.R.styleable#AndroidManifestApplication_persistent @@ -212,20 +214,20 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:allowTaskReparenting} of the <application> tag. */ public static final int FLAG_ALLOW_TASK_REPARENTING = 1<<5; - + /** * Value for {@link #flags}: default value for the corresponding ActivityInfo flag. * Comes from {@link android.R.styleable#AndroidManifestApplication_allowClearUserData * android:allowClearUserData} of the <application> tag. */ public static final int FLAG_ALLOW_CLEAR_USER_DATA = 1<<6; - + /** * Value for {@link #flags}: this is set if this application has been * installed as an update to a built-in system application. */ public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7; - + /** * Value for {@link #flags}: this is set if the application has specified * {@link android.R.styleable#AndroidManifestApplication_testOnly @@ -240,15 +242,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:smallScreens}. */ public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9; - + /** * Value for {@link #flags}: true when the application's window can be * displayed on normal screens. Corresponds to * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens * android:normalScreens}. */ - public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; - + public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; + /** * Value for {@link #flags}: true when the application's window can be * increased in size for larger screens. Corresponds to @@ -256,7 +258,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:largeScreens}. */ public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11; - + /** * Value for {@link #flags}: true when the application knows how to adjust * its UI for different screen sizes. Corresponds to @@ -264,7 +266,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:resizeable}. */ public static final int FLAG_RESIZEABLE_FOR_SCREENS = 1<<12; - + /** * Value for {@link #flags}: true when the application knows how to * accommodate different screen densities. Corresponds to @@ -276,7 +278,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ @Deprecated public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13; - + /** * Value for {@link #flags}: set to true if this application would like to * request the VM to operate under the safe mode. Comes from @@ -288,7 +290,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #flags}: set to <code>false</code> if the application does not wish * to permit any OS-driven backups of its data; <code>true</code> otherwise. - * + * * <p>Comes from the * {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup} * attribute of the <application> tag. @@ -351,7 +353,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * android:xlargeScreens}. */ public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19; - + /** * Value for {@link #flags}: true when the application has requested a * large heap for its processes. Corresponds to @@ -1114,7 +1116,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * the same uid). */ public int uid; - + /** * The minimum SDK version this application can run on. It will not run * on earlier versions. @@ -1817,7 +1819,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (sb == null) { sb = ab.packageName; } - + return sCollator.compare(sa.toString(), sb.toString()); } @@ -1830,7 +1832,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public ApplicationInfo() { createTimestamp = System.currentTimeMillis(); } - + public ApplicationInfo(ApplicationInfo orig) { super(orig); taskAffinity = orig.taskAffinity; @@ -2125,7 +2127,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Disable compatibility mode - * + * * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -2346,7 +2348,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } return pm.getDefaultActivityIcon(); } - + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean isPackageUnavailable(PackageManager pm) { try { @@ -2655,4 +2657,22 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int getLocaleConfigRes() { return localeConfigRes; } + + + /** + * List of all shared libraries this application is linked against. This + * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES + * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure. + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public List<SharedLibraryInfo> getSharedLibraryInfos() { + if (sharedLibraryInfos == null) { + return Collections.EMPTY_LIST; + } + return sharedLibraryInfos; + } + } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index aa647000ee2d..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/OWNERS b/core/java/android/hardware/OWNERS index 95f13b5c1d8c..3b6a564fc353 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -1,3 +1,9 @@ +# Generic +etalvala@google.com +jreck@google.com +michaelwr@google.com +sumir@google.com + # Camera per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,yinchiayeh@google.com,zhijunhe@google.com,jchowdhary@google.com 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/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index c12e8195eeb4..d6d3a97687b5 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -25,6 +25,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Point; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; @@ -458,12 +459,14 @@ public final class CameraManager { (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); if (display != null) { - int width = display.getWidth(); - int height = display.getHeight(); + Point sz = new Point(); + display.getRealSize(sz); + int width = sz.x; + int height = sz.y; if (height > width) { height = width; - width = display.getHeight(); + width = sz.y; } ret = new Size(width, height); @@ -471,7 +474,7 @@ public final class CameraManager { Log.e(TAG, "Invalid default display!"); } } catch (Exception e) { - Log.e(TAG, "getDisplaySize Failed. " + e.toString()); + Log.e(TAG, "getDisplaySize Failed. " + e); } return ret; diff --git a/core/java/android/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/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 1a7a63ae8b69..af8ec279ac30 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -916,6 +916,17 @@ public final class DisplayManagerGlobal { } /** + * Returns the system preferred display mode. + */ + public Display.Mode getSystemPreferredDisplayMode(int displayId) { + try { + return mDm.getSystemPreferredDisplayMode(displayId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * When enabled the app requested display resolution and refresh rate is always selected * in DisplayModeDirector regardless of user settings and policies for low brightness, low * battery etc. diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 35663af189f4..b3af52b19063 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -168,6 +168,7 @@ interface IDisplayManager { // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission. void setUserPreferredDisplayMode(int displayId, in Mode mode); Mode getUserPreferredDisplayMode(int displayId); + Mode getSystemPreferredDisplayMode(int displayId); // When enabled the app requested display resolution and refresh rate is always selected // in DisplayModeDirector regardless of user settings and policies for low brightness, low diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java index cc9aeab7de55..3f20002c8028 100644 --- a/core/java/android/hardware/input/InputManagerInternal.java +++ b/core/java/android/hardware/input/InputManagerInternal.java @@ -87,6 +87,12 @@ public abstract class InputManagerInternal { */ public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId); + /** + * Gets the display id that the MouseCursorController is being forced to target. Returns + * {@link android.view.Display#INVALID_DISPLAY} if there is no override + */ + public abstract int getVirtualMousePointerDisplayId(); + /** Gets the current position of the mouse cursor. */ public abstract PointF getCursorPosition(); @@ -94,7 +100,7 @@ public abstract class InputManagerInternal { * Sets the pointer acceleration. * See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}. */ - public abstract void setPointerAcceleration(float acceleration); + public abstract void setPointerAcceleration(float acceleration, int displayId); /** * Sets the eligibility of windows on a given display for pointer capture. If a display is @@ -103,6 +109,9 @@ public abstract class InputManagerInternal { */ public abstract void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible); + /** Sets the visibility of the cursor. */ + public abstract void setPointerIconVisible(boolean visible, int displayId); + /** Registers the {@link LidSwitchCallback} to begin receiving notifications. */ public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks); 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/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index fc2fbc39dbeb..b6e1b1f1fa3f 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -839,10 +839,9 @@ public class InputMethodService extends AbstractInputMethodService { final boolean wasVisible = isInputViewShown(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); - applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */); mShowInputFlags = 0; mShowInputRequested = false; - doHideWindow(); + hideWindow(); final boolean isVisible = isInputViewShown(); final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { @@ -891,7 +890,6 @@ public class InputMethodService extends AbstractInputMethodService { final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { showWindow(true); - applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */); } setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); @@ -1666,7 +1664,7 @@ public class InputMethodService extends AbstractInputMethodService { onDisplayCompletions(completions); } } else { - doHideWindow(); + hideWindow(); } } else if (mCandidatesVisibility == View.VISIBLE) { // If the candidates are currently visible, make sure the @@ -1674,7 +1672,7 @@ public class InputMethodService extends AbstractInputMethodService { showWindow(false); } else { // Otherwise hide the window. - doHideWindow(); + hideWindow(); } // If user uses hard keyboard, IME button should always be shown. boolean showing = onEvaluateInputViewShown(); @@ -2119,7 +2117,7 @@ public class InputMethodService extends AbstractInputMethodService { if (shown) { showWindow(false); } else { - doHideWindow(); + hideWindow(); } } } @@ -2540,12 +2538,11 @@ public class InputMethodService extends AbstractInputMethodService { mWindowVisible = true; // request draw for the IME surface. - // When IME is not pre-rendered, this will actually show the IME. - if ((previousImeWindowStatus & IME_ACTIVE) == 0) { - if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); - mWindow.show(); - } + if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); + mWindow.show(); mDecorViewWasVisible = true; + applyVisibilityInInsetsConsumerIfNecessary(true); + cancelImeSurfaceRemoval(); mInShowWindow = false; Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -2602,9 +2599,6 @@ public class InputMethodService extends AbstractInputMethodService { ImeTracing.getInstance().triggerServiceDump( "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper, null /* icProto */); - if (setVisible) { - cancelImeSurfaceRemoval(); - } mPrivOps.applyImeVisibilityAsync(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } @@ -2622,15 +2616,12 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesViewStarted = false; } - private void doHideWindow() { - setImeWindowStatus(0, mBackDisposition); - hideWindow(); - } - public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper, null /* icProto */); + setImeWindowStatus(0, mBackDisposition); + applyVisibilityInInsetsConsumerIfNecessary(false); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { @@ -2916,7 +2907,7 @@ public class InputMethodService extends AbstractInputMethodService { // If we have the window visible for some other reason -- // most likely to show candidates -- then just get rid // of it. This really shouldn't happen, but just in case... - if (doIt) doHideWindow(); + if (doIt) hideWindow(); } return true; } diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index e5c22e4de08e..6f4fd76839d1 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); } @@ -531,12 +531,14 @@ final class NavigationBarController { if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) { mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground; - if (mDrawLegacyNavigationBarBackground) { - mNavigationBarFrame.setBackgroundColor(Color.BLACK); - } else { - mNavigationBarFrame.setBackground(null); + if (mNavigationBarFrame != null) { + if (mDrawLegacyNavigationBarBackground) { + mNavigationBarFrame.setBackgroundColor(Color.BLACK); + } else { + mNavigationBarFrame.setBackground(null); + } + scheduleRelayout(); } - scheduleRelayout(); onSystemBarAppearanceChanged(mAppearance); } return drawLegacyNavigationBarBackground; @@ -546,7 +548,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/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl index 68b5679919d6..02db2749bbe8 100644 --- a/core/java/android/os/logcat/ILogcatManagerService.aidl +++ b/core/java/android/os/logcat/ILogcatManagerService.aidl @@ -22,5 +22,7 @@ package android.os.logcat; interface ILogcatManagerService { void startThread(in int uid, in int gid, in int pid, in int fd); void finishThread(in int uid, in int gid, in int pid, in int fd); + void approve(in int uid, in int gid, in int pid, in int fd); + void decline(in int uid, in int gid, in int pid, in int fd); } diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 1c0320e9a86e..619c8705ddae 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -79,7 +79,8 @@ interface IPermissionManager { void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions); void startOneTimePermissionSession(String packageName, int userId, long timeout, - int importanceToResetTimer, int importanceToKeepSessionAlive); + long revokeAfterKilledDelay, int importanceToResetTimer, + int importanceToKeepSessionAlive); void stopOneTimePermissionSession(String packageName, int userId); diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0cf06aa364ec..a005ab4e6ac7 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -907,21 +907,23 @@ public final class PermissionControllerManager { * <li>Each permission in {@code permissions} must be a runtime permission. * </ul> * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. This revocation happens asynchronously and kills all processes running in the - * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * This revocation happens asynchronously and kills all processes running in the same UID as + * {@code packageName}. It will be triggered once it is safe to do so. * * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. - * @param callback Callback called when the revocation request has been completed. * - * @see Context#revokeOwnPermissionsOnKill(Collection) + * @see Context#revokeOwnPermissionsOnKill(java.util.Collection) * * @hide */ public void revokeOwnPermissionsOnKill(@NonNull String packageName, - @NonNull List<String> permissions, AndroidFuture<Void> callback) { + @NonNull List<String> permissions) { mRemoteService.postAsync(service -> { + AndroidFuture<Void> callback = new AndroidFuture<>(); service.revokeOwnPermissionsOnKill(packageName, permissions, callback); return callback; }).whenComplete((result, err) -> { diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 8d9f82b04b54..3292e7110ee5 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -291,7 +291,7 @@ public abstract class PermissionControllerService extends Service { /** * Called when a package is considered inactive based on the criteria given by - * {@link PermissionManager#startOneTimePermissionSession(String, long, int, int)}. + * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}. * This method is called at the end of a one-time permission session * * @param packageName The package that has been inactive @@ -329,9 +329,11 @@ public abstract class PermissionControllerService extends Service { * Triggers the revocation of one or more permissions for a package. This should only be called * at the request of {@code packageName}. * <p> - * For every permission in {@code permissions}, the entire permission group it belongs to will - * be revoked. This revocation happens asynchronously and kills all processes running in the - * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * Background permissions which have no corresponding foreground permission still granted once + * the revocation is effective will also be revoked. + * <p> + * This revocation happens asynchronously and kills all processes running in the same UID as + * {@code packageName}. It will be triggered once it is safe to do so. * * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 15f13eb89cb9..12fa0ddfc648 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -20,6 +20,7 @@ import static android.os.Build.VERSION_CODES.S; import android.Manifest; import android.annotation.CheckResult; +import android.annotation.DurationMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1282,6 +1283,22 @@ public final class PermissionManager { } /** + * Starts a one-time permission session for a given package. + * @see #startOneTimePermissionSession(String, long, long, int, int) + * @hide + * @deprecated Use {@link #startOneTimePermissionSession(String, long, long, int, int)} instead + */ + @Deprecated + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) + public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis, + @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer, + @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) { + startOneTimePermissionSession(packageName, timeoutMillis, -1, + importanceToResetTimer, importanceToKeepSessionAlive); + } + + /** * Starts a one-time permission session for a given package. A one-time permission session is * ended if app becomes inactive. Inactivity is defined as the package's uid importance level * staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid @@ -1301,25 +1318,33 @@ public final class PermissionManager { * {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked. * </p> * <p> - * Note that if there is currently an active session for a package a new one isn't created and - * the existing one isn't changed. + * Note that if there is currently an active session for a package a new one isn't created but + * each parameter of the existing one will be updated to the more aggressive of both sessions. + * This means that durations will be set to the shortest parameter and importances will be set + * to the lowest one. * </p> * @param packageName The package to start a one-time permission session for * @param timeoutMillis Number of milliseconds for an app to be in an inactive state + * @param revokeAfterKilledDelayMillis Number of milliseconds to wait before revoking on the + * event an app is terminated. Set to -1 to use default + * value for the device. * @param importanceToResetTimer The least important level to uid must be to reset the timer * @param importanceToKeepSessionAlive The least important level the uid must be to keep the - * session alive + * session alive * * @hide */ @SystemApi @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) - public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis, + public void startOneTimePermissionSession(@NonNull String packageName, + @DurationMillisLong long timeoutMillis, + @DurationMillisLong long revokeAfterKilledDelayMillis, @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer, @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) { try { mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(), - timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive); + timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer, + importanceToKeepSessionAlive); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/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/util/Dumpable.java b/core/java/android/util/Dumpable.java index 79c576d08866..955113d9e21e 100644 --- a/core/java/android/util/Dumpable.java +++ b/core/java/android/util/Dumpable.java @@ -35,8 +35,6 @@ public interface Dumpable { return getClass().getName(); } - //TODO(b/149254050): decide whether it should take a ParcelFileDescription as well. - /** * Dumps the internal state into the given {@code writer}. * diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS index 5425c214de1f..c199b9629b5b 100644 --- a/core/java/android/util/OWNERS +++ b/core/java/android/util/OWNERS @@ -1,6 +1,5 @@ per-file FeatureFlagUtils.java = sbasi@google.com per-file FeatureFlagUtils.java = tmfang@google.com -per-file FeatureFlagUtils.java = asapperstein@google.com per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS per-file TypedValue.java = file:/core/java/android/content/res/OWNERS diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java index e1be0fe8d6d2..1680fd28f1e5 100644 --- a/core/java/android/view/ActionProvider.java +++ b/core/java/android/view/ActionProvider.java @@ -16,6 +16,8 @@ package android.view; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.util.Log; @@ -67,7 +69,7 @@ public abstract class ActionProvider { * * @param context Context for accessing resources. */ - public ActionProvider(Context context) { + public ActionProvider(@NonNull Context context) { } /** @@ -82,7 +84,7 @@ public abstract class ActionProvider { * @deprecated use {@link #onCreateActionView(MenuItem)} */ @Deprecated - public abstract View onCreateActionView(); + public abstract @NonNull View onCreateActionView(); /** * Factory method called by the Android framework to create new action views. @@ -96,7 +98,7 @@ public abstract class ActionProvider { * @param forItem MenuItem to create the action view for * @return the new action view */ - public View onCreateActionView(MenuItem forItem) { + public @NonNull View onCreateActionView(@NonNull MenuItem forItem) { return onCreateActionView(); } @@ -200,7 +202,7 @@ public abstract class ActionProvider { * * @param subMenu Submenu that will be displayed */ - public void onPrepareSubMenu(SubMenu subMenu) { + public void onPrepareSubMenu(@NonNull SubMenu subMenu) { } /** @@ -220,7 +222,7 @@ public abstract class ActionProvider { * @hide Internal use only */ @UnsupportedAppUsage - public void setSubUiVisibilityListener(SubUiVisibilityListener listener) { + public void setSubUiVisibilityListener(@Nullable SubUiVisibilityListener listener) { mSubUiVisibilityListener = listener; } @@ -230,7 +232,7 @@ public abstract class ActionProvider { * * @param listener listener to set */ - public void setVisibilityListener(VisibilityListener listener) { + public void setVisibilityListener(@Nullable VisibilityListener listener) { if (mVisibilityListener != null) { Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " + "when one is already set. Are you reusing this " + getClass().getSimpleName() + diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index fa39380cdcc1..246a8c9d17d3 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1118,6 +1118,19 @@ public final class Display { } /** + * Returns the system's preferred display mode. This mode will be used when the user has not + * specified a display-mode preference. This returns null if the boot display mode feature is + * not supported by system. + * + * @hide + */ + @TestApi + @Nullable + public Display.Mode getSystemPreferredDisplayMode() { + return mGlobal.getSystemPreferredDisplayMode(getDisplayId()); + } + + /** * Returns the display's HDR capabilities. * * @see #isHdr() diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 9b36b9bcb460..b3b7f10d8c7e 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -260,7 +260,7 @@ public final class DisplayCutout { private final float mScale; public CutoutPathParserInfo(int displayWidth, int displayHeight, float density, - String cutoutSpec, @Rotation int rotation, float scale) { + @Nullable String cutoutSpec, @Rotation int rotation, float scale) { mDisplayWidth = displayWidth; mDisplayHeight = displayHeight; mDensity = density; @@ -269,7 +269,7 @@ public final class DisplayCutout { mScale = scale; } - public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) { + public CutoutPathParserInfo(@NonNull CutoutPathParserInfo cutoutPathParserInfo) { mDisplayWidth = cutoutPathParserInfo.mDisplayWidth; mDisplayHeight = cutoutPathParserInfo.mDisplayHeight; mDensity = cutoutPathParserInfo.mDensity; diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 63a8300ce6aa..57ba7e9e816f 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -23,6 +23,8 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UiContext; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -68,7 +70,7 @@ public class GestureDetector { * * @param e The down motion event. */ - boolean onDown(MotionEvent e); + boolean onDown(@NonNull MotionEvent e); /** * The user has performed a down {@link MotionEvent} and not performed @@ -78,7 +80,7 @@ public class GestureDetector { * * @param e The down motion event */ - void onShowPress(MotionEvent e); + void onShowPress(@NonNull MotionEvent e); /** * Notified when a tap occurs with the up {@link MotionEvent} @@ -87,7 +89,7 @@ public class GestureDetector { * @param e The up motion event that completed the first tap * @return true if the event is consumed, else false */ - boolean onSingleTapUp(MotionEvent e); + boolean onSingleTapUp(@NonNull MotionEvent e); /** * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the @@ -104,7 +106,8 @@ public class GestureDetector { * and {@code e2}. * @return true if the event is consumed, else false */ - boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY); + boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, + float distanceY); /** * Notified when a long press occurs with the initial on down {@link MotionEvent} @@ -112,7 +115,7 @@ public class GestureDetector { * * @param e The initial on down motion event that started the longpress. */ - void onLongPress(MotionEvent e); + void onLongPress(@NonNull MotionEvent e); /** * Notified of a fling event when it occurs with the initial on down {@link MotionEvent} @@ -127,7 +130,8 @@ public class GestureDetector { * along the y axis. * @return true if the event is consumed, else false */ - boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); + boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, + float velocityY); } /** @@ -146,7 +150,7 @@ public class GestureDetector { * @param e The down motion event of the single-tap. * @return true if the event is consumed, else false */ - boolean onSingleTapConfirmed(MotionEvent e); + boolean onSingleTapConfirmed(@NonNull MotionEvent e); /** * Notified when a double-tap occurs. Triggered on the down event of second tap. @@ -154,7 +158,7 @@ public class GestureDetector { * @param e The down motion event of the first tap of the double-tap. * @return true if the event is consumed, else false */ - boolean onDoubleTap(MotionEvent e); + boolean onDoubleTap(@NonNull MotionEvent e); /** * Notified when an event within a double-tap gesture occurs, including @@ -163,7 +167,7 @@ public class GestureDetector { * @param e The motion event that occurred during the double-tap gesture. * @return true if the event is consumed, else false */ - boolean onDoubleTapEvent(MotionEvent e); + boolean onDoubleTapEvent(@NonNull MotionEvent e); } /** @@ -178,7 +182,7 @@ public class GestureDetector { * @param e The motion event that occurred during the context click. * @return true if the event is consumed, else false */ - boolean onContextClick(MotionEvent e); + boolean onContextClick(@NonNull MotionEvent e); } /** @@ -190,43 +194,43 @@ public class GestureDetector { public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener, OnContextClickListener { - public boolean onSingleTapUp(MotionEvent e) { + public boolean onSingleTapUp(@NonNull MotionEvent e) { return false; } - public void onLongPress(MotionEvent e) { + public void onLongPress(@NonNull MotionEvent e) { } - public boolean onScroll(MotionEvent e1, MotionEvent e2, + public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { return false; } - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { return false; } - public void onShowPress(MotionEvent e) { + public void onShowPress(@NonNull MotionEvent e) { } - public boolean onDown(MotionEvent e) { + public boolean onDown(@NonNull MotionEvent e) { return false; } - public boolean onDoubleTap(MotionEvent e) { + public boolean onDoubleTap(@NonNull MotionEvent e) { return false; } - public boolean onDoubleTapEvent(MotionEvent e) { + public boolean onDoubleTapEvent(@NonNull MotionEvent e) { return false; } - public boolean onSingleTapConfirmed(MotionEvent e) { + public boolean onSingleTapConfirmed(@NonNull MotionEvent e) { return false; } - public boolean onContextClick(MotionEvent e) { + public boolean onContextClick(@NonNull MotionEvent e) { return false; } } @@ -348,14 +352,13 @@ public class GestureDetector { * not be null. * @param handler the handler to use * - * @throws NullPointerException if either {@code listener} or - * {@code handler} is null. + * @throws NullPointerException if {@code listener} is null. * * @deprecated Use {@link #GestureDetector(android.content.Context, * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead. */ @Deprecated - public GestureDetector(OnGestureListener listener, Handler handler) { + public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) { this(null, listener, handler); } @@ -373,7 +376,7 @@ public class GestureDetector { * android.view.GestureDetector.OnGestureListener)} instead. */ @Deprecated - public GestureDetector(OnGestureListener listener) { + public GestureDetector(@NonNull OnGestureListener listener) { this(null, listener, null); } @@ -392,7 +395,8 @@ public class GestureDetector { * @throws NullPointerException if {@code listener} is null. */ // TODO(b/182007470): Use @ConfigurationContext instead - public GestureDetector(@UiContext Context context, OnGestureListener listener) { + public GestureDetector(@Nullable @UiContext Context context, + @NonNull OnGestureListener listener) { this(context, listener, null); } @@ -411,8 +415,8 @@ public class GestureDetector { * * @throws NullPointerException if {@code listener} is null. */ - public GestureDetector(@UiContext Context context, OnGestureListener listener, - Handler handler) { + public GestureDetector(@Nullable @UiContext Context context, + @NonNull OnGestureListener listener, @Nullable Handler handler) { if (handler != null) { mHandler = new GestureHandler(handler); } else { @@ -442,8 +446,8 @@ public class GestureDetector { * * @throws NullPointerException if {@code listener} is null. */ - public GestureDetector(@UiContext Context context, OnGestureListener listener, Handler handler, - boolean unused) { + public GestureDetector(@Nullable @UiContext Context context, + @NonNull OnGestureListener listener, @Nullable Handler handler, boolean unused) { this(context, listener, handler); } @@ -486,7 +490,7 @@ public class GestureDetector { * @param onDoubleTapListener the listener invoked for all the callbacks, or * null to stop listening for double-tap gestures. */ - public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) { + public void setOnDoubleTapListener(@Nullable OnDoubleTapListener onDoubleTapListener) { mDoubleTapListener = onDoubleTapListener; } @@ -496,7 +500,7 @@ public class GestureDetector { * @param onContextClickListener the listener invoked for all the callbacks, or null to stop * listening for context clicks. */ - public void setContextClickListener(OnContextClickListener onContextClickListener) { + public void setContextClickListener(@Nullable OnContextClickListener onContextClickListener) { mContextClickListener = onContextClickListener; } @@ -528,7 +532,7 @@ public class GestureDetector { * @return true if the {@link OnGestureListener} consumed the event, * else false. */ - public boolean onTouchEvent(MotionEvent ev) { + public boolean onTouchEvent(@NonNull MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 0); } @@ -800,7 +804,7 @@ public class GestureDetector { * @return true if the {@link OnGestureListener} consumed the event, * else false. */ - public boolean onGenericMotionEvent(MotionEvent ev) { + public boolean onGenericMotionEvent(@NonNull MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onGenericMotionEvent(ev, 0); } @@ -860,8 +864,8 @@ public class GestureDetector { mIgnoreNextUpEvent = false; } - private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp, - MotionEvent secondDown) { + private boolean isConsideredDoubleTap(@NonNull MotionEvent firstDown, + @NonNull MotionEvent firstUp, @NonNull MotionEvent secondDown) { if (!mAlwaysInBiggerTapRegion) { return false; } diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index c8bfd36b2b48..b4cdc6e5deb8 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.IntDef; +import android.annotation.NonNull; import android.graphics.Rect; import java.lang.annotation.Retention; @@ -187,8 +188,8 @@ public class Gravity * @see View#LAYOUT_DIRECTION_LTR * @see View#LAYOUT_DIRECTION_RTL */ - public static void apply(int gravity, int w, int h, Rect container, - Rect outRect, int layoutDirection) { + public static void apply(int gravity, int w, int h, @NonNull Rect container, + @NonNull Rect outRect, int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); apply(absGravity, w, h, container, 0, 0, outRect); } @@ -214,8 +215,8 @@ public class Gravity * @param outRect Receives the computed frame of the object in its * container. */ - public static void apply(int gravity, int w, int h, Rect container, - int xAdj, int yAdj, Rect outRect) { + public static void apply(int gravity, int w, int h, @NonNull Rect container, + int xAdj, int yAdj, @NonNull Rect outRect) { switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) { case 0: outRect.left = container.left @@ -324,8 +325,8 @@ public class Gravity * @see View#LAYOUT_DIRECTION_LTR * @see View#LAYOUT_DIRECTION_RTL */ - public static void apply(int gravity, int w, int h, Rect container, - int xAdj, int yAdj, Rect outRect, int layoutDirection) { + public static void apply(int gravity, int w, int h, @NonNull Rect container, + int xAdj, int yAdj, @NonNull Rect outRect, int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); apply(absGravity, w, h, container, xAdj, yAdj, outRect); } @@ -346,7 +347,7 @@ public class Gravity * @param inoutObj Supplies the current object position; returns with it * modified if needed to fit in the display. */ - public static void applyDisplay(int gravity, Rect display, Rect inoutObj) { + public static void applyDisplay(int gravity, @NonNull Rect display, @NonNull Rect inoutObj) { if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) { if (inoutObj.top < display.top) inoutObj.top = display.top; if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom; @@ -404,7 +405,8 @@ public class Gravity * @see View#LAYOUT_DIRECTION_LTR * @see View#LAYOUT_DIRECTION_RTL */ - public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) { + public static void applyDisplay(int gravity, @NonNull Rect display, @NonNull Rect inoutObj, + int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); applyDisplay(absGravity, display, inoutObj); } diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java index a2fb596f743e..bc46d55ae4dc 100644 --- a/core/java/android/view/MenuItem.java +++ b/core/java/android/view/MenuItem.java @@ -91,7 +91,7 @@ public interface MenuItem { * @return Return true to consume this click and prevent others from * executing. */ - public boolean onMenuItemClick(MenuItem item); + public boolean onMenuItemClick(@NonNull MenuItem item); } /** @@ -110,7 +110,7 @@ public interface MenuItem { * @param item Item that was expanded * @return true if the item should expand, false if expansion should be suppressed. */ - public boolean onMenuItemActionExpand(MenuItem item); + public boolean onMenuItemActionExpand(@NonNull MenuItem item); /** * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} @@ -118,7 +118,7 @@ public interface MenuItem { * @param item Item that was collapsed * @return true if the item should collapse, false if collapsing should be suppressed. */ - public boolean onMenuItemActionCollapse(MenuItem item); + public boolean onMenuItemActionCollapse(@NonNull MenuItem item); } /** @@ -159,7 +159,7 @@ public interface MenuItem { * @param title The new text to be displayed. * @return This Item so additional setters can be called. */ - public MenuItem setTitle(CharSequence title); + public @NonNull MenuItem setTitle(@Nullable CharSequence title); /** * Change the title associated with this item. @@ -173,14 +173,14 @@ public interface MenuItem { * @see #setTitleCondensed(CharSequence) */ - public MenuItem setTitle(@StringRes int title); + public @NonNull MenuItem setTitle(@StringRes int title); /** * Retrieve the current title of the item. * * @return The title. */ - public CharSequence getTitle(); + public @Nullable CharSequence getTitle(); /** * Change the condensed title associated with this item. The condensed @@ -190,7 +190,7 @@ public interface MenuItem { * @param title The new text to be displayed as the condensed title. * @return This Item so additional setters can be called. */ - public MenuItem setTitleCondensed(CharSequence title); + public @NonNull MenuItem setTitleCondensed(@Nullable CharSequence title); /** * Retrieve the current condensed title of the item. If a condensed @@ -199,7 +199,7 @@ public interface MenuItem { * @return The condensed title, if it exists. * Otherwise the normal title. */ - public CharSequence getTitleCondensed(); + public @Nullable CharSequence getTitleCondensed(); /** * Change the icon associated with this item. This icon will not always be @@ -209,7 +209,7 @@ public interface MenuItem { * @param icon The new icon (as a Drawable) to be displayed. * @return This Item so additional setters can be called. */ - public MenuItem setIcon(Drawable icon); + public @NonNull MenuItem setIcon(@Nullable Drawable icon); /** * Change the icon associated with this item. This icon will not always be @@ -222,7 +222,7 @@ public interface MenuItem { * @param iconRes The new icon (as a resource ID) to be displayed. * @return This Item so additional setters can be called. */ - public MenuItem setIcon(@DrawableRes int iconRes); + public @NonNull MenuItem setIcon(@DrawableRes int iconRes); /** * Returns the icon for this item as a Drawable (getting it from resources if it hasn't been @@ -233,7 +233,7 @@ public interface MenuItem { * * @return The icon as a Drawable. */ - public Drawable getIcon(); + public @Nullable Drawable getIcon(); /** * Applies a tint to this item's icon. Does not modify the @@ -250,15 +250,14 @@ public interface MenuItem { * @see #getIconTintList() * @see Drawable#setTintList(ColorStateList) */ - public default MenuItem setIconTintList(@Nullable ColorStateList tint) { return this; } + public default @NonNull MenuItem setIconTintList(@Nullable ColorStateList tint) { return this; } /** * @return the tint applied to this item's icon * @attr ref android.R.styleable#MenuItem_iconTint * @see #setIconTintList(ColorStateList) */ - @Nullable - public default ColorStateList getIconTintList() { return null; } + public default @Nullable ColorStateList getIconTintList() { return null; } /** * Specifies the blending mode used to apply the tint specified by @@ -304,8 +303,7 @@ public interface MenuItem { * @see #setIconTintBlendMode(BlendMode) * */ - @Nullable - public default PorterDuff.Mode getIconTintMode() { return null; } + public default @Nullable PorterDuff.Mode getIconTintMode() { return null; } /** * Returns the blending mode used to apply the tint to this item's icon, if specified. @@ -315,8 +313,7 @@ public interface MenuItem { * @see #setIconTintBlendMode(BlendMode) * */ - @Nullable - default BlendMode getIconTintBlendMode() { + default @Nullable BlendMode getIconTintBlendMode() { PorterDuff.Mode mode = getIconTintMode(); if (mode != null) { return BlendMode.fromValue(mode.nativeInt); @@ -343,7 +340,7 @@ public interface MenuItem { * modify it later. * @return This Item so additional setters can be called. */ - public MenuItem setIntent(Intent intent); + public @NonNull MenuItem setIntent(@Nullable Intent intent); /** * Return the Intent associated with this item. This returns a @@ -354,7 +351,7 @@ public interface MenuItem { * @return Returns the last value supplied to {@link #setIntent}, or * null. */ - public Intent getIntent(); + public @Nullable Intent getIntent(); /** * Change both the numeric and alphabetic shortcut associated with this @@ -372,7 +369,7 @@ public interface MenuItem { * using a keyboard with alphabetic keys. * @return This Item so additional setters can be called. */ - public MenuItem setShortcut(char numericChar, char alphaChar); + public @NonNull MenuItem setShortcut(char numericChar, char alphaChar); /** * Change both the numeric and alphabetic shortcut associated with this @@ -397,8 +394,8 @@ public interface MenuItem { * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. * @return This Item so additional setters can be called. */ - default public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers, - int alphaModifiers) { + default public @NonNull MenuItem setShortcut(char numericChar, char alphaChar, + int numericModifiers, int alphaModifiers) { if ((alphaModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON && (numericModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) { return setShortcut(numericChar, alphaChar); @@ -416,7 +413,7 @@ public interface MenuItem { * using a 12-key (numeric) keyboard. * @return This Item so additional setters can be called. */ - public MenuItem setNumericShortcut(char numericChar); + public @NonNull MenuItem setNumericShortcut(char numericChar); /** * Change the numeric shortcut and modifiers associated with this item. @@ -431,7 +428,7 @@ public interface MenuItem { * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. * @return This Item so additional setters can be called. */ - default public MenuItem setNumericShortcut(char numericChar, int numericModifiers) { + default public @NonNull MenuItem setNumericShortcut(char numericChar, int numericModifiers) { if ((numericModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) { return setNumericShortcut(numericChar); } else { @@ -475,7 +472,7 @@ public interface MenuItem { * using a keyboard with alphabetic keys. * @return This Item so additional setters can be called. */ - public MenuItem setAlphabeticShortcut(char alphaChar); + public @NonNull MenuItem setAlphabeticShortcut(char alphaChar); /** * Change the alphabetic shortcut associated with this item. The shortcut @@ -495,7 +492,7 @@ public interface MenuItem { * {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}. * @return This Item so additional setters can be called. */ - default public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) { + default public @NonNull MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) { if ((alphaModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) { return setAlphabeticShortcut(alphaChar); } else { @@ -539,7 +536,7 @@ public interface MenuItem { * @see Menu#setGroupCheckable * @return This Item so additional setters can be called. */ - public MenuItem setCheckable(boolean checkable); + public @NonNull MenuItem setCheckable(boolean checkable); /** * Return whether the item can currently display a check mark. @@ -566,7 +563,7 @@ public interface MenuItem { * it. The default value is false. * @return This Item so additional setters can be called. */ - public MenuItem setChecked(boolean checked); + public @NonNull MenuItem setChecked(boolean checked); /** * Return whether the item is currently displaying a check mark. @@ -586,7 +583,7 @@ public interface MenuItem { * hidden. * @return This Item so additional setters can be called. */ - public MenuItem setVisible(boolean visible); + public @NonNull MenuItem setVisible(boolean visible); /** * Return the visibility of the menu item. @@ -604,7 +601,7 @@ public interface MenuItem { * won't be invokable. * @return This Item so additional setters can be called. */ - public MenuItem setEnabled(boolean enabled); + public @NonNull MenuItem setEnabled(boolean enabled); /** * Return the enabled state of the menu item. @@ -628,7 +625,7 @@ public interface MenuItem { * * @return The associated menu if there is one, else null */ - public SubMenu getSubMenu(); + public @Nullable SubMenu getSubMenu(); /** * Set a custom listener for invocation of this menu item. In most @@ -641,7 +638,8 @@ public interface MenuItem { * @see Activity#onOptionsItemSelected(MenuItem) * @see Activity#onContextItemSelected(MenuItem) */ - public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener); + public @NonNull MenuItem setOnMenuItemClickListener( + @Nullable MenuItem.OnMenuItemClickListener menuItemClickListener); /** * Gets the extra information linked to this menu item. This extra @@ -652,7 +650,7 @@ public interface MenuItem { * @return The extra information linked to the View that added this * menu item to the menu. This can be null. */ - public ContextMenuInfo getMenuInfo(); + public @Nullable ContextMenuInfo getMenuInfo(); /** * Sets how this item should display in the presence of an Action Bar. @@ -690,7 +688,7 @@ public interface MenuItem { * @see #setActionView(View) * @return This MenuItem instance for call chaining. */ - public MenuItem setShowAsActionFlags(int actionEnum); + public @NonNull MenuItem setShowAsActionFlags(int actionEnum); /** * Set an action view for this menu item. An action view will be displayed in place @@ -706,7 +704,7 @@ public interface MenuItem { * * @see #setShowAsAction(int) */ - public MenuItem setActionView(View view); + public @NonNull MenuItem setActionView(@Nullable View view); /** * Set an action view for this menu item. An action view will be displayed in place @@ -722,7 +720,7 @@ public interface MenuItem { * * @see #setShowAsAction(int) */ - public MenuItem setActionView(@LayoutRes int resId); + public @NonNull MenuItem setActionView(@LayoutRes int resId); /** * Returns the currently set action view for this menu item. @@ -732,7 +730,7 @@ public interface MenuItem { * @see #setActionView(View) * @see #setShowAsAction(int) */ - public View getActionView(); + public @Nullable View getActionView(); /** * Sets the {@link ActionProvider} responsible for creating an action view if @@ -748,7 +746,7 @@ public interface MenuItem { * * @see ActionProvider */ - public MenuItem setActionProvider(ActionProvider actionProvider); + public @NonNull MenuItem setActionProvider(@Nullable ActionProvider actionProvider); /** * Gets the {@link ActionProvider}. @@ -758,7 +756,7 @@ public interface MenuItem { * @see ActionProvider * @see #setActionProvider(ActionProvider) */ - public ActionProvider getActionProvider(); + public @Nullable ActionProvider getActionProvider(); /** * Expand the action view associated with this menu item. @@ -806,14 +804,14 @@ public interface MenuItem { * @param listener Listener that will respond to expand/collapse events * @return This menu item instance for call chaining */ - public MenuItem setOnActionExpandListener(OnActionExpandListener listener); + public @NonNull MenuItem setOnActionExpandListener(@Nullable OnActionExpandListener listener); /** * Change the content description associated with this menu item. * * @param contentDescription The new content description. */ - default MenuItem setContentDescription(CharSequence contentDescription) { + default @NonNull MenuItem setContentDescription(@Nullable CharSequence contentDescription) { return this; } @@ -822,7 +820,7 @@ public interface MenuItem { * * @return The content description. */ - default CharSequence getContentDescription() { + default @Nullable CharSequence getContentDescription() { return null; } @@ -831,7 +829,7 @@ public interface MenuItem { * * @param tooltipText The new tooltip text. */ - default MenuItem setTooltipText(CharSequence tooltipText) { + default @NonNull MenuItem setTooltipText(@Nullable CharSequence tooltipText) { return this; } @@ -840,7 +838,7 @@ public interface MenuItem { * * @return The tooltip text. */ - default CharSequence getTooltipText() { + default @Nullable CharSequence getTooltipText() { return null; } 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/PointerIcon.java b/core/java/android/view/PointerIcon.java index 38ae03db3290..2a8e7e468616 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -184,7 +184,7 @@ public final class PointerIcon implements Parcelable { * @see #TYPE_NULL * @hide */ - public static PointerIcon getNullIcon() { + public static @NonNull PointerIcon getNullIcon() { return gNullIcon; } @@ -197,7 +197,7 @@ public final class PointerIcon implements Parcelable { * @throws IllegalArgumentException if context is null. * @hide */ - public static PointerIcon getDefaultIcon(@NonNull Context context) { + public static @NonNull PointerIcon getDefaultIcon(@NonNull Context context) { return getSystemIcon(context, TYPE_DEFAULT); } @@ -211,7 +211,7 @@ public final class PointerIcon implements Parcelable { * * @throws IllegalArgumentException if context is null. */ - public static PointerIcon getSystemIcon(@NonNull Context context, int type) { + public static @NonNull PointerIcon getSystemIcon(@NonNull Context context, int type) { if (context == null) { throw new IllegalArgumentException("context must not be null"); } @@ -287,7 +287,8 @@ public final class PointerIcon implements Parcelable { * @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot * parameters are invalid. */ - public static PointerIcon create(@NonNull Bitmap bitmap, float hotSpotX, float hotSpotY) { + public static @NonNull PointerIcon create(@NonNull Bitmap bitmap, float hotSpotX, + float hotSpotY) { if (bitmap == null) { throw new IllegalArgumentException("bitmap must not be null"); } @@ -321,7 +322,7 @@ public final class PointerIcon implements Parcelable { * @throws Resources.NotFoundException if the resource was not found or the drawable * linked in the resource was not found. */ - public static PointerIcon load(@NonNull Resources resources, @XmlRes int resourceId) { + public static @NonNull PointerIcon load(@NonNull Resources resources, @XmlRes int resourceId) { if (resources == null) { throw new IllegalArgumentException("resources must not be null"); } @@ -342,7 +343,7 @@ public final class PointerIcon implements Parcelable { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public PointerIcon load(@NonNull Context context) { + public @NonNull PointerIcon load(@NonNull Context context) { if (context == null) { throw new IllegalArgumentException("context must not be null"); } @@ -362,7 +363,7 @@ public final class PointerIcon implements Parcelable { return mType; } - public static final @android.annotation.NonNull Parcelable.Creator<PointerIcon> CREATOR + public static final @NonNull Parcelable.Creator<PointerIcon> CREATOR = new Parcelable.Creator<PointerIcon>() { public PointerIcon createFromParcel(Parcel in) { int type = in.readInt(); diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index 346f76cace7d..a0a172d366cc 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -16,6 +16,8 @@ package android.view; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; @@ -67,7 +69,7 @@ public class ScaleGestureDetector { * only wants to update scaling factors if the change is * greater than 0.01. */ - public boolean onScale(ScaleGestureDetector detector); + public boolean onScale(@NonNull ScaleGestureDetector detector); /** * Responds to the beginning of a scaling gesture. Reported by @@ -81,7 +83,7 @@ public class ScaleGestureDetector { * sense, onScaleBegin() may return false to ignore the * rest of the gesture. */ - public boolean onScaleBegin(ScaleGestureDetector detector); + public boolean onScaleBegin(@NonNull ScaleGestureDetector detector); /** * Responds to the end of a scale gesture. Reported by existing @@ -94,7 +96,7 @@ public class ScaleGestureDetector { * @param detector The detector reporting the event - use this to * retrieve extended info about event state. */ - public void onScaleEnd(ScaleGestureDetector detector); + public void onScaleEnd(@NonNull ScaleGestureDetector detector); } /** @@ -109,15 +111,15 @@ public class ScaleGestureDetector { */ public static class SimpleOnScaleGestureListener implements OnScaleGestureListener { - public boolean onScale(ScaleGestureDetector detector) { + public boolean onScale(@NonNull ScaleGestureDetector detector) { return false; } - public boolean onScaleBegin(ScaleGestureDetector detector) { + public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) { return true; } - public void onScaleEnd(ScaleGestureDetector detector) { + public void onScaleEnd(@NonNull ScaleGestureDetector detector) { // Intentionally empty } } @@ -180,7 +182,8 @@ public class ScaleGestureDetector { * * @throws NullPointerException if {@code listener} is null. */ - public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { + public ScaleGestureDetector(@NonNull Context context, + @NonNull OnScaleGestureListener listener) { this(context, listener, null); } @@ -195,8 +198,8 @@ public class ScaleGestureDetector { * * @throws NullPointerException if {@code listener} is null. */ - public ScaleGestureDetector(Context context, OnScaleGestureListener listener, - Handler handler) { + public ScaleGestureDetector(@NonNull Context context, @NonNull OnScaleGestureListener listener, + @Nullable Handler handler) { mContext = context; mListener = listener; final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); @@ -226,7 +229,7 @@ public class ScaleGestureDetector { * @return true if the event was processed and the detector wants to receive the * rest of the MotionEvents in this event stream. */ - public boolean onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(@NonNull MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 904d7c82959b..8f8c5a9fce06 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -197,6 +197,9 @@ public final class SurfaceControl implements Parcelable { private static native int[] nativeGetCompositionDataspaces(); private static native boolean nativeSetActiveColorMode(IBinder displayToken, int colorMode); + private static native boolean nativeGetBootDisplayModeSupport(); + private static native void nativeSetBootDisplayMode(IBinder displayToken, int displayMode); + private static native void nativeClearBootDisplayMode(IBinder displayToken); private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on); private static native void nativeSetGameContentType(IBinder displayToken, boolean on); private static native void nativeSetDisplayPowerMode( @@ -1878,6 +1881,8 @@ public final class SurfaceControl implements Parcelable { public boolean autoLowLatencyModeSupported; public boolean gameContentTypeSupported; + public int preferredBootDisplayMode; + @Override public String toString() { return "DynamicDisplayInfo{" @@ -1887,7 +1892,8 @@ public final class SurfaceControl implements Parcelable { + ", activeColorMode=" + activeColorMode + ", hdrCapabilities=" + hdrCapabilities + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported - + ", gameContentTypeSupported" + gameContentTypeSupported + "}"; + + ", gameContentTypeSupported" + gameContentTypeSupported + + ", preferredBootDisplayMode" + preferredBootDisplayMode + "}"; } @Override @@ -1899,7 +1905,8 @@ public final class SurfaceControl implements Parcelable { && activeDisplayModeId == that.activeDisplayModeId && Arrays.equals(supportedColorModes, that.supportedColorModes) && activeColorMode == that.activeColorMode - && Objects.equals(hdrCapabilities, that.hdrCapabilities); + && Objects.equals(hdrCapabilities, that.hdrCapabilities) + && preferredBootDisplayMode == that.preferredBootDisplayMode; } @Override @@ -2266,6 +2273,36 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public static boolean getBootDisplayModeSupport() { + return nativeGetBootDisplayModeSupport(); + } + + /** There is no associated getter for this method. When this is set, the display is expected + * to start up in this mode next time the device reboots. + * @hide + */ + public static void setBootDisplayMode(IBinder displayToken, int displayModeId) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + nativeSetBootDisplayMode(displayToken, displayModeId); + } + + /** + * @hide + */ + public static void clearBootDisplayMode(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + nativeClearBootDisplayMode(displayToken); + } + + /** + * @hide + */ public static void setAutoLowLatencyMode(IBinder displayToken, boolean on) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); @@ -2694,6 +2731,17 @@ public final class SurfaceControl implements Parcelable { } /** + * Interface to handle request to + * {@link SurfaceControl.Transaction#addTransactionCommittedListener(Executor, TransactionCommittedListener)} + */ + public interface TransactionCommittedListener { + /** + * Invoked when the transaction has been committed in SurfaceFlinger. + */ + void onTransactionCommitted(); + } + + /** * An atomic set of changes to a set of SurfaceControl. */ public static class Transaction implements Closeable, Parcelable { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4ff7e2297ea0..70689bdfb117 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; @@ -8608,7 +8608,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - public AccessibilityNodeInfo createAccessibilityNodeInfoInternal() { + public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfoInternal() { AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { return provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID); @@ -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) @@ -14226,7 +14226,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param arguments Optional action arguments * @return true if the action was consumed by a parent */ - public boolean dispatchNestedPrePerformAccessibilityAction(int action, Bundle arguments) { + public boolean dispatchNestedPrePerformAccessibilityAction(int action, + @Nullable Bundle arguments) { for (ViewParent p = getParent(); p != null; p = p.getParent()) { if (p.onNestedPrePerformAccessibilityAction(this, action, arguments)) { return true; @@ -14254,7 +14255,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param arguments Optional action arguments. * @return Whether the action was performed. */ - public boolean performAccessibilityAction(int action, Bundle arguments) { + public boolean performAccessibilityAction(int action, @Nullable Bundle arguments) { if (mAccessibilityDelegate != null) { return mAccessibilityDelegate.performAccessibilityAction(this, action, arguments); } else { @@ -14270,7 +14271,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @UnsupportedAppUsage - public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) { if (isNestedScrollingEnabled() && (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD @@ -29268,12 +29269,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Called when the view is attached to a window. * @param v The view that was attached */ - public void onViewAttachedToWindow(View v); + public void onViewAttachedToWindow(@NonNull View v); /** * Called when the view is detached from a window. * @param v The view that was detached */ - public void onViewDetachedFromWindow(View v); + public void onViewDetachedFromWindow(@NonNull View v); } /** @@ -29298,7 +29299,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param insets The insets to apply * @return The insets supplied, minus any insets that were consumed */ - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets); + public @NonNull WindowInsets onApplyWindowInsets(@NonNull View v, + @NonNull WindowInsets insets); } private final class UnsetPressedState implements Runnable { @@ -30241,7 +30243,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int) */ - public void sendAccessibilityEvent(View host, int eventType) { + public void sendAccessibilityEvent(@NonNull View host, int eventType) { host.sendAccessibilityEventInternal(eventType); } @@ -30261,7 +30263,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#performAccessibilityAction(int, Bundle) * View#performAccessibilityAction(int, Bundle) */ - public boolean performAccessibilityAction(View host, int action, Bundle args) { + public boolean performAccessibilityAction(@NonNull View host, int action, + @Nullable Bundle args) { return host.performAccessibilityActionInternal(action, args); } @@ -30283,7 +30286,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent) * View#sendAccessibilityEventUnchecked(AccessibilityEvent) */ - public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { + public void sendAccessibilityEventUnchecked(@NonNull View host, + @NonNull AccessibilityEvent event) { host.sendAccessibilityEventUncheckedInternal(event); } @@ -30304,7 +30308,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) */ - public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + public boolean dispatchPopulateAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { return host.dispatchPopulateAccessibilityEventInternal(event); } @@ -30324,7 +30329,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#onPopulateAccessibilityEvent(AccessibilityEvent) * View#onPopulateAccessibilityEvent(AccessibilityEvent) */ - public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + public void onPopulateAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { host.onPopulateAccessibilityEventInternal(event); } @@ -30344,7 +30350,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#onInitializeAccessibilityEvent(AccessibilityEvent) * View#onInitializeAccessibilityEvent(AccessibilityEvent) */ - public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + public void onInitializeAccessibilityEvent(@NonNull View host, + @NonNull AccessibilityEvent event) { host.onInitializeAccessibilityEventInternal(event); } @@ -30363,7 +30370,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) * View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) */ - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + public void onInitializeAccessibilityNodeInfo(@NonNull View host, + @NonNull AccessibilityNodeInfo info) { host.onInitializeAccessibilityNodeInfoInternal(info); } @@ -30415,8 +30423,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) * ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent) */ - public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, - AccessibilityEvent event) { + public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child, + @NonNull AccessibilityEvent event) { return host.onRequestSendAccessibilityEventInternal(child, event); } @@ -30434,7 +30442,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see AccessibilityNodeProvider */ - public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) { + public @Nullable AccessibilityNodeProvider getAccessibilityNodeProvider( + @NonNull View host) { return null; } @@ -30462,7 +30471,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public AccessibilityNodeInfo createAccessibilityNodeInfo(View host) { + public AccessibilityNodeInfo createAccessibilityNodeInfo(@NonNull View host) { return host.createAccessibilityNodeInfoInternal(); } } @@ -31447,23 +31456,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/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 9ed42f36d04b..b25c0255c1a6 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.FloatRange; +import android.annotation.NonNull; import android.annotation.TestApi; import android.annotation.UiContext; import android.app.Activity; @@ -405,7 +406,7 @@ public class ViewConfiguration { * @see #get(android.content.Context) * @see android.util.DisplayMetrics */ - private ViewConfiguration(@UiContext Context context) { + private ViewConfiguration(@NonNull @UiContext Context context) { mConstructedWithContext = true; final Resources res = context.getResources(); final DisplayMetrics metrics = res.getDisplayMetrics(); @@ -517,7 +518,7 @@ public class ViewConfiguration { * {@link Context#createWindowContext(int, Bundle)}. */ // TODO(b/182007470): Use @ConfigurationContext instead - public static ViewConfiguration get(@UiContext Context context) { + public static ViewConfiguration get(@NonNull @UiContext Context context) { StrictMode.assertConfigurationContext(context, "ViewConfiguration"); final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 49f5229d3c09..128da314e907 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; @@ -298,7 +299,7 @@ public interface ViewParent { * * @param child The child whose drawable state has changed. */ - public void childDrawableStateChanged(View child); + public void childDrawableStateChanged(@NonNull View child); /** * Called when a child does not want this parent and its ancestors to @@ -337,7 +338,7 @@ public interface ViewParent { * false otherwise * @return Whether the group scrolled to handle the operation */ - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, + public boolean requestChildRectangleOnScreen(@NonNull View child, Rect rectangle, boolean immediate); /** @@ -356,7 +357,7 @@ public interface ViewParent { * @param event The event to be sent. * @return True if the event was sent. */ - public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event); + public boolean requestSendAccessibilityEvent(@NonNull View child, AccessibilityEvent event); /** * Called when a child view now has or no longer is tracking transient state. @@ -381,7 +382,7 @@ public interface ViewParent { * @param child Child view whose state has changed * @param hasTransientState true if this child has transient state */ - public void childHasTransientStateChanged(View child, boolean hasTransientState); + public void childHasTransientStateChanged(@NonNull View child, boolean hasTransientState); /** * Ask that a new dispatch of {@link View#fitSystemWindows(Rect) @@ -418,7 +419,7 @@ public interface ViewParent { * </ul> */ public void notifySubtreeAccessibilityStateChanged( - View child, @NonNull View source, int changeType); + @NonNull View child, @NonNull View source, int changeType); /** * Tells if this view parent can resolve the layout direction. @@ -525,7 +526,8 @@ public interface ViewParent { * {@link View#SCROLL_AXIS_VERTICAL} or both * @return true if this ViewParent accepts the nested scroll operation */ - public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes); + public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, + int nestedScrollAxes); /** * React to the successful claiming of a nested scroll operation. @@ -543,7 +545,8 @@ public interface ViewParent { * @see #onStartNestedScroll(View, View, int) * @see #onStopNestedScroll(View) */ - public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes); + public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, + int nestedScrollAxes); /** * React to a nested scroll operation ending. @@ -556,7 +559,7 @@ public interface ViewParent { * * @param target View that initiated the nested scroll */ - public void onStopNestedScroll(View target); + public void onStopNestedScroll(@NonNull View target); /** * React to a nested scroll in progress. @@ -579,7 +582,7 @@ public interface ViewParent { * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target * @param dyUnconsumed Vertical scroll distance in pixels not consumed by target */ - public void onNestedScroll(View target, int dxConsumed, int dyConsumed, + public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed); /** @@ -602,7 +605,7 @@ public interface ViewParent { * @param dy Vertical scroll distance in pixels * @param consumed Output. The horizontal and vertical scroll distance consumed by this parent */ - public void onNestedPreScroll(View target, int dx, int dy, int[] consumed); + public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed); /** * Request a fling from a nested scroll. @@ -623,7 +626,8 @@ public interface ViewParent { * @param consumed true if the child consumed the fling, false otherwise * @return true if this parent consumed or otherwise reacted to the fling */ - public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed); + public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, + boolean consumed); /** * React to a nested fling before the target view consumes it. @@ -645,7 +649,7 @@ public interface ViewParent { * @param velocityY Vertical velocity in pixels per second * @return true if this parent consumed the fling ahead of the target view */ - public boolean onNestedPreFling(View target, float velocityX, float velocityY); + public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY); /** * React to an accessibility action delegated by a target descendant view before the target @@ -664,7 +668,8 @@ public interface ViewParent { * @param arguments Optional action arguments * @return true if the action was consumed by this ViewParent */ - public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments); + public boolean onNestedPrePerformAccessibilityAction(@NonNull View target, int action, + @Nullable Bundle arguments); /** * Given a touchable region of a child, this method reduces region by the bounds of all views on diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 65cc2f8bcd5a..774d6978bf2a 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -20,6 +20,8 @@ import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.RenderNode; import java.util.ArrayList; @@ -246,7 +248,7 @@ public class ViewPropertyAnimator { * * @param view The View associated with this ViewPropertyAnimator */ - ViewPropertyAnimator(View view) { + ViewPropertyAnimator(@NonNull View view) { mView = view; view.ensureTransformationInfo(); } @@ -259,7 +261,7 @@ public class ViewPropertyAnimator { * cannot be negative. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator setDuration(long duration) { + public @NonNull ViewPropertyAnimator setDuration(long duration) { if (duration < 0) { throw new IllegalArgumentException("Animators cannot have negative duration: " + duration); @@ -316,7 +318,7 @@ public class ViewPropertyAnimator { * cannot be negative. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator setStartDelay(long startDelay) { + public @NonNull ViewPropertyAnimator setStartDelay(long startDelay) { if (startDelay < 0) { throw new IllegalArgumentException("Animators cannot have negative start " + "delay: " + startDelay); @@ -335,7 +337,7 @@ public class ViewPropertyAnimator { * of <code>null</code> will result in linear interpolation. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) { + public @NonNull ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) { mInterpolatorSet = true; mInterpolator = interpolator; return this; @@ -346,7 +348,7 @@ public class ViewPropertyAnimator { * * @return The timing interpolator for this animation. */ - public TimeInterpolator getInterpolator() { + public @Nullable TimeInterpolator getInterpolator() { if (mInterpolatorSet) { return mInterpolator; } else { @@ -369,12 +371,12 @@ public class ViewPropertyAnimator { * <code>null</code> removes any existing listener. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) { + public @NonNull ViewPropertyAnimator setListener(@Nullable Animator.AnimatorListener listener) { mListener = listener; return this; } - Animator.AnimatorListener getListener() { + @Nullable Animator.AnimatorListener getListener() { return mListener; } @@ -392,12 +394,13 @@ public class ViewPropertyAnimator { * <code>null</code> removes any existing listener. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) { + public @NonNull ViewPropertyAnimator setUpdateListener( + @Nullable ValueAnimator.AnimatorUpdateListener listener) { mUpdateListener = listener; return this; } - ValueAnimator.AnimatorUpdateListener getUpdateListener() { + @Nullable ValueAnimator.AnimatorUpdateListener getUpdateListener() { return mUpdateListener; } @@ -441,7 +444,7 @@ public class ViewPropertyAnimator { * @see View#setX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator x(float value) { + public @NonNull ViewPropertyAnimator x(float value) { animateProperty(X, value); return this; } @@ -454,7 +457,7 @@ public class ViewPropertyAnimator { * @see View#setX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator xBy(float value) { + public @NonNull ViewPropertyAnimator xBy(float value) { animatePropertyBy(X, value); return this; } @@ -467,7 +470,7 @@ public class ViewPropertyAnimator { * @see View#setY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator y(float value) { + public @NonNull ViewPropertyAnimator y(float value) { animateProperty(Y, value); return this; } @@ -480,7 +483,7 @@ public class ViewPropertyAnimator { * @see View#setY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator yBy(float value) { + public @NonNull ViewPropertyAnimator yBy(float value) { animatePropertyBy(Y, value); return this; } @@ -493,7 +496,7 @@ public class ViewPropertyAnimator { * @see View#setZ(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator z(float value) { + public @NonNull ViewPropertyAnimator z(float value) { animateProperty(Z, value); return this; } @@ -506,7 +509,7 @@ public class ViewPropertyAnimator { * @see View#setZ(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator zBy(float value) { + public @NonNull ViewPropertyAnimator zBy(float value) { animatePropertyBy(Z, value); return this; } @@ -519,7 +522,7 @@ public class ViewPropertyAnimator { * @see View#setRotation(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotation(float value) { + public @NonNull ViewPropertyAnimator rotation(float value) { animateProperty(ROTATION, value); return this; } @@ -532,7 +535,7 @@ public class ViewPropertyAnimator { * @see View#setRotation(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotationBy(float value) { + public @NonNull ViewPropertyAnimator rotationBy(float value) { animatePropertyBy(ROTATION, value); return this; } @@ -545,7 +548,7 @@ public class ViewPropertyAnimator { * @see View#setRotationX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotationX(float value) { + public @NonNull ViewPropertyAnimator rotationX(float value) { animateProperty(ROTATION_X, value); return this; } @@ -558,7 +561,7 @@ public class ViewPropertyAnimator { * @see View#setRotationX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotationXBy(float value) { + public @NonNull ViewPropertyAnimator rotationXBy(float value) { animatePropertyBy(ROTATION_X, value); return this; } @@ -571,7 +574,7 @@ public class ViewPropertyAnimator { * @see View#setRotationY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotationY(float value) { + public @NonNull ViewPropertyAnimator rotationY(float value) { animateProperty(ROTATION_Y, value); return this; } @@ -584,7 +587,7 @@ public class ViewPropertyAnimator { * @see View#setRotationY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator rotationYBy(float value) { + public @NonNull ViewPropertyAnimator rotationYBy(float value) { animatePropertyBy(ROTATION_Y, value); return this; } @@ -597,7 +600,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationX(float value) { + public @NonNull ViewPropertyAnimator translationX(float value) { animateProperty(TRANSLATION_X, value); return this; } @@ -610,7 +613,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationXBy(float value) { + public @NonNull ViewPropertyAnimator translationXBy(float value) { animatePropertyBy(TRANSLATION_X, value); return this; } @@ -623,7 +626,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationY(float value) { + public @NonNull ViewPropertyAnimator translationY(float value) { animateProperty(TRANSLATION_Y, value); return this; } @@ -636,7 +639,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationYBy(float value) { + public @NonNull ViewPropertyAnimator translationYBy(float value) { animatePropertyBy(TRANSLATION_Y, value); return this; } @@ -649,7 +652,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationZ(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationZ(float value) { + public @NonNull ViewPropertyAnimator translationZ(float value) { animateProperty(TRANSLATION_Z, value); return this; } @@ -662,7 +665,7 @@ public class ViewPropertyAnimator { * @see View#setTranslationZ(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator translationZBy(float value) { + public @NonNull ViewPropertyAnimator translationZBy(float value) { animatePropertyBy(TRANSLATION_Z, value); return this; } @@ -674,7 +677,7 @@ public class ViewPropertyAnimator { * @see View#setScaleX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator scaleX(float value) { + public @NonNull ViewPropertyAnimator scaleX(float value) { animateProperty(SCALE_X, value); return this; } @@ -687,7 +690,7 @@ public class ViewPropertyAnimator { * @see View#setScaleX(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator scaleXBy(float value) { + public @NonNull ViewPropertyAnimator scaleXBy(float value) { animatePropertyBy(SCALE_X, value); return this; } @@ -700,7 +703,7 @@ public class ViewPropertyAnimator { * @see View#setScaleY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator scaleY(float value) { + public @NonNull ViewPropertyAnimator scaleY(float value) { animateProperty(SCALE_Y, value); return this; } @@ -713,7 +716,7 @@ public class ViewPropertyAnimator { * @see View#setScaleY(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator scaleYBy(float value) { + public @NonNull ViewPropertyAnimator scaleYBy(float value) { animatePropertyBy(SCALE_Y, value); return this; } @@ -726,7 +729,7 @@ public class ViewPropertyAnimator { * @see View#setAlpha(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) { + public @NonNull ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) { animateProperty(ALPHA, value); return this; } @@ -739,7 +742,7 @@ public class ViewPropertyAnimator { * @see View#setAlpha(float) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator alphaBy(float value) { + public @NonNull ViewPropertyAnimator alphaBy(float value) { animatePropertyBy(ALPHA, value); return this; } @@ -765,7 +768,7 @@ public class ViewPropertyAnimator { * @see View#setLayerType(int, android.graphics.Paint) * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator withLayer() { + public @NonNull ViewPropertyAnimator withLayer() { mPendingSetupAction= new Runnable() { @Override public void run() { @@ -803,7 +806,7 @@ public class ViewPropertyAnimator { * @param runnable The action to run when the next animation starts. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator withStartAction(Runnable runnable) { + public @NonNull ViewPropertyAnimator withStartAction(Runnable runnable) { mPendingOnStartAction = runnable; if (runnable != null && mAnimatorOnStartMap == null) { mAnimatorOnStartMap = new HashMap<Animator, Runnable>(); @@ -832,7 +835,7 @@ public class ViewPropertyAnimator { * @param runnable The action to run when the next animation ends. * @return This object, allowing calls to methods in this class to be chained. */ - public ViewPropertyAnimator withEndAction(Runnable runnable) { + public @NonNull ViewPropertyAnimator withEndAction(Runnable runnable) { mPendingOnEndAction = runnable; if (runnable != null && mAnimatorOnEndMap == null) { mAnimatorOnEndMap = new HashMap<Animator, Runnable>(); @@ -1061,7 +1064,7 @@ public class ViewPropertyAnimator { private class AnimatorEventListener implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { @Override - public void onAnimationStart(Animator animation) { + public void onAnimationStart(@NonNull Animator animation) { if (mAnimatorSetupMap != null) { Runnable r = mAnimatorSetupMap.get(animation); if (r != null) { @@ -1082,7 +1085,7 @@ public class ViewPropertyAnimator { } @Override - public void onAnimationCancel(Animator animation) { + public void onAnimationCancel(@NonNull Animator animation) { if (mListener != null) { mListener.onAnimationCancel(animation); } @@ -1092,14 +1095,14 @@ public class ViewPropertyAnimator { } @Override - public void onAnimationRepeat(Animator animation) { + public void onAnimationRepeat(@NonNull Animator animation) { if (mListener != null) { mListener.onAnimationRepeat(animation); } } @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationEnd(@NonNull Animator animation) { mView.setHasTransientState(false); if (mAnimatorCleanupMap != null) { Runnable r = mAnimatorCleanupMap.get(animation); @@ -1130,7 +1133,7 @@ public class ViewPropertyAnimator { * the current value of each property. */ @Override - public void onAnimationUpdate(ValueAnimator animation) { + public void onAnimationUpdate(@NonNull ValueAnimator animation) { PropertyBundle propertyBundle = mAnimatorMap.get(animation); if (propertyBundle == null) { // Shouldn't happen, but just to play it safe 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/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 6f4bc719dc84..82e823fa77e6 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -526,9 +526,9 @@ public final class AccessibilityInteractionClient * @param prefetchFlags flags to guide prefetching. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ - public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, - int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache, - int prefetchFlags, Bundle arguments) { + public @Nullable AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId( + int connectionId, int accessibilityWindowId, long accessibilityNodeId, + boolean bypassCache, int prefetchFlags, Bundle arguments) { if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0 && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) { throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS" diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java index f4c7b96b8edc..91d4a3598e65 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java +++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java @@ -17,6 +17,7 @@ package android.view.accessibility; import android.accessibilityservice.AccessibilityService; +import android.annotation.Nullable; import android.os.Bundle; import android.view.View; @@ -195,7 +196,7 @@ public abstract class AccessibilityNodeProvider { * @see View#createAccessibilityNodeInfo() * @see AccessibilityNodeInfo */ - public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { + public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { return null; } @@ -234,7 +235,7 @@ public abstract class AccessibilityNodeProvider { * @see #createAccessibilityNodeInfo(int) * @see AccessibilityNodeInfo */ - public boolean performAction(int virtualViewId, int action, Bundle arguments) { + public boolean performAction(int virtualViewId, int action, @Nullable Bundle arguments) { return false; } @@ -252,7 +253,7 @@ public abstract class AccessibilityNodeProvider { * @see #createAccessibilityNodeInfo(int) * @see AccessibilityNodeInfo */ - public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text, + public @Nullable List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text, int virtualViewId) { return null; } @@ -268,7 +269,7 @@ public abstract class AccessibilityNodeProvider { * @see AccessibilityNodeInfo#FOCUS_INPUT * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY */ - public AccessibilityNodeInfo findFocus(int focus) { + public @Nullable AccessibilityNodeInfo findFocus(int focus) { return null; } } diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index 426a3f448543..cf2ea15707cb 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -133,7 +133,7 @@ public class AccessibilityRecord { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setSource(View source) { + public void setSource(@Nullable View source) { setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID); } @@ -184,7 +184,7 @@ public class AccessibilityRecord { * </p> * @return The info of the source. */ - public AccessibilityNodeInfo getSource() { + public @Nullable AccessibilityNodeInfo getSource() { enforceSealed(); if ((mConnectionId == UNDEFINED) || (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) @@ -629,7 +629,7 @@ public class AccessibilityRecord { * * @return The class name. */ - public CharSequence getClassName() { + public @Nullable CharSequence getClassName() { return mClassName; } @@ -640,7 +640,7 @@ public class AccessibilityRecord { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setClassName(CharSequence className) { + public void setClassName(@Nullable CharSequence className) { enforceNotSealed(); mClassName = className; } @@ -651,7 +651,7 @@ public class AccessibilityRecord { * * @return The text. */ - public List<CharSequence> getText() { + public @NonNull List<CharSequence> getText() { return mText; } @@ -660,7 +660,7 @@ public class AccessibilityRecord { * * @return The text before the change. */ - public CharSequence getBeforeText() { + public @Nullable CharSequence getBeforeText() { return mBeforeText; } @@ -671,7 +671,7 @@ public class AccessibilityRecord { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setBeforeText(CharSequence beforeText) { + public void setBeforeText(@Nullable CharSequence beforeText) { enforceNotSealed(); mBeforeText = (beforeText == null) ? null : beforeText.subSequence(0, beforeText.length()); @@ -682,7 +682,7 @@ public class AccessibilityRecord { * * @return The description. */ - public CharSequence getContentDescription() { + public @Nullable CharSequence getContentDescription() { return mContentDescription; } @@ -693,7 +693,7 @@ public class AccessibilityRecord { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setContentDescription(CharSequence contentDescription) { + public void setContentDescription(@Nullable CharSequence contentDescription) { enforceNotSealed(); mContentDescription = (contentDescription == null) ? null : contentDescription.subSequence(0, contentDescription.length()); @@ -704,7 +704,7 @@ public class AccessibilityRecord { * * @return The parcelable data. */ - public Parcelable getParcelableData() { + public @Nullable Parcelable getParcelableData() { return mParcelableData; } @@ -715,7 +715,7 @@ public class AccessibilityRecord { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setParcelableData(Parcelable parcelableData) { + public void setParcelableData(@Nullable Parcelable parcelableData) { enforceNotSealed(); mParcelableData = parcelableData; } @@ -822,7 +822,7 @@ public class AccessibilityRecord { * @return An instance. */ @Deprecated - public static AccessibilityRecord obtain(AccessibilityRecord record) { + public static @NonNull AccessibilityRecord obtain(@NonNull AccessibilityRecord record) { AccessibilityRecord clone = AccessibilityRecord.obtain(); clone.init(record); return clone; @@ -836,7 +836,7 @@ public class AccessibilityRecord { * @return An instance. */ @Deprecated - public static AccessibilityRecord obtain() { + public static @NonNull AccessibilityRecord obtain() { return new AccessibilityRecord(); } @@ -854,7 +854,7 @@ public class AccessibilityRecord { * * @param record The to initialize from. */ - void init(AccessibilityRecord record) { + void init(@NonNull AccessibilityRecord record) { mSealed = record.mSealed; mBooleanProperties = record.mBooleanProperties; mCurrentItemIndex = record.mCurrentItemIndex; 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/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java index 99d6b9c54c20..341c71b18d69 100644 --- a/core/java/android/view/animation/PathInterpolator.java +++ b/core/java/android/view/animation/PathInterpolator.java @@ -15,6 +15,8 @@ */ package android.view.animation; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources.Theme; @@ -61,7 +63,7 @@ public class PathInterpolator extends BaseInterpolator implements NativeInterpol * * @param path The <code>Path</code> to use to make the line representing the interpolator. */ - public PathInterpolator(Path path) { + public PathInterpolator(@NonNull Path path) { initPath(path); } @@ -94,7 +96,7 @@ public class PathInterpolator extends BaseInterpolator implements NativeInterpol } /** @hide */ - public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) { + public PathInterpolator(Resources res, @Nullable Theme theme, @NonNull AttributeSet attrs) { TypedArray a; if (theme != null) { a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0, 0); 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/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java index c47f6f7db53a..0f0fa4a83c00 100644 --- a/core/java/android/view/autofill/AutofillClientController.java +++ b/core/java/android/view/autofill/AutofillClientController.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.text.TextUtils; +import android.util.Dumpable; import android.util.Log; import android.util.Slog; import android.view.KeyEvent; @@ -43,7 +44,7 @@ import java.util.Arrays; * * @hide */ -public final class AutofillClientController implements AutofillManager.AutofillClient { +public final class AutofillClientController implements AutofillManager.AutofillClient, Dumpable { private static final String TAG = "AutofillClientController"; @@ -54,6 +55,8 @@ public final class AutofillClientController implements AutofillManager.AutofillC public static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded"; public static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:"; + public static final String DUMPABLE_NAME = "AutofillManager"; + /** The last autofill id that was returned from {@link #getNextAutofillId()} */ public int mLastAutofillId = View.LAST_APP_AUTOFILL_ID; @@ -73,6 +76,7 @@ public final class AutofillClientController implements AutofillManager.AutofillC */ public AutofillClientController(Activity activity) { mActivity = activity; + activity.addDumpable(this); } private AutofillManager getAutofillManager() { @@ -280,10 +284,14 @@ public final class AutofillClientController implements AutofillManager.AutofillC } } - /** - * Prints autofill related information for the Activity. - */ - public void dumpAutofillManager(String prefix, PrintWriter writer) { + @Override + public String getDumpableName() { + return DUMPABLE_NAME; + } + + @Override + public void dump(PrintWriter writer, String[] args) { + final String prefix = ""; final AutofillManager afm = getAutofillManager(); if (afm != null) { afm.dump(prefix, writer); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index bcab36629d1e..54bd9e701acf 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -29,6 +29,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UserIdInt; +import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.ContentCaptureOptions; @@ -41,6 +42,7 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Dumpable; import android.util.Log; import android.util.Slog; import android.view.View; @@ -215,6 +217,9 @@ public final class ContentCaptureManager { /** @hide */ public static final boolean DEBUG = false; + /** @hide */ + public static final String DUMPABLE_NAME = "ContentCaptureManager"; + /** Error happened during the data sharing session. */ public static final int DATA_SHARE_ERROR_UNKNOWN = 1; @@ -402,6 +407,9 @@ public final class ContentCaptureManager { mService = Objects.requireNonNull(service, "service cannot be null"); mOptions = Objects.requireNonNull(options, "options cannot be null"); + if (context instanceof Activity) { + ((Activity) context).addDumpable(new Dumper()); + } ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel); if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); @@ -740,28 +748,37 @@ public final class ContentCaptureManager { return resultReceiver; } - /** @hide */ - public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.println("ContentCaptureManager"); - final String prefix2 = prefix + " "; - synchronized (mLock) { - pw.print(prefix2); pw.print("isContentCaptureEnabled(): "); - pw.println(isContentCaptureEnabled()); - pw.print(prefix2); pw.print("Debug: "); pw.print(sDebug); - pw.print(" Verbose: "); pw.println(sVerbose); - pw.print(prefix2); pw.print("Context: "); pw.println(mContext); - pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId()); - pw.print(prefix2); pw.print("Service: "); pw.println(mService); - pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags); - pw.print(prefix2); pw.print("Options: "); mOptions.dumpShort(pw); pw.println(); - if (mMainSession != null) { - final String prefix3 = prefix2 + " "; - pw.print(prefix2); pw.println("Main session:"); - mMainSession.dump(prefix3, pw); - } else { - pw.print(prefix2); pw.println("No sessions"); + // NOTE: ContentCaptureManager cannot implement it directly as it would be exposed as public API + private final class Dumper implements Dumpable { + @Override + public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { + String prefix = ""; + pw.print(prefix); pw.println("ContentCaptureManager"); + final String prefix2 = prefix + " "; + synchronized (mLock) { + pw.print(prefix2); pw.print("isContentCaptureEnabled(): "); + pw.println(isContentCaptureEnabled()); + pw.print(prefix2); pw.print("Debug: "); pw.print(sDebug); + pw.print(" Verbose: "); pw.println(sVerbose); + pw.print(prefix2); pw.print("Context: "); pw.println(mContext); + pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId()); + pw.print(prefix2); pw.print("Service: "); pw.println(mService); + pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags); + pw.print(prefix2); pw.print("Options: "); mOptions.dumpShort(pw); pw.println(); + if (mMainSession != null) { + final String prefix3 = prefix2 + " "; + pw.print(prefix2); pw.println("Main session:"); + mMainSession.dump(prefix3, pw); + } else { + pw.print(prefix2); pw.println("No sessions"); + } } } + + @Override + public String getDumpableName() { + return DUMPABLE_NAME; + } } /** diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 2702c2d69c2b..8cf032bc03cb 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -33,6 +33,7 @@ import android.os.HandlerThread; import android.os.Process; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Dumpable; import android.util.IntArray; import android.util.Log; import android.util.LongSparseArray; @@ -62,11 +63,15 @@ import java.util.function.BiConsumer; * * @hide */ -public class UiTranslationController { +public class UiTranslationController implements Dumpable { public static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG); + /** @hide */ + public static final String DUMPABLE_NAME = "UiTranslationController"; + private static final String TAG = "UiTranslationController"; + @NonNull private final Activity mActivity; @NonNull @@ -104,6 +109,7 @@ public class UiTranslationController { Process.THREAD_PRIORITY_FOREGROUND); mWorkerThread.start(); mWorkerHandler = mWorkerThread.getThreadHandler(); + activity.addDumpable(this); } /** @@ -206,10 +212,14 @@ public class UiTranslationController { mLastRequestAutofillIds.addAll(views); } - /** - * Called to dump the translation information for Activity. - */ - public void dump(String outerPrefix, PrintWriter pw) { + @Override + public String getDumpableName() { + return DUMPABLE_NAME; + } + + @Override + public void dump(PrintWriter pw, String[] args) { + String outerPrefix = ""; pw.print(outerPrefix); pw.println("UiTranslationController:"); final String pfx = outerPrefix + " "; pw.print(pfx); pw.print("activity: "); pw.print(mActivity); 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/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 347153c7e53e..cdb69e546b8f 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1011,7 +1011,9 @@ public class ResolverActivity extends Activity implements protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); ViewPager viewPager = findViewById(R.id.profile_pager); - outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem()); + if (viewPager != null) { + outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem()); + } } @Override @@ -1019,7 +1021,9 @@ public class ResolverActivity extends Activity implements super.onRestoreInstanceState(savedInstanceState); resetButtonBar(); ViewPager viewPager = findViewById(R.id.profile_pager); - viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY)); + if (viewPager != null) { + viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY)); + } mMultiProfilePagerAdapter.clearInactiveProfileCache(); } @@ -1568,6 +1572,11 @@ public class ResolverActivity extends Activity implements rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted; } + if (shouldUseMiniResolver()) { + configureMiniResolverContent(); + return false; + } + if (useLayoutWithDefault()) { mLayoutId = R.layout.resolver_list_with_default; } else { @@ -1578,6 +1587,72 @@ public class ResolverActivity extends Activity implements return postRebuildList(rebuildCompleted); } + private void configureMiniResolverContent() { + mLayoutId = R.layout.miniresolver; + setContentView(mLayoutId); + + DisplayResolveInfo sameProfileResolveInfo = + mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0); + boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK; + + DisplayResolveInfo otherProfileResolveInfo = + mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList.get(0); + ImageView icon = findViewById(R.id.icon); + // TODO: Set icon drawable to app icon. + + ((TextView) findViewById(R.id.open_cross_profile)).setText( + getResources().getString( + inWorkProfile ? R.string.miniresolver_open_in_personal + : R.string.miniresolver_open_in_work, + otherProfileResolveInfo.getDisplayLabel())); + ((Button) findViewById(R.id.use_same_profile_browser)).setText( + inWorkProfile ? R.string.miniresolver_use_work_browser + : R.string.miniresolver_use_personal_browser); + + findViewById(R.id.use_same_profile_browser).setOnClickListener( + v -> safelyStartActivity(sameProfileResolveInfo)); + + findViewById(R.id.button_open).setOnClickListener(v -> { + Intent intent = otherProfileResolveInfo.getResolvedIntent(); + if (intent != null) { + prepareIntentForCrossProfileLaunch(intent); + } + safelyStartActivityInternal(otherProfileResolveInfo, + mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController + .getUserHandle()); + }); + } + + private boolean shouldUseMiniResolver() { + if (mMultiProfilePagerAdapter.getActiveListAdapter() == null + || mMultiProfilePagerAdapter.getInactiveListAdapter() == null) { + return false; + } + List<DisplayResolveInfo> sameProfileList = + mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList; + List<DisplayResolveInfo> otherProfileList = + mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList; + + if (otherProfileList.size() != 1) { + Log.d(TAG, "Found " + otherProfileList.size() + " resolvers in the other profile"); + return false; + } + + if (otherProfileList.get(0).getResolveInfo().handleAllWebDataURI) { + Log.d(TAG, "Other profile is a web browser"); + return false; + } + + for (DisplayResolveInfo info : sameProfileList) { + if (!info.getResolveInfo().handleAllWebDataURI) { + Log.d(TAG, "Non-browser found in this profile"); + return false; + } + } + + return true; + } + /** * Finishing procedures to be performed after the list has been rebuilt. * </p>Subclasses must call postRebuildListInternal at the end of postRebuildList. diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c0fec62bdd94..5ba45c9b8bc3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -607,11 +607,15 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_BT = 0x08; int UPDATE_RPM = 0x10; int UPDATE_DISPLAY = 0x20; + int RESET = 0x40; + int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY; int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT; + int UPDATE_ON_RESET = UPDATE_ALL | RESET; + @IntDef(flag = true, prefix = "UPDATE_", value = { UPDATE_CPU, UPDATE_WIFI, @@ -12909,7 +12913,7 @@ public class BatteryStatsImpl extends BatteryStats { // Flush external data, gathering snapshots, but don't process it since it is pre-reset data mIgnoreNextExternalStats = true; - mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ALL); + mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ON_RESET); mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS); } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 2925341cd948..40e40856b000 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -87,7 +87,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.OnBackInvokedDispatcher; import android.view.PendingInsetsController; import android.view.ThreadedRenderer; import android.view.View; @@ -109,7 +108,6 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.PopupWindow; -import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; @@ -297,7 +295,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return true; }; private Consumer<Boolean> mCrossWindowBlurEnabledListener; - private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { @@ -326,7 +323,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); - mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(); } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1880,7 +1876,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } mPendingInsetsController.detach(); - mOnBackInvokedDispatcher.detachFromWindow(); } @Override @@ -1925,11 +1920,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return mPendingInsetsController; } - @Override - public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - private ActionMode createActionMode( int type, ActionMode.Callback2 callback, View originatingView) { switch (type) { @@ -2384,7 +2374,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } } - mOnBackInvokedDispatcher.clear(); } @Override @@ -2666,15 +2655,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - /** - * Returns the {@link OnBackInvokedDispatcher} on the decor view. - */ - @Override - @Nullable - public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { - return mOnBackInvokedDispatcher; - } - @Override public String toString() { return "DecorView@" + Integer.toHexString(this.hashCode()) + "[" diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 7755b694af03..12f38a4d5b9a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -91,6 +91,8 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; import android.view.ScrollCaptureCallback; import android.view.SearchEvent; import android.view.SurfaceHolder.Callback2; @@ -110,6 +112,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import android.window.ProxyOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.view.menu.ContextMenuBuilder; @@ -134,7 +137,8 @@ import java.util.List; * * @hide */ -public class PhoneWindow extends Window implements MenuBuilder.Callback { +public class PhoneWindow extends Window implements MenuBuilder.Callback, + OnBackInvokedDispatcherOwner { private final static String TAG = "PhoneWindow"; @@ -340,6 +344,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean mDecorFitsSystemWindows = true; + private ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher = + new ProxyOnBackInvokedDispatcher(); + static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); @@ -2146,6 +2153,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */ void onViewRootImplSet(ViewRootImpl viewRoot) { viewRoot.setActivityConfigCallback(mActivityConfigCallback); + mProxyOnBackInvokedDispatcher.setActualDispatcherOwner(viewRoot); applyDecorFitsSystemWindows(); } @@ -3993,4 +4001,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public AttachedSurfaceControl getRootSurfaceControl() { return getViewRootImplOrNull(); } + + @NonNull + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + return mProxyOnBackInvokedDispatcher; + } } diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index a52ae107d983..7262e846d9b0 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -438,10 +438,16 @@ public class MeasuredEnergyStats { mState = state; mStateChangeTimestampMs = timestampMs; if (mAccumulatedMultiStateChargeMicroCoulomb == null) { - return; + mAccumulatedMultiStateChargeMicroCoulomb = + new LongMultiStateCounter[mAccumulatedChargeMicroCoulomb.length]; } for (int i = 0; i < mAccumulatedMultiStateChargeMicroCoulomb.length; i++) { LongMultiStateCounter counter = mAccumulatedMultiStateChargeMicroCoulomb[i]; + if (counter == null && mConfig.isSupportedMultiStateBucket(i)) { + counter = new LongMultiStateCounter(mConfig.mStateNames.length); + counter.updateValue(0, timestampMs); + mAccumulatedMultiStateChargeMicroCoulomb[i] = counter; + } if (counter != null) { counter.setState(state, timestampMs); } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 23ebc9f94915..51eb4296d7ae 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -24,12 +24,14 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.service.notification.StatusBarNotification; import android.view.InsetsVisibilities; import com.android.internal.statusbar.IAddTileResultCallback; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.view.AppearanceRegion; @@ -296,4 +298,15 @@ oneway interface IStatusBar void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback); void cancelRequestAddTile(in String packageName); + + /** Notifies System UI about an update to the media tap-to-transfer sender state. */ + void updateMediaTapToTransferSenderDisplay( + int displayState, + in MediaRoute2Info routeInfo, + in IUndoMediaTransferCallback undoCallback); + + /** Notifies System UI about an update to the media tap-to-transfer receiver state. */ + void updateMediaTapToTransferReceiverDisplay( + int displayState, + in MediaRoute2Info routeInfo); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index f28325e3cc51..0c45e5b3eab4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -24,6 +24,7 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; @@ -33,6 +34,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; @@ -196,4 +198,15 @@ interface IStatusBarService */ void onSessionStarted(int sessionType, in InstanceId instanceId); void onSessionEnded(int sessionType, in InstanceId instanceId); + + /** Notifies System UI about an update to the media tap-to-transfer sender state. */ + void updateMediaTapToTransferSenderDisplay( + int displayState, + in MediaRoute2Info routeInfo, + in IUndoMediaTransferCallback undoCallback); + + /** Notifies System UI about an update to the media tap-to-transfer receiver state. */ + void updateMediaTapToTransferReceiverDisplay( + int displayState, + in MediaRoute2Info routeInfo); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl index b47be8736d23..3dd29807be01 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl +++ b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl @@ -14,17 +14,16 @@ * limitations under the License. */ -package com.android.systemui.shared.mediattt; +package com.android.internal.statusbar; /** - * An interface that will be invoked by System UI if the user choose to undo a transfer. - * - * Other services will implement this interface and System UI will invoke it. + * An interface that will be invoked if the user chooses to undo a transfer. */ -interface IUndoTransferCallback { +interface IUndoMediaTransferCallback { /** - * Invoked by SystemUI when the user requests to undo the media transfer that just occurred. + * Invoked to notify callers that the user has chosen to undo the media transfer that just + * occurred. * * Implementors of this method are repsonsible for actually undoing the transfer. */ diff --git a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java index d48b4b136f4a..d3916846905b 100644 --- a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java +++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java @@ -15,7 +15,6 @@ */ package com.android.internal.util.dump; -import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Dumpable; import android.util.DumpableContainer; @@ -38,7 +37,6 @@ public final class DumpableContainerImpl implements DumpableContainer { private static final boolean DEBUG = false; - @Nullable private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>(); @Override @@ -64,7 +62,7 @@ public final class DumpableContainerImpl implements DumpableContainer { * Dumps the number of dumpable, without a newline. */ private int dumpNumberDumpables(IndentingPrintWriter writer) { - int size = mDumpables == null ? 0 : mDumpables.size(); + int size = mDumpables.size(); if (size == 0) { writer.print("No dumpables"); } else { @@ -102,7 +100,7 @@ public final class DumpableContainerImpl implements DumpableContainer { ipw.println(); return; } - ipw.println(": "); + ipw.println(":"); for (int i = 0; i < size; i++) { String dumpableName = mDumpables.keyAt(i); diff --git a/core/java/com/android/internal/view/OneShotPreDrawListener.java b/core/java/com/android/internal/view/OneShotPreDrawListener.java index 42d104a9942b..c750c0bbe6aa 100644 --- a/core/java/com/android/internal/view/OneShotPreDrawListener.java +++ b/core/java/com/android/internal/view/OneShotPreDrawListener.java @@ -15,6 +15,7 @@ */ package com.android.internal.view; +import android.annotation.NonNull; import android.view.View; import android.view.ViewTreeObserver; @@ -36,7 +37,8 @@ public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListene private final Runnable mRunnable; private final boolean mReturnValue; - private OneShotPreDrawListener(View view, boolean returnValue, Runnable runnable) { + private OneShotPreDrawListener(@NonNull View view, boolean returnValue, + @NonNull Runnable runnable) { mView = view; mViewTreeObserver = view.getViewTreeObserver(); mRunnable = runnable; @@ -52,7 +54,7 @@ public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListene * @return The added OneShotPreDrawListener. It can be removed prior to * the onPreDraw by calling {@link #removeListener()}. */ - public static OneShotPreDrawListener add(View view, Runnable runnable) { + public static OneShotPreDrawListener add(@NonNull View view, @NonNull Runnable runnable) { return add(view, true, runnable); } @@ -65,7 +67,8 @@ public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListene * @return The added OneShotPreDrawListener. It can be removed prior to * the onPreDraw by calling {@link #removeListener()}. */ - public static OneShotPreDrawListener add(View view, boolean returnValue, Runnable runnable) { + public static OneShotPreDrawListener add(@NonNull View view, boolean returnValue, + @NonNull Runnable runnable) { OneShotPreDrawListener listener = new OneShotPreDrawListener(view, returnValue, runnable); view.getViewTreeObserver().addOnPreDrawListener(listener); view.addOnAttachStateChangeListener(listener); @@ -93,12 +96,12 @@ public class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListene } @Override - public void onViewAttachedToWindow(View v) { + public void onViewAttachedToWindow(@NonNull View v) { mViewTreeObserver = v.getViewTreeObserver(); } @Override - public void onViewDetachedFromWindow(View v) { + public void onViewDetachedFromWindow(@NonNull View v) { removeListener(); } } 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/java/com/android/internal/widget/NestedScrollingChild.java b/core/java/com/android/internal/widget/NestedScrollingChild.java index 20285b5ff44d..c7f58919b131 100644 --- a/core/java/com/android/internal/widget/NestedScrollingChild.java +++ b/core/java/com/android/internal/widget/NestedScrollingChild.java @@ -16,6 +16,7 @@ package com.android.internal.widget; +import android.annotation.Nullable; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -149,7 +150,7 @@ public interface NestedScrollingChild { * @see #dispatchNestedPreScroll(int, int, int[], int[]) */ boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, - int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow); + int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow); /** * Dispatch one step of a nested scroll in progress before this view consumes any portion of it. @@ -170,7 +171,8 @@ public interface NestedScrollingChild { * @return true if the parent consumed some or all of the scroll delta * @see #dispatchNestedScroll(int, int, int, int, int[]) */ - boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow); + boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, + @Nullable int[] offsetInWindow); /** * Dispatch a fling to a nested scrolling parent. diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 39f17e510a1c..93864fa2e05d 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -237,6 +237,10 @@ public class SystemConfig { // be delivered anonymously even to apps which target O+. final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>(); + // These are the packages that are exempted from the background restriction applied + // by the system automatically, i.e., due to high background current drain. + final ArraySet<String> mBgRestrictionExemption = new ArraySet<>(); + // These are the package names of apps which should be automatically granted domain verification // for all of their domains. The only way these apps can be overridden by the user is by // explicitly disabling overall link handling support in app info. @@ -389,6 +393,10 @@ public class SystemConfig { return mAllowIgnoreLocationSettings; } + public ArraySet<String> getBgRestrictionExemption() { + return mBgRestrictionExemption; + } + public ArraySet<String> getLinkedApps() { return mLinkedApps; } @@ -1049,6 +1057,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "bg-restriction-exemption": { + if (allowOverrideAppRestrictions) { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, "<" + name + "> without package in " + + permFile + " at " + parser.getPositionDescription()); + } else { + mBgRestrictionExemption.add(pkgname); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; case "default-enabled-vr-app": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); 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/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a8cf2536341b..1033efa5094f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -104,6 +104,7 @@ static struct { jfieldID hdrCapabilities; jfieldID autoLowLatencyModeSupported; jfieldID gameContentTypeSupported; + jfieldID preferredBootDisplayMode; } gDynamicDisplayInfoClassInfo; static struct { @@ -1301,6 +1302,9 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject to env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported, info.gameContentTypeSupported); + + env->SetIntField(object, gDynamicDisplayInfoClassInfo.preferredBootDisplayMode, + info.preferredBootDisplayMode); return object; } @@ -1638,6 +1642,27 @@ static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObjec } } +static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) { + bool isBootDisplayModeSupported = false; + SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported); + return static_cast<jboolean>(isBootDisplayModeSupported); +} + +static void nativeSetBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject, + jint displayModId) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); + if (token == NULL) return; + + SurfaceComposerClient::setBootDisplayMode(token, displayModId); +} + +static void nativeClearBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); + if (token == NULL) return; + + SurfaceComposerClient::clearBootDisplayMode(token); +} + static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) { sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return; @@ -2046,6 +2071,12 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDisplayNativePrimaries }, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, + {"nativeGetBootDisplayModeSupport", "()Z", + (void*)nativeGetBootDisplayModeSupport }, + {"nativeSetBootDisplayMode", "(Landroid/os/IBinder;I)V", + (void*)nativeSetBootDisplayMode }, + {"nativeClearBootDisplayMode", "(Landroid/os/IBinder;)V", + (void*)nativeClearBootDisplayMode }, {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V", (void*)nativeSetAutoLowLatencyMode }, {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V", @@ -2137,7 +2168,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetLayerId }, {"nativeSetDropInputMode", "(JJI)V", (void*)nativeSetDropInputMode }, - {"nativeAddTransactionCommittedListener", "(JLandroid/view/TransactionCommittedListener;)V", + {"nativeAddTransactionCommittedListener", "(JLandroid/view/SurfaceControl$TransactionCommittedListener;)V", (void*) nativeAddTransactionCommittedListener }, {"nativeSanitize", "(J)V", (void*) nativeSanitize } @@ -2184,6 +2215,8 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z"); gDynamicDisplayInfoClassInfo.gameContentTypeSupported = GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z"); + gDynamicDisplayInfoClassInfo.preferredBootDisplayMode = + GetFieldIDOrDie(env, dynamicInfoClazz, "preferredBootDisplayMode", "I"); jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode"); gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz); @@ -2360,7 +2393,7 @@ int register_android_view_SurfaceControl(JNIEnv* env) "([Landroid/view/SurfaceControl$JankData;)V"); jclass transactionCommittedListenerClazz = - FindClassOrDie(env, "android/view/TransactionCommittedListener"); + FindClassOrDie(env, "android/view/SurfaceControl$TransactionCommittedListener"); gTransactionCommittedListenerClassInfo.clazz = MakeGlobalRefOrDie(env, transactionCommittedListenerClazz); gTransactionCommittedListenerClassInfo.onTransactionCommitted = 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/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index d48ea3b8785c..04f4d7b09d82 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -452,16 +452,16 @@ message LowPowerStandbyControllerDumpProto { optional bool is_interactive = 5; // Time (in elapsedRealtime) when the device was last interactive - optional bool last_interactive_time = 6; + optional int64 last_interactive_time = 6; - // Time (in milliseconds) after becoming non-interactive that Low Power Standby can activate + // Timeout (in milliseconds) after becoming non-interactive that Low Power Standby can activate optional int32 standby_timeout_config = 7; // True if the device has entered idle mode since becoming non-interactive - optional int32 idle_since_non_interactive = 8; + optional bool idle_since_non_interactive = 8; // True if the device is currently in idle mode - optional int32 is_device_idle = 9; + optional bool is_device_idle = 9; // Set of app ids that are exempt form low power standby repeated int32 allowlist = 10; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 85504ceae791..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 --> <!-- ======================================= --> @@ -6540,12 +6601,27 @@ android:exported="false"> </activity> + <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity" + android:theme="@style/Theme.Dialog.Confirmation" + android:excludeFromRecents="true" + android:process=":ui" + android:label="@string/log_access_confirmation_title" + android:exported="false"> + </activity> + <activity android:name="com.android.server.notification.NASLearnMoreActivity" android:theme="@style/Theme.Dialog.Confirmation" android:excludeFromRecents="true" android:exported="false"> </activity> + <activity android:name="android.service.games.GameSessionTrampolineActivity" + android:excludeFromRecents="true" + android:exported="true" + android:permission="android.permission.MANAGE_GAME_ACTIVITY" + android:theme="@style/Theme.Translucent.NoTitleBar"> + </activity> + <receiver android:name="com.android.server.BootReceiver" android:exported="true" android:systemUserOnly="true"> diff --git a/core/res/res/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/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml new file mode 100644 index 000000000000..44ed6f2a0676 --- /dev/null +++ b/core/res/res/layout/miniresolver.xml @@ -0,0 +1,111 @@ +<?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. + --> +<com.android.internal.widget.ResolverDrawerLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:maxWidth="@dimen/resolver_max_width" + android:maxCollapsedHeight="@dimen/resolver_max_collapsed_height" + android:maxCollapsedHeightSmall="56dp" + android:id="@id/contentPanel"> + + <RelativeLayout + android:id="@+id/title_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alwaysShow="true" + android:elevation="@dimen/resolver_elevation" + android:paddingTop="@dimen/resolver_small_margin" + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_edge_margin" + android:paddingBottom="@dimen/resolver_title_padding_bottom" + android:background="@drawable/bottomsheet_background"> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + /> + + <TextView + android:id="@+id/open_cross_profile" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/icon" + android:layout_centerHorizontal="true" + android:textColor="?android:textColorPrimary" + /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/button_bar_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alwaysShow="true" + android:orientation="vertical" + android:background="?attr/colorBackground" + android:layout_ignoreOffset="true"> + <View + android:id="@+id/resolver_button_bar_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackground" + android:foreground="?attr/dividerVertical" /> + <RelativeLayout + style="?attr/buttonBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_ignoreOffset="true" + android:layout_hasNestedScrollIndicator="true" + android:gravity="end|center_vertical" + android:orientation="horizontal" + android:layoutDirection="locale" + android:measureWithLargestChild="true" + android:paddingTop="@dimen/resolver_button_bar_spacing" + android:paddingBottom="@dimen/resolver_button_bar_spacing" + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_small_margin" + android:elevation="@dimen/resolver_elevation"> + + <Button + android:id="@+id/use_same_profile_browser" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:maxLines="2" + style="@android:style/Widget.DeviceDefault.Button.Borderless" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textAllCaps="false" + android:text="@string/activity_resolver_use_once" + /> + + <Button + android:id="@+id/button_open" + android:layout_width="wrap_content" + android:layout_alignParentEnd="true" + android:maxLines="2" + style="@android:style/Widget.DeviceDefault.Button.Colored" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textAllCaps="false" + android:layout_height="wrap_content" + android:text="@string/whichViewApplicationLabel" + /> + </RelativeLayout> + </LinearLayout> +</com.android.internal.widget.ResolverDrawerLayout> 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/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 2e4b783a6dca..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] --> @@ -5689,6 +5705,20 @@ <!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] --> <string name="harmful_app_warning_title">Harmful app detected</string> + <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] --> + <string name="log_access_confirmation_title">System log access request</string> + <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] --> + <string name="log_access_confirmation_allow">Only this time</string> + <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] --> + <string name="log_access_confirmation_deny">Don\u2019t allow</string> + + <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> + <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging. + These logs might contain information that apps and services on your device have written.</string> + + <!-- Privacy notice do not show [CHAR LIMIT=20] --> + <string name="log_access_do_not_show_again">Don\u2019t show again</string> + <!-- Text describing a permission request for one app to show another app's slices [CHAR LIMIT=NONE] --> <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string> @@ -5936,9 +5966,9 @@ <string name="resolver_no_personal_apps_available">No personal apps</string> <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] --> - <string name="miniresolver_open_in_personal">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in personal profile?</string> + <string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string> <!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] --> - <string name="miniresolver_open_in_work">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in work profile?</string> + <string name="miniresolver_open_in_work">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your work profile?</string> <!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] --> <string name="miniresolver_use_personal_browser">Use personal browser</string> <!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c232a8a474bd..2b25c3eb099b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1599,6 +1599,13 @@ <java-symbol type="layout" name="resolver_list_per_profile" /> <java-symbol type="layout" name="chooser_list_per_profile" /> <java-symbol type="layout" name="resolver_empty_states" /> + <java-symbol type="id" name="open_cross_profile" /> + <java-symbol type="string" name="miniresolver_open_in_personal" /> + <java-symbol type="string" name="miniresolver_open_in_work" /> + <java-symbol type="string" name="miniresolver_use_personal_browser" /> + <java-symbol type="string" name="miniresolver_use_work_browser" /> + <java-symbol type="id" name="button_open" /> + <java-symbol type="id" name="use_same_profile_browser" /> <java-symbol type="anim" name="slide_in_child_bottom" /> <java-symbol type="anim" name="slide_in_right" /> @@ -2715,6 +2722,7 @@ <java-symbol type="bool" name="config_allow_ussd_over_ims" /> <java-symbol type="attr" name="touchscreenBlocksFocus" /> <java-symbol type="layout" name="resolver_list_with_default" /> + <java-symbol type="layout" name="miniresolver" /> <java-symbol type="string" name="activity_resolver_use_always" /> <java-symbol type="string" name="whichApplicationNamed" /> <java-symbol type="string" name="whichApplicationLabel" /> @@ -3277,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" /> @@ -3857,6 +3862,11 @@ <java-symbol type="string" name="harmful_app_warning_title" /> <java-symbol type="layout" name="harmful_app_warning_dialog" /> + <java-symbol type="string" name="log_access_confirmation_allow" /> + <java-symbol type="string" name="log_access_confirmation_deny" /> + <java-symbol type="string" name="log_access_confirmation_title" /> + <java-symbol type="string" name="log_access_confirmation_body" /> + <java-symbol type="string" name="config_defaultAssistantAccessComponent" /> <java-symbol type="string" name="slices_permission_request" /> @@ -4712,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/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml index bd987a03b51f..6639c0241ae2 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.BATTERY_STATS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/> <application android:theme="@style/Theme" diff --git a/core/tests/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/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 75d20252ae27..2817728fd9ea 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -731,6 +731,25 @@ public class ResolverActivityTest { } @Test + public void testMiniResolver() { + ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTest(1); + List<ResolvedComponentInfo> workResolvedComponentInfos = + createResolvedComponentsForTest(1); + // Personal profile only has a browser + personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true; + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); + Intent sendIntent = createSendImageIntent(); + sendIntent.setType("TestType"); + + mActivityRule.launchActivity(sendIntent); + waitForIdle(); + onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed())); + } + + @Test public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; 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/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index dbb2cf15dd8e..88349b38040c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -383,10 +383,10 @@ public class MeasuredEnergyStatsTest { assertEquals(13, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0)); // 6 * (6000-4000)/(6000-2000) assertEquals(3, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1)); - - // POWER_BUCKET_SCREEN_OTHER was only present along with state=1 - assertEquals(0, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0)); - assertEquals(40, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1)); + // 40 * (4000-1000)/(5000-1000) + assertEquals(30, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0)); + // 40 * (5000-4000)/(5000-1000) + assertEquals(10, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1)); } @Test diff --git a/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 d0026016ecf4..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"/> @@ -474,6 +476,7 @@ applications that come with the platform <!-- Permission needed for CTS test - WifiManagerTest --> <permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <permission name="android.permission.NEARBY_WIFI_DEVICES" /> <permission name="android.permission.OVERRIDE_WIFI_CONFIG" /> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 15672332a522..f2a875c76f1c 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -727,12 +727,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1343787701": { - "message": "startBackNavigation task=%s, topRunningActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-1340540100": { "message": "Creating SnapshotStartingData", "level": "VERBOSE", @@ -1765,6 +1759,12 @@ "group": "WM_DEBUG_SYNC_ENGINE", "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" }, + "-228813488": { + "message": "%s: Setting back callback %s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "-208825711": { "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s", "level": "DEBUG", @@ -3691,6 +3691,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "1898905572": { + "message": "startBackNavigation task=%s, topRunningActivity=%s, topWindow=%s backCallback=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1903353011": { "message": "notifyAppStopped: %s", "level": "VERBOSE", diff --git a/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/res/layout/letterbox_education_toast_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml deleted file mode 100644 index a309d4829f0d..000000000000 --- a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml +++ /dev/null @@ -1,61 +0,0 @@ -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<com.android.wm.shell.compatui.LetterboxEduToastLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@color/compat_controls_background" - android:gravity="center" - android:paddingVertical="14dp" - android:paddingHorizontal="16dp"> - - <!-- Adding an extra layer to animate the alpha of the background and content separately. --> - <LinearLayout - android:id="@+id/letterbox_education_content" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/letterbox_education_icon" - android:layout_width="@dimen/letterbox_education_toast_icon_size" - android:layout_height="@dimen/letterbox_education_toast_icon_size"/> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxWidth="@dimen/letterbox_education_toast_text_max_width" - android:paddingHorizontal="16dp" - android:lineSpacingExtra="5sp" - android:text="@string/letterbox_education_toast_title" - android:textAlignment="viewStart" - android:textColor="@color/compat_controls_text" - android:textSize="16sp" - android:maxLines="1" - android:ellipsize="end"/> - - <ImageButton - android:id="@+id/letterbox_education_toast_expand" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/letterbox_education_ic_expand_more_ripple" - android:background="@android:color/transparent" - android:contentDescription="@string/letterbox_education_expand_button_description"/> - - </LinearLayout> - -</com.android.wm.shell.compatui.LetterboxEduToastLayout> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index ab2c9b1466b8..40c7647ecedf 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -219,18 +219,9 @@ <!-- The width of the camera compat hint. --> <dimen name="camera_compat_hint_width">143dp</dimen> - <!-- The corner radius of the letterbox education toast. --> - <dimen name="letterbox_education_toast_corner_radius">100dp</dimen> - <!-- The corner radius of the letterbox education dialog. --> <dimen name="letterbox_education_dialog_corner_radius">28dp</dimen> - <!-- The margin between the letterbox education toast/dialog and the bottom of the task. --> - <dimen name="letterbox_education_margin_bottom">16dp</dimen> - - <!-- The size of the icon in the letterbox education toast. --> - <dimen name="letterbox_education_toast_icon_size">24dp</dimen> - <!-- The size of an icon in the letterbox education dialog. --> <dimen name="letterbox_education_dialog_icon_size">48dp</dimen> @@ -243,9 +234,6 @@ <!-- The maximum width of the title and subtitle in the letterbox education dialog. --> <dimen name="letterbox_education_dialog_title_max_width">444dp</dimen> - <!-- The maximum width of the text in the letterbox education toast. --> - <dimen name="letterbox_education_toast_text_max_width">398dp</dimen> - <!-- The distance that the letterbox education dialog will move up during appear/dismiss animation. --> <dimen name="letterbox_education_dialog_animation_elevation">20dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index a8a9ed74cd35..16a4b524803a 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -174,9 +174,6 @@ <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] --> <string name="letterbox_education_dialog_title">Get the most out of <xliff:g id="app_name" example="YouTube">%s</xliff:g></string> - <!-- The title of the letterbox education toast. [CHAR LIMIT=60] --> - <string name="letterbox_education_toast_title">Rotate your device for a full-screen view</string> - <!-- Description of the rotate screen into portrait action. [CHAR LIMIT=NONE] --> <string name="letterbox_education_screen_rotation_portrait_text">Rotate your screen to portrait</string> @@ -192,7 +189,4 @@ <!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] --> <string name="letterbox_education_got_it">Got it</string> - <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] --> - <string name="letterbox_education_expand_button_description">Expand for more information.</string> - </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/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/compatui/letterboxedu/LetterboxEduToastLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java deleted file mode 100644 index e7f592dcb14d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.compatui.letterboxedu; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.wm.shell.R; - -/** - * Container for the Letterbox Education Toast. - */ -// TODO(b/215316431): Add tests -public class LetterboxEduToastLayout extends FrameLayout { - - public LetterboxEduToastLayout(Context context) { - this(context, null); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - /** - * Register a callback for the dismiss button. - * @param callback The callback to register - */ - void setExpandOnClickListener(Runnable callback) { - findViewById(R.id.letterbox_education_toast_expand).setOnClickListener( - view -> callback.run()); - } - - /** - * Updates the layout with the given app info. - * @param appName The name of the app - * @param appIcon The icon of the app - */ - void updateAppInfo(String appName, Drawable appIcon) { - ImageView icon = findViewById(R.id.letterbox_education_icon); - icon.setContentDescription(appName); - icon.setImageDrawable(appIcon); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/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/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 3cfa541c1c86..d022ec15f232 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -89,9 +89,17 @@ interface ISplitScreen { /** * Version of startTasks using legacy transition system. */ - oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, - int sideTaskId, in Bundle sideOptions, int sidePosition, - float splitRatio, in RemoteAnimationAdapter adapter) = 11; + oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, + int sideTaskId, in Bundle sideOptions, int sidePosition, + float splitRatio, in RemoteAnimationAdapter adapter) = 11; + + /** + * Start a pair of intent and task using legacy transition system. + */ + oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent, + in Intent fillInIntent, int taskId, boolean intentFirst, in Bundle mainOptions, + in Bundle sideOptions, int sidePosition, float splitRatio, + in RemoteAnimationAdapter adapter) = 12; /** * Blocking call that notifies and gets additional split-screen targets when entering @@ -100,5 +108,7 @@ interface ISplitScreen { * @param appTargets apps that will be re-parented to display area */ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - in RemoteAnimationTarget[] appTargets) = 12; + in RemoteAnimationTarget[] appTargets) = 13; + + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 3e6dc8241f4f..990b53a601f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -641,6 +641,18 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override + public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, + Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions, + Bundle sideOptions, int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter) { + executeRemoteCallWithTaskPermission(mController, + "startIntentAndTaskWithLegacyTransition", (controller) -> + controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition( + pendingIntent, fillInIntent, taskId, intentFirst, mainOptions, + sideOptions, sidePosition, splitRatio, adapter)); + } + + @Override public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index a2c2f591cde0..219530b46da4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -57,8 +57,10 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; +import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; @@ -467,6 +469,116 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } + /** Start an intent and a task ordered by {@code intentFirst}. */ + void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent, + int taskId, boolean intentFirst, @Nullable Bundle mainOptions, + @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio, + RemoteAnimationAdapter adapter) { + // TODO: try pulling the first chunk of this method into a method so that it can be shared + // with startTasksWithLegacyTransition. So far attempts to do so result in failure in split. + + // Init divider first to make divider leash for remote animation target. + mSplitLayout.init(); + // Set false to avoid record new bounds with old task still on top; + mShouldUpdateRecents = false; + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final WindowContainerTransaction evictWct = new WindowContainerTransaction(); + prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct); + prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct); + // Need to add another wrapper here in shell so that we can inject the divider bar + // and also manage the process elevation via setRunningRemote + IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { + @Override + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + final IRemoteAnimationFinishedCallback finishedCallback) { + RemoteAnimationTarget[] augmentedNonApps = + new RemoteAnimationTarget[nonApps.length + 1]; + for (int i = 0; i < nonApps.length; ++i) { + augmentedNonApps[i] = nonApps[i]; + } + augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget(); + + IRemoteAnimationFinishedCallback wrapCallback = + new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() throws RemoteException { + mShouldUpdateRecents = true; + mSyncQueue.queue(evictWct); + mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + finishedCallback.onAnimationFinished(); + } + }; + try { + try { + ActivityTaskManager.getService().setRunningRemoteTransitionDelegate( + adapter.getCallingApplication()); + } catch (SecurityException e) { + Slog.e(TAG, "Unable to boost animation thread. This should only happen" + + " during unit tests"); + } + adapter.getRunner().onAnimationStart(transit, apps, wallpapers, + augmentedNonApps, wrapCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Error starting remote animation", e); + } + } + + @Override + public void onAnimationCancelled() { + mShouldUpdateRecents = true; + mSyncQueue.queue(evictWct); + mSyncQueue.runInSync(t -> setDividerVisibility(true, t)); + try { + adapter.getRunner().onAnimationCancelled(); + } catch (RemoteException e) { + Slog.e(TAG, "Error starting remote animation", e); + } + } + }; + RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter( + wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay()); + + if (mainOptions == null) { + mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle(); + } else { + ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions); + mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter)); + mainOptions = mainActivityOptions.toBundle(); + } + + sideOptions = sideOptions != null ? sideOptions : new Bundle(); + setSideStagePosition(sidePosition, wct); + + mSplitLayout.setDivideRatio(splitRatio); + if (mMainStage.isActive()) { + mMainStage.moveToTop(getMainStageBounds(), wct); + } else { + // Build a request WCT that will launch both apps such that task 0 is on the main stage + // while task 1 is on the side stage. + mMainStage.activate(getMainStageBounds(), wct, false /* reparent */); + } + mSideStage.moveToTop(getSideStageBounds(), wct); + + // Make sure the launch options will put tasks in the corresponding split roots + addActivityOptions(mainOptions, mMainStage); + addActivityOptions(sideOptions, mSideStage); + + // Add task launch requests + if (intentFirst) { + wct.sendPendingIntent(pendingIntent, fillInIntent, mainOptions); + wct.startTask(taskId, sideOptions); + } else { + wct.startTask(taskId, mainOptions); + wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions); + } + + // Using legacy transitions, so we can't use blast sync since it conflicts. + mTaskOrganizer.applyTransaction(wct); + } + /** * Collects all the current child tasks of a specific split and prepares transaction to evict * them to display. diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index 574a9f4da627..556742e2ac5f 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -26,8 +26,16 @@ <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6000s" /> <option name="hidden-api-checks" value="false" /> + <option name="device-listeners" + value="com.android.server.wm.flicker.TraceFileReadyListener" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="(\w)+\.winscope" /> + <option name="pull-pattern-keys" value="(\w)+\.mp4" /> + <option name="collect-on-run-ended-only" value="false" /> + <option name="clean-up" value="true" /> + </metrics_collector> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt index 6524182e9082..906123914731 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt @@ -93,7 +93,7 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 20) + repetitions = 5) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 960c7ac4099a..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 15a398de9021..cdc31631637e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5874,7 +5874,7 @@ public class AudioManager { return false; } - /** + /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) * @param state new connection state: 1 connected, 0 disconnected @@ -5883,10 +5883,29 @@ public class AudioManager { */ @UnsupportedAppUsage @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public void setWiredDeviceConnectionState(int type, int state, String address, String name) { + public void setWiredDeviceConnectionState(int device, int state, String address, + String name) { + final IAudioService service = getService(); + int role = isOutputDevice(device) + ? AudioDeviceAttributes.ROLE_OUTPUT : AudioDeviceAttributes.ROLE_INPUT; + AudioDeviceAttributes attributes = new AudioDeviceAttributes( + role, AudioDeviceInfo.convertInternalDeviceToDeviceType(device), address, + name, new ArrayList<>()/*mAudioProfiles*/, new ArrayList<>()/*mAudioDescriptors*/); + setWiredDeviceConnectionState(attributes, state); + } + + /** + * Indicate wired accessory connection state change and attributes. + * @param state new connection state: 1 connected, 0 disconnected + * @param attributes attributes of the connected device + * {@hide} + */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) { final IAudioService service = getService(); try { - service.setWiredDeviceConnectionState(type, state, address, name, + service.setWiredDeviceConnectionState(attributes, state, mApplicationContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -5919,13 +5938,14 @@ public class AudioManager { * @param newDevice Bluetooth device connected or null if there is no new devices * @param previousDevice Bluetooth device disconnected or null if there is no disconnected * devices - * @param info contain all info related to the device. {@link BtProfileConnectionInfo} + * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo} * {@hide} */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice, - @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + @Nullable BluetoothDevice previousDevice, + @NonNull BluetoothProfileConnectionInfo info) { final IAudioService service = getService(); try { service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info); diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java index ae8d0a5ad4ab..5c5f837dd07a 100644 --- a/media/java/android/media/AudioProfile.java +++ b/media/java/android/media/AudioProfile.java @@ -18,10 +18,14 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -33,7 +37,7 @@ import java.util.stream.Collectors; * be reported in different audio profiles. The application can choose any of the encapsulation * types. */ -public class AudioProfile { +public class AudioProfile implements Parcelable { /** * No encapsulation type is specified. */ @@ -57,9 +61,19 @@ public class AudioProfile { private final int[] mChannelIndexMasks; private final int mEncapsulationType; - AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, - @NonNull int[] channelIndexMasks, - int encapsulationType) { + /** + * @hide + * Constructor from format, sampling rates, channel masks, channel index masks and + * encapsulation type. + * @param format the audio format + * @param samplingRates the supported sampling rates + * @param channelMasks the supported channel masks + * @param channelIndexMasks the supported channel index masks + * @param encapsulationType the encapsulation type of the encoding format + */ + @SystemApi + public AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, + @NonNull int[] channelIndexMasks, int encapsulationType) { mFormat = format; mSamplingRates = samplingRates; mChannelMasks = channelMasks; @@ -114,6 +128,26 @@ public class AudioProfile { } @Override + public int hashCode() { + return Objects.hash(mFormat, Arrays.hashCode(mSamplingRates), + Arrays.hashCode(mChannelMasks), Arrays.hashCode(mChannelIndexMasks), + mEncapsulationType); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioProfile that = (AudioProfile) o; + return ((mFormat == that.mFormat) + && (hasIdenticalElements(mSamplingRates, that.mSamplingRates)) + && (hasIdenticalElements(mChannelMasks, that.mChannelMasks)) + && (hasIdenticalElements(mChannelIndexMasks, that.mChannelIndexMasks)) + && (mEncapsulationType == that.mEncapsulationType)); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder("{"); sb.append(AudioFormat.toLogFriendlyEncoding(mFormat)); @@ -126,6 +160,7 @@ public class AudioProfile { if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) { sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks)); } + sb.append(", encapsulation type=" + mEncapsulationType); sb.append("}"); return sb.toString(); } @@ -137,4 +172,50 @@ public class AudioProfile { return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X", anInt)) .collect(Collectors.joining(", ")); } + + private static boolean hasIdenticalElements(int[] array1, int[] array2) { + int[] sortedArray1 = Arrays.copyOf(array1, array1.length); + Arrays.sort(sortedArray1); + int[] sortedArray2 = Arrays.copyOf(array2, array2.length); + Arrays.sort(sortedArray2); + return Arrays.equals(sortedArray1, sortedArray2); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mFormat); + dest.writeIntArray(mSamplingRates); + dest.writeIntArray(mChannelMasks); + dest.writeIntArray(mChannelIndexMasks); + dest.writeInt(mEncapsulationType); + } + + private AudioProfile(@NonNull Parcel in) { + mFormat = in.readInt(); + mSamplingRates = in.createIntArray(); + mChannelMasks = in.createIntArray(); + mChannelIndexMasks = in.createIntArray(); + mEncapsulationType = in.readInt(); + } + + public static final @NonNull Parcelable.Creator<AudioProfile> CREATOR = + new Parcelable.Creator<AudioProfile>() { + /** + * Rebuilds an AudioProfile previously stored with writeToParcel(). + * @param p Parcel object to read the AudioProfile from + * @return a new AudioProfile created from the data in the parcel + */ + public AudioProfile createFromParcel(Parcel p) { + return new AudioProfile(p); + } + + public AudioProfile[] newArray(int size) { + return new AudioProfile[size]; + } + }; } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 1b46a50d7886..536b4ad71285 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -26,10 +26,12 @@ import android.bluetooth.BluetoothLeAudioCodecConfig; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; +import android.media.audio.common.AidlConversion; import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.os.Build; import android.os.IBinder; +import android.os.Parcel; import android.os.Vibrator; import android.telephony.TelephonyManager; import android.util.Log; @@ -1555,9 +1557,24 @@ public class AudioSystem * {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED} */ @UnsupportedAppUsage - public static native int setDeviceConnectionState(int device, int state, - String device_address, String device_name, - int codecFormat); + public static int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + android.media.audio.common.AudioPort port = + AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + Parcel parcel = Parcel.obtain(); + port.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + try { + return setDeviceConnectionState(state, parcel, codecFormat); + } finally { + parcel.recycle(); + } + } + /** + * @hide + */ + @UnsupportedAppUsage + public static native int setDeviceConnectionState(int state, Parcel parcel, int codecFormat); /** @hide */ @UnsupportedAppUsage public static native int getDeviceConnectionState(int device, String device_address); diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BluetoothProfileConnectionInfo.aidl index 047f06be0964..0617084fd826 100644 --- a/media/java/android/media/BtProfileConnectionInfo.aidl +++ b/media/java/android/media/BluetoothProfileConnectionInfo.aidl @@ -16,5 +16,5 @@ package android.media; -parcelable BtProfileConnectionInfo; +parcelable BluetoothProfileConnectionInfo; diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BluetoothProfileConnectionInfo.java index 88b9777e911d..c14884657ddd 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BluetoothProfileConnectionInfo.java @@ -26,15 +26,14 @@ import android.os.Parcelable; * {@hide} */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) -public final class BtProfileConnectionInfo implements Parcelable { - +public final class BluetoothProfileConnectionInfo implements Parcelable { private final int mProfile; private final boolean mSupprNoisy; private final int mVolume; private final boolean mIsLeOutput; - private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume, - boolean isLeOutput) { + private BluetoothProfileConnectionInfo(int profile, boolean suppressNoisyIntent, + int volume, boolean isLeOutput) { mProfile = profile; mSupprNoisy = suppressNoisyIntent; mVolume = volume; @@ -45,21 +44,21 @@ public final class BtProfileConnectionInfo implements Parcelable { * Constructor used by BtHelper when a profile is connected * {@hide} */ - public BtProfileConnectionInfo(int profile) { + public BluetoothProfileConnectionInfo(int profile) { this(profile, false, -1, false); } - public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR = - new Parcelable.Creator<BtProfileConnectionInfo>() { + public static final @NonNull Parcelable.Creator<BluetoothProfileConnectionInfo> CREATOR = + new Parcelable.Creator<BluetoothProfileConnectionInfo>() { @Override - public BtProfileConnectionInfo createFromParcel(Parcel source) { - return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(), - source.readInt(), source.readBoolean()); + public BluetoothProfileConnectionInfo createFromParcel(Parcel source) { + return new BluetoothProfileConnectionInfo(source.readInt(), + source.readBoolean(), source.readInt(), source.readBoolean()); } @Override - public BtProfileConnectionInfo[] newArray(int size) { - return new BtProfileConnectionInfo[size]; + public BluetoothProfileConnectionInfo[] newArray(int size) { + return new BluetoothProfileConnectionInfo[size]; } }; @@ -84,10 +83,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param volume of device -1 to ignore value */ - public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent, - int volume) { - return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume, - false); + public static @NonNull BluetoothProfileConnectionInfo createA2dpInfo( + boolean suppressNoisyIntent, int volume) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, + volume, false); } /** @@ -96,8 +95,8 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param volume of device -1 to ignore value */ - public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) { - return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false); + public static @NonNull BluetoothProfileConnectionInfo createA2dpSinkInfo(int volume) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false); } /** @@ -106,9 +105,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} * intent will not be sent. */ - public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) { - return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1, - false); + public static @NonNull BluetoothProfileConnectionInfo createHearingAidInfo( + boolean suppressNoisyIntent) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, + -1, false); } /** @@ -119,10 +119,10 @@ public final class BtProfileConnectionInfo implements Parcelable { * * @param isLeOutput if true mean the device is an output device, if false it's an input device */ - public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent, - boolean isLeOutput) { - return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1, - isLeOutput); + public static @NonNull BluetoothProfileConnectionInfo createLeAudioInfo( + boolean suppressNoisyIntent, boolean isLeOutput) { + return new BluetoothProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, + -1, isLeOutput); } /** @@ -136,7 +136,7 @@ public final class BtProfileConnectionInfo implements Parcelable { * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be * sent */ - public boolean getSuppressNoisyIntent() { + public boolean isSuppressNoisyIntent() { return mSupprNoisy; } @@ -153,7 +153,7 @@ public final class BtProfileConnectionInfo implements Parcelable { * @return {@code true} is the LE device is an output device, {@code false} if it's an input * device */ - public boolean getIsLeOutput() { + public boolean isLeOutput() { return mIsLeOutput; } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 96199a988704..fec14def618c 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -25,7 +25,7 @@ import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -215,8 +215,7 @@ interface IAudioService { IRingtonePlayer getRingtonePlayer(); int getUiSoundsStreamType(); - void setWiredDeviceConnectionState(int type, int state, String address, String name, - String caller); + void setWiredDeviceConnectionState(in AudioDeviceAttributes aa, int state, String caller); @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); @@ -276,7 +275,7 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice, - in BluetoothDevice previousDevice, in BtProfileConnectionInfo info); + in BluetoothDevice previousDevice, in BluetoothProfileConnectionInfo info); oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult, in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/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/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java index 0f5bf082d44f..14a9144c9cdc 100644 --- a/media/java/android/media/tv/tuner/DemuxCapabilities.java +++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java @@ -36,13 +36,8 @@ import java.lang.annotation.RetentionPolicy; public class DemuxCapabilities { /** @hide */ - @IntDef(flag = true, value = { - Filter.TYPE_TS, - Filter.TYPE_MMTP, - Filter.TYPE_IP, - Filter.TYPE_TLV, - Filter.TYPE_ALP - }) + @IntDef(value = {Filter.TYPE_TS, Filter.TYPE_MMTP, Filter.TYPE_IP, Filter.TYPE_TLV, + Filter.TYPE_ALP}) @Retention(RetentionPolicy.SOURCE) public @interface FilterCapabilities {} diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 9f4423644877..a3e731b0ffa1 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -153,8 +153,8 @@ public class Filter implements AutoCloseable { /** @hide */ - @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_DATA_READY, STATUS_LOW_WATER, - STATUS_HIGH_WATER, STATUS_OVERFLOW}) + @IntDef(prefix = "STATUS_", + value = {STATUS_DATA_READY, STATUS_LOW_WATER, STATUS_HIGH_WATER, STATUS_OVERFLOW}) @Retention(RetentionPolicy.SOURCE) public @interface Status {} @@ -185,8 +185,7 @@ public class Filter implements AutoCloseable { public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW; /** @hide */ - @IntDef(flag = true, - prefix = "SCRAMBLING_STATUS_", + @IntDef(prefix = "SCRAMBLING_STATUS_", value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED, SCRAMBLING_STATUS_SCRAMBLED}) @Retention(RetentionPolicy.SOURCE) @@ -209,8 +208,7 @@ public class Filter implements AutoCloseable { android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED; /** @hide */ - @IntDef(flag = true, - prefix = "MONITOR_EVENT_", + @IntDef(prefix = "MONITOR_EVENT_", value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) @Retention(RetentionPolicy.SOURCE) public @interface MonitorEventMask {} @@ -625,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; @@ -655,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/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java index d34581da29cb..b16d9fb247b7 100644 --- a/media/java/android/media/tv/tuner/filter/RecordSettings.java +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -40,8 +40,7 @@ public class RecordSettings extends Settings { * * @hide */ - @IntDef(flag = true, - value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR, + @IntDef(value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR, TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED, TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR, TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR, @@ -165,7 +164,6 @@ public class RecordSettings extends Settings { * @hide */ @IntDef(prefix = "SC_INDEX_", - flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME, SC_INDEX_SEQUENCE, SC_INDEX_I_SLICE, SC_INDEX_P_SLICE, SC_INDEX_B_SLICE, SC_INDEX_SI_SLICE, SC_INDEX_SP_SLICE}) @@ -214,8 +212,7 @@ public class RecordSettings extends Settings { * * @hide */ - @IntDef(flag = true, - value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, + @IntDef(value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP, SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP, SC_HEVC_INDEX_SLICE_TRAIL_CRA}) @@ -258,8 +255,7 @@ public class RecordSettings extends Settings { /** * @hide */ - @IntDef(flag = true, - prefix = "SC_", + @IntDef(prefix = "SC_", value = { SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, diff --git a/media/java/android/media/tv/tuner/filter/SharedFilter.java b/media/java/android/media/tv/tuner/filter/SharedFilter.java index 740ab9c45c83..21964ee5a32a 100644 --- a/media/java/android/media/tv/tuner/filter/SharedFilter.java +++ b/media/java/android/media/tv/tuner/filter/SharedFilter.java @@ -38,7 +38,7 @@ import java.util.concurrent.Executor; @SystemApi public final class SharedFilter implements AutoCloseable { /** @hide */ - @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_INACCESSIBLE}) + @IntDef(prefix = "STATUS_", value = {STATUS_INACCESSIBLE}) @Retention(RetentionPolicy.SOURCE) public @interface Status {} diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index e0405ef57f2e..6c1134ae8fac 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -36,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class AnalogFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "SIGNAL_TYPE_", + @IntDef(prefix = "SIGNAL_TYPE_", value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_AUTO, SIGNAL_TYPE_PAL, SIGNAL_TYPE_PAL_M, SIGNAL_TYPE_PAL_N, SIGNAL_TYPE_PAL_60, SIGNAL_TYPE_NTSC, SIGNAL_TYPE_NTSC_443, SIGNAL_TYPE_SECAM}) @@ -82,8 +81,7 @@ public class AnalogFrontendSettings extends FrontendSettings { public static final int SIGNAL_TYPE_SECAM = FrontendAnalogType.SECAM; /** @hide */ - @IntDef(flag = true, - prefix = "SIF_", + @IntDef(prefix = "SIF_", value = {SIF_UNDEFINED, SIF_AUTO, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK, SIF_DK1_A2, SIF_DK2_A2, SIF_DK3_A2, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2, SIF_M_EIAJ, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME}) diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java index a7157e20c5e6..c99f911c4236 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java @@ -39,8 +39,7 @@ import java.lang.annotation.RetentionPolicy; public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_BANDWIDTH_6MHZ, BANDWIDTH_BANDWIDTH_7MHZ, BANDWIDTH_BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -69,8 +68,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM, MODULATION_MOD_256QAM, @@ -113,8 +111,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_CTI, TIME_INTERLEAVE_MODE_HTI}) @Retention(RetentionPolicy.SOURCE) @@ -140,8 +137,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_15, CODERATE_3_15, CODERATE_4_15, CODERATE_5_15, CODERATE_6_15, CODERATE_7_15, CODERATE_8_15, CODERATE_9_15, CODERATE_10_15, CODERATE_11_15, CODERATE_12_15, CODERATE_13_15}) @@ -207,8 +203,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "FEC_", + @IntDef(prefix = "FEC_", value = {FEC_UNDEFINED, FEC_AUTO, FEC_BCH_LDPC_16K, FEC_BCH_LDPC_64K, FEC_CRC_LDPC_16K, FEC_CRC_LDPC_64K, FEC_LDPC_16K, FEC_LDPC_64K}) @Retention(RetentionPolicy.SOURCE) @@ -249,8 +244,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "DEMOD_OUTPUT_FORMAT_", + @IntDef(prefix = "DEMOD_OUTPUT_FORMAT_", value = {DEMOD_OUTPUT_FORMAT_UNDEFINED, DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET, DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java index 3071ce861e0d..64c6ce629740 100644 --- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java @@ -34,8 +34,7 @@ import java.lang.annotation.RetentionPolicy; public class AtscFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_8VSB, MODULATION_MOD_16VSB}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java index 6b5d6ca9eb84..07c1fbffd91e 100644 --- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java @@ -43,8 +43,7 @@ import java.lang.annotation.RetentionPolicy; public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) public @interface Bandwidth {} @@ -68,8 +67,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720}) @Retention(RetentionPolicy.SOURCE) @@ -97,8 +95,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "GUARD_INTERVAL_", + @IntDef(prefix = "GUARD_INTERVAL_", value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST, GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST, @@ -143,8 +140,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO, MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR, MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM, @@ -187,8 +183,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { FrontendDtmbModulation.CONSTELLATION_64QAM; /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5}) @Retention(RetentionPolicy.SOURCE) public @interface CodeRate {} @@ -215,8 +210,7 @@ public final class DtmbFrontendSettings extends FrontendSettings { public static final int CODERATE_4_5 = FrontendDtmbCodeRate.CODERATE_4_5; /** @hide */ - @IntDef(flag = true, - prefix = "TRANSMISSION_MODE_", + @IntDef(prefix = "TRANSMISSION_MODE_", value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index afe953de5389..45bfc09b8ae7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -40,8 +40,7 @@ import java.lang.annotation.RetentionPolicy; public class DvbcFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_16QAM, MODULATION_MOD_32QAM, MODULATION_MOD_64QAM, MODULATION_MOD_128QAM, MODULATION_MOD_256QAM}) @@ -98,8 +97,7 @@ public class DvbcFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "ANNEX_", + @IntDef(prefix = "ANNEX_", value = {ANNEX_UNDEFINED, ANNEX_A, ANNEX_B, ANNEX_C}) @Retention(RetentionPolicy.SOURCE) public @interface Annex {} @@ -159,8 +157,7 @@ public class DvbcFrontendSettings extends FrontendSettings { android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED; /** @hide */ - @IntDef(flag = true, - prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1, TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4, @@ -226,8 +223,7 @@ public class DvbcFrontendSettings extends FrontendSettings { FrontendCableTimeInterleaveMode.INTERLEAVING_128_4; /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ, BANDWIDTH_8MHZ}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index e16f19285100..56dbb480880b 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -42,8 +42,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "SCAN_TYPE_", + @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC, SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS}) @Retention(RetentionPolicy.SOURCE) @@ -75,8 +74,7 @@ public class DvbsFrontendSettings extends FrontendSettings { public static final int SCAN_TYPE_JESS = FrontendDvbsScanType.JESS; /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK, MODULATION_MOD_32PSK, MODULATION_MOD_ACM, MODULATION_MOD_8APSK, @@ -207,8 +205,7 @@ public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "STANDARD_", + @IntDef(prefix = "STANDARD_", value = {STANDARD_AUTO, STANDARD_S, STANDARD_S2, STANDARD_S2X}) @Retention(RetentionPolicy.SOURCE) public @interface Standard {} diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index d86e9a8af5f6..06547e1fc0e7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -42,8 +42,7 @@ import java.lang.annotation.RetentionPolicy; public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "TRANSMISSION_MODE_", + @IntDef(prefix = "TRANSMISSION_MODE_", value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_1K, TRANSMISSION_MODE_16K, TRANSMISSION_MODE_32K}) @@ -98,8 +97,7 @@ public class DvbtFrontendSettings extends FrontendSettings { FrontendDvbtTransmissionMode.MODE_32K_E; /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ, BANDWIDTH_6MHZ, BANDWIDTH_5MHZ, BANDWIDTH_1_7MHZ, BANDWIDTH_10MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -140,8 +138,7 @@ public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CONSTELLATION_", + @IntDef(prefix = "CONSTELLATION_", value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK, CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM, CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R, @@ -192,8 +189,7 @@ public class DvbtFrontendSettings extends FrontendSettings { FrontendDvbtConstellation.CONSTELLATION_256QAM_R; /** @hide */ - @IntDef(flag = true, - prefix = "HIERARCHY_", + @IntDef(prefix = "HIERARCHY_", value = {HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE, HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH, HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH}) @@ -243,8 +239,7 @@ public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, CODERATE_5_6, CODERATE_7_8, CODERATE_3_5, CODERATE_4_5, CODERATE_6_7, CODERATE_8_9}) @Retention(RetentionPolicy.SOURCE) @@ -296,8 +291,7 @@ public class DvbtFrontendSettings extends FrontendSettings { public static final int CODERATE_8_9 = FrontendDvbtCoderate.CODERATE_8_9; /** @hide */ - @IntDef(flag = true, - prefix = "GUARD_INTERVAL_", + @IntDef(prefix = "GUARD_INTERVAL_", value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4, @@ -346,8 +340,7 @@ public class DvbtFrontendSettings extends FrontendSettings { public static final int GUARD_INTERVAL_19_256 = FrontendDvbtGuardInterval.INTERVAL_19_256; /** @hide */ - @IntDef(flag = true, - prefix = "STANDARD", + @IntDef(prefix = "STANDARD_", value = {STANDARD_AUTO, STANDARD_T, STANDARD_T2} ) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java index 38bffec3f77f..2f45a7072017 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -89,8 +89,7 @@ public abstract class FrontendSettings { /** @hide */ - @LongDef(flag = true, - prefix = "FEC_", + @LongDef(prefix = "FEC_", value = {FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5, FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8, FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20, diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index c1e9b38a13b0..9fbea724f491 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -25,6 +25,9 @@ import android.media.tv.tuner.Lnb; import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * A Frontend Status class that contains the metrics of the active frontend. @@ -1086,22 +1089,26 @@ public class FrontendStatus { } /** - * Gets an array of all PLPs information of ATSC3 frontend, which includes both tuned and not + * Gets a list of all PLPs information of ATSC3 frontend, which includes both tuned and not * tuned PLPs for currently watching service. * - * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL - * doesn't return all PLPs information will throw IllegalStateException. Use - * {@link TunerVersionChecker#getTunerVersion()} to check the version. + * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will throw + * UnsupportedOperationException. Use {@link TunerVersionChecker#getTunerVersion()} to check + * the version. + * + * @return a list of all PLPs information. It is empty if HAL doesn't return all PLPs + * information status. */ - @SuppressLint("ArrayReturn") @NonNull - public Atsc3PlpInfo[] getAllAtsc3PlpInfo() { - TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status"); + public List<Atsc3PlpInfo> getAllAtsc3PlpInfo() { + if (!TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status")) { + throw new UnsupportedOperationException("Atsc3PlpInfo all status is empty"); + } if (mAllPlpInfo == null) { - throw new IllegalStateException("Atsc3PlpInfo all status is empty"); + return Collections.EMPTY_LIST; } - return mAllPlpInfo; + return Arrays.asList(mAllPlpInfo); } /** diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java index 726fe15b8edb..7e83d1545032 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java @@ -36,8 +36,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class Isdbs3FrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK, MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16APSK, MODULATION_MOD_32APSK}) @@ -75,8 +74,7 @@ public class Isdbs3FrontendSettings extends FrontendSettings { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_3, CODERATE_2_5, CODERATE_1_2, CODERATE_3_5, CODERATE_2_3, CODERATE_3_4, CODERATE_7_9, CODERATE_4_5, CODERATE_5_6, CODERATE_7_8, CODERATE_9_10}) diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java index 51ec5aeeb2eb..50294539e8f8 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java @@ -54,8 +54,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK, MODULATION_MOD_QPSK, MODULATION_MOD_TC8PSK}) @Retention(RetentionPolicy.SOURCE) @@ -84,8 +83,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "CODERATE_", + @IntDef(prefix = "CODERATE_", value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, CODERATE_5_6, CODERATE_7_8}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java index 89512a05674b..f08a51497f75 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java @@ -40,8 +40,7 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODULATION_", + @IntDef(prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_DQPSK, MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM}) @Retention(RetentionPolicy.SOURCE) @@ -74,8 +73,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "MODE_", + @IntDef(prefix = "MODE_", value = {MODE_UNDEFINED, MODE_AUTO, MODE_1, MODE_2, MODE_3}) @Retention(RetentionPolicy.SOURCE) public @interface Mode {} @@ -103,8 +101,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ - @IntDef(flag = true, - prefix = "BANDWIDTH_", + @IntDef(prefix = "BANDWIDTH_", value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ, BANDWIDTH_6MHZ}) @Retention(RetentionPolicy.SOURCE) @@ -132,7 +129,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ; /** @hide */ - @IntDef(flag = true, prefix = "PARTIAL_RECEPTION_FLAG_", + @IntDef(prefix = "PARTIAL_RECEPTION_FLAG_", value = {PARTIAL_RECEPTION_FLAG_UNDEFINED, PARTIAL_RECEPTION_FLAG_FALSE, PARTIAL_RECEPTION_FLAG_TRUE}) @Retention(RetentionPolicy.SOURCE) @@ -153,7 +150,7 @@ public class IsdbtFrontendSettings extends FrontendSettings { public static final int PARTIAL_RECEPTION_FLAG_TRUE = FrontendIsdbtPartialReceptionFlag.TRUE; /** @hide */ - @IntDef(flag = true, prefix = "TIME_INTERLEAVE_MODE_", + @IntDef(prefix = "TIME_INTERLEAVE_MODE_", value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, TIME_INTERLEAVE_MODE_1_0, TIME_INTERLEAVE_MODE_1_4, TIME_INTERLEAVE_MODE_1_8, TIME_INTERLEAVE_MODE_1_16, TIME_INTERLEAVE_MODE_2_0, TIME_INTERLEAVE_MODE_2_2, diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index d8f48c2cf0c6..20d711cf4c54 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -103,6 +103,7 @@ public class MtpDatabase implements AutoCloseable { private int mDeviceType; private String mHostType; private boolean mSkipThumbForHost = false; + private volatile boolean mHostIsWindows = false; private MtpServer mServer; private MtpStorageManager mManager; @@ -358,7 +359,7 @@ public class MtpDatabase implements AutoCloseable { } public void addStorage(StorageVolume storage) { - MtpStorage mtpStorage = mManager.addMtpStorage(storage); + MtpStorage mtpStorage = mManager.addMtpStorage(storage, () -> mHostIsWindows); mStorageMap.put(storage.getPath(), mtpStorage); if (mServer != null) { mServer.addStorage(mtpStorage); @@ -413,6 +414,7 @@ public class MtpDatabase implements AutoCloseable { } mHostType = ""; mSkipThumbForHost = false; + mHostIsWindows = false; } @VisibleForNative @@ -736,10 +738,12 @@ public class MtpDatabase implements AutoCloseable { : MtpConstants.RESPONSE_GENERAL_ERROR); case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO: mHostType = stringValue; + Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property) + + "=" + stringValue); if (stringValue.startsWith("Android/")) { - Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property) - + "=" + stringValue); mSkipThumbForHost = true; + } else if (stringValue.startsWith("Windows/")) { + mHostIsWindows = true; } return MtpConstants.RESPONSE_OK; } diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 88c32a3ea72b..a3754e90a875 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -19,6 +19,8 @@ package android.mtp; import android.compat.annotation.UnsupportedAppUsage; import android.os.storage.StorageVolume; +import java.util.function.Supplier; + /** * This class represents a storage unit on an MTP device. * Used only for MTP support in USB responder mode. @@ -33,14 +35,16 @@ public class MtpStorage { private final boolean mRemovable; private final long mMaxFileSize; private final String mVolumeName; + private final Supplier<Boolean> mIsHostWindows; - public MtpStorage(StorageVolume volume, int storageId) { + public MtpStorage(StorageVolume volume, int storageId, Supplier<Boolean> isHostWindows) { mStorageId = storageId; mPath = volume.getPath(); mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); mVolumeName = volume.getMediaStoreVolumeName(); + mIsHostWindows = isHostWindows; } /** @@ -93,4 +97,13 @@ public class MtpStorage { public String getVolumeName() { return mVolumeName; } + + /** + * Returns true if the mtp host of this storage is Windows. + * + * @return is host Windows + */ + public boolean isHostWindows() { + return mIsHostWindows.get(); + } } diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java index 0bede0dccbed..e9426cf2ce31 100644 --- a/media/java/android/mtp/MtpStorageManager.java +++ b/media/java/android/mtp/MtpStorageManager.java @@ -18,7 +18,11 @@ package android.mtp; import android.media.MediaFile; import android.os.FileObserver; +import android.os.SystemProperties; import android.os.storage.StorageVolume; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.util.Log; import com.android.internal.util.Preconditions; @@ -35,6 +39,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Supplier; /** * MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of @@ -199,7 +204,38 @@ public class MtpStorageManager { } public long getSize() { - return mIsDir ? 0 : getPath().toFile().length(); + return mIsDir ? 0 : maybeApplyTranscodeLengthWorkaround(getPath().toFile().length()); + } + + private long maybeApplyTranscodeLengthWorkaround(long length) { + // Windows truncates transferred files to the size advertised in the object property. + if (mStorage.isHostWindows() && isTranscodeMtpEnabled() && isFileTranscodeSupported()) { + // If the file supports transcoding, we double the returned size to accommodate + // the increase in size from transcoding to AVC. This is the same heuristic + // applied in the FUSE daemon (MediaProvider). + return length * 2; + } + return length; + } + + private boolean isTranscodeMtpEnabled() { + return SystemProperties.getBoolean("sys.fuse.transcode_mtp", false); + } + + private boolean isFileTranscodeSupported() { + // Check if the file supports transcoding by reading the |st_nlinks| struct stat + // field. This will be > 1 if the file supports transcoding. The FUSE daemon + // sets the field accordingly to enable the MTP stack workaround some Windows OS + // MTP client bug where they ignore the size returned as part of getting the MTP + // object, see MtpServer#doGetObject. + final Path path = getPath(); + try { + StructStat stat = Os.stat(path.toString()); + return stat.st_nlink > 1; + } catch (ErrnoException e) { + Log.w(TAG, "Failed to stat path: " + getPath() + ". Ignoring transcoding."); + return false; + } } public Path getPath() { @@ -420,10 +456,12 @@ public class MtpStorageManager { * @param volume Storage to add. * @return the associated MtpStorage */ - public synchronized MtpStorage addMtpStorage(StorageVolume volume) { + public synchronized MtpStorage addMtpStorage(StorageVolume volume, + Supplier<Boolean> isHostWindows) { int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1; - MtpStorage storage = new MtpStorage(volume, storageId); - MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true); + MtpStorage storage = new MtpStorage(volume, storageId, isHostWindows); + MtpObject root = new MtpObject(storage.getPath(), storageId, storage, /* parent= */ null, + /* isDir= */ true); mRoots.put(storageId, root); return storage; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java index fd66d3b9904e..f23794b50543 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java @@ -19,7 +19,7 @@ package com.android.mediaframeworktest.unit; import static org.junit.Assert.assertEquals; import android.bluetooth.BluetoothProfile; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import androidx.test.runner.AndroidJUnit4; @@ -27,22 +27,24 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) -public class BtProfileConnectionInfoTest { +public class BluetoothProfileConnectionInfoTest { @Test public void testCoverageA2dp() { final boolean supprNoisy = false; final int volume = 42; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createA2dpInfo(supprNoisy, volume); assertEquals(info.getProfile(), BluetoothProfile.A2DP); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); assertEquals(info.getVolume(), volume); } @Test public void testCoverageA2dpSink() { final int volume = 42; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createA2dpSinkInfo(volume); assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK); assertEquals(info.getVolume(), volume); } @@ -50,20 +52,21 @@ public class BtProfileConnectionInfoTest { @Test public void testCoveragehearingAid() { final boolean supprNoisy = true; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createHearingAidInfo(supprNoisy); assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); } @Test public void testCoverageLeAudio() { final boolean supprNoisy = false; final boolean isLeOutput = true; - final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy, - isLeOutput); + final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo + .createLeAudioInfo(supprNoisy, isLeOutput); assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO); - assertEquals(info.getSuppressNoisyIntent(), supprNoisy); - assertEquals(info.getIsLeOutput(), isLeOutput); + assertEquals(info.isSuppressNoisyIntent(), supprNoisy); + assertEquals(info.isLeOutput(), isLeOutput); } } diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java index abdc7e559fb3..a6a5568ac8f4 100644 --- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java +++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java @@ -160,10 +160,11 @@ public class MtpStorageManagerTest { Log.d(TAG, "sendObjectInfoChanged: " + id); objectsInfoChanged.add(id); } - }, null); + }, /* subdirectories= */ null); - mainMtpStorage = manager.addMtpStorage(mainStorage); - secondaryMtpStorage = manager.addMtpStorage(secondaryStorage); + mainMtpStorage = manager.addMtpStorage(mainStorage, /* isHostWindows= */ () -> false); + secondaryMtpStorage = manager.addMtpStorage(secondaryStorage, + /* isHostWindows= */ () -> false); } @After diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java index 414de89f8218..09573909c288 100644 --- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java +++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java @@ -16,8 +16,18 @@ package android.media.audio.common; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + import android.media.AudioAttributes; +import android.media.AudioDescriptor; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; +import android.media.AudioProfile; import android.media.AudioSystem; import android.media.AudioTrack; import android.media.MediaFormat; @@ -25,11 +35,12 @@ import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; -import static org.junit.Assert.*; - import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; + /** * Unit tests for AidlConversion utilities. * @@ -417,10 +428,102 @@ public final class AidlConversionUnitTests { () -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue)); } + @Test + public void testAudioDescriptorConversion_Default() { + ExtraAudioDescriptor aidl = createDefaultDescriptor(); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_NONE, audioDescriptor.getStandard()); + assertEquals( + AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDescriptorConversion() { + ExtraAudioDescriptor aidl = createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A}); + AudioDescriptor audioDescriptor = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl); + assertEquals(AudioDescriptor.STANDARD_EDID, audioDescriptor.getStandard()); + assertEquals(AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937, + audioDescriptor.getEncapsulationType()); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, audioDescriptor.getDescriptor())); + + ExtraAudioDescriptor reconstructedExtraDescriptor = + AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor); + assertEquals(aidl, reconstructedExtraDescriptor); + } + + @Test + public void testAudioDeviceAttributesConversion_Default() { + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_DEFAULT, "myAddress"); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes); + assertEquals("", port.name); + assertEquals(0, port.extraAudioDescriptors.length); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals("", port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEFAULT, port.ext.getDevice().device.type.type); + } + + @Test + public void testAudioDeviceAttributesConversion() { + AudioDescriptor audioDescriptor1 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A})); + + AudioDescriptor audioDescriptor2 = + AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor( + createDefaultDescriptor()); + + AudioDeviceAttributes attributes = + new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HDMI_ARC, "myAddress", "myName", new ArrayList<>(), + new ArrayList<>(Arrays.asList(audioDescriptor1, audioDescriptor2))); + AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort( + attributes); + assertEquals("myName", port.name); + assertEquals(2, port.extraAudioDescriptors.length); + assertEquals(AudioStandard.EDID, port.extraAudioDescriptors[0].standard); + assertEquals(AudioEncapsulationType.IEC61937, + port.extraAudioDescriptors[0].encapsulationType); + assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, + port.extraAudioDescriptors[0].audioDescriptor)); + assertEquals(AudioStandard.NONE, port.extraAudioDescriptors[1].standard); + assertEquals(AudioEncapsulationType.NONE, + port.extraAudioDescriptors[1].encapsulationType); + assertTrue(Arrays.equals(new byte[]{}, + port.extraAudioDescriptors[1].audioDescriptor)); + assertEquals("myAddress", port.ext.getDevice().device.address.getId()); + assertEquals(AudioDeviceDescription.CONNECTION_HDMI_ARC, + port.ext.getDevice().device.type.connection); + assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type); + } + private static AudioFormatDescription createPcm16FormatAidl() { final AudioFormatDescription aidl = new AudioFormatDescription(); aidl.type = AudioFormatType.PCM; aidl.pcm = PcmType.INT_16_BIT; return aidl; } + + private static ExtraAudioDescriptor createDefaultDescriptor() { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.NONE; + extraDescriptor.encapsulationType = AudioEncapsulationType.NONE; + extraDescriptor.audioDescriptor = new byte[]{}; + return extraDescriptor; + } + + private static ExtraAudioDescriptor createEncapsulationDescriptor(byte[] audioDescriptor) { + ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor(); + extraDescriptor.standard = AudioStandard.EDID; + extraDescriptor.encapsulationType = AudioEncapsulationType.IEC61937; + extraDescriptor.audioDescriptor = audioDescriptor; + return extraDescriptor; + } } diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 327b1fb2f5d8..66527802a0e4 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -125,14 +125,14 @@ filegroup { name: "framework-connectivity-ethernet-sources", srcs: [ "src/android/net/EthernetManager.java", + "src/android/net/EthernetNetworkManagementException.java", + "src/android/net/EthernetNetworkManagementException.aidl", "src/android/net/EthernetNetworkSpecifier.java", + "src/android/net/EthernetNetworkUpdateRequest.java", + "src/android/net/EthernetNetworkUpdateRequest.aidl", "src/android/net/IEthernetManager.aidl", + "src/android/net/IEthernetNetworkManagementListener.aidl", "src/android/net/IEthernetServiceListener.aidl", - "src/android/net/IInternalNetworkManagementListener.aidl", - "src/android/net/InternalNetworkUpdateRequest.java", - "src/android/net/InternalNetworkUpdateRequest.aidl", - "src/android/net/InternalNetworkManagementException.java", - "src/android/net/InternalNetworkManagementException.aidl", "src/android/net/ITetheredInterfaceCallback.aidl", ], path: "src", @@ -158,8 +158,6 @@ filegroup { name: "framework-connectivity-tiramisu-sources", srcs: [ ":framework-connectivity-ethernet-sources", - ":framework-connectivity-ipsec-sources", - ":framework-connectivity-netstats-sources", ], visibility: ["//frameworks/base"], } @@ -167,6 +165,8 @@ filegroup { 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 630f902ecfd7..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; @@ -48,5 +49,24 @@ public final class ConnectivityFrameworkInitializerTiramisu { return new NsdManager(context, service); } ); + + SystemServiceRegistry.registerContextAwareService( + Context.IPSEC_SERVICE, + IpSecManager.class, + (context, serviceBinder) -> { + IIpSecService service = IIpSecService.Stub.asInterface(serviceBinder); + return new IpSecManager(context, service); + } + ); + + 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 ece54df96665..e0ce081c0276 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java @@ -320,15 +320,15 @@ public class EthernetManager { } private static final class InternalNetworkManagementListener - extends IInternalNetworkManagementListener.Stub { + extends IEthernetNetworkManagementListener.Stub { @NonNull private final Executor mExecutor; @NonNull - private final BiConsumer<Network, InternalNetworkManagementException> mListener; + private final BiConsumer<Network, EthernetNetworkManagementException> mListener; InternalNetworkManagementListener( @NonNull final Executor executor, - @NonNull final BiConsumer<Network, InternalNetworkManagementException> listener) { + @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) { Objects.requireNonNull(executor, "Pass a non-null executor"); Objects.requireNonNull(listener, "Pass a non-null listener"); mExecutor = executor; @@ -338,14 +338,14 @@ public class EthernetManager { @Override public void onComplete( @Nullable final Network network, - @Nullable final InternalNetworkManagementException e) { + @Nullable final EthernetNetworkManagementException e) { mExecutor.execute(() -> mListener.accept(network, e)); } } private InternalNetworkManagementListener getInternalNetworkManagementListener( @Nullable final Executor executor, - @Nullable final BiConsumer<Network, InternalNetworkManagementException> listener) { + @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) { if (null != listener) { Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener"); } @@ -358,11 +358,12 @@ public class EthernetManager { return proxy; } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void updateConfiguration( @NonNull String iface, - @NonNull InternalNetworkUpdateRequest request, + @NonNull EthernetNetworkUpdateRequest request, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) { + @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( executor, listener); try { @@ -372,10 +373,11 @@ public class EthernetManager { } } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void connectNetwork( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) { + @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( executor, listener); try { @@ -385,10 +387,11 @@ public class EthernetManager { } } + @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS) private void disconnectNetwork( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) { + @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( executor, listener); try { diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl index dcce706989f6..adf9e5a4db9d 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl @@ -16,4 +16,4 @@ package android.net; - parcelable InternalNetworkManagementException;
\ No newline at end of file + parcelable EthernetNetworkManagementException;
\ No newline at end of file diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java index 798e9c3b52b5..a35f28e172fd 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java @@ -23,11 +23,11 @@ import android.os.Parcelable; import java.util.Objects; /** @hide */ -public final class InternalNetworkManagementException +public final class EthernetNetworkManagementException extends RuntimeException implements Parcelable { /* @hide */ - public InternalNetworkManagementException(@NonNull final String errorMessage) { + public EthernetNetworkManagementException(@NonNull final String errorMessage) { super(errorMessage); } @@ -40,7 +40,7 @@ public final class InternalNetworkManagementException public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; - final InternalNetworkManagementException that = (InternalNetworkManagementException) obj; + final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj; return Objects.equals(getMessage(), that.getMessage()); } @@ -56,16 +56,16 @@ public final class InternalNetworkManagementException } @NonNull - public static final Parcelable.Creator<InternalNetworkManagementException> CREATOR = - new Parcelable.Creator<InternalNetworkManagementException>() { + public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR = + new Parcelable.Creator<EthernetNetworkManagementException>() { @Override - public InternalNetworkManagementException[] newArray(int size) { - return new InternalNetworkManagementException[size]; + public EthernetNetworkManagementException[] newArray(int size) { + return new EthernetNetworkManagementException[size]; } @Override - public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) { - return new InternalNetworkManagementException(source.readString()); + public EthernetNetworkManagementException createFromParcel(@NonNull Parcel source) { + return new EthernetNetworkManagementException(source.readString()); } }; } diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl index da00cb97afb4..debc348ea363 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl @@ -16,4 +16,4 @@ package android.net; - parcelable InternalNetworkUpdateRequest;
\ No newline at end of file + parcelable EthernetNetworkUpdateRequest;
\ No newline at end of file diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java index f42c4b7c420d..4d229d23b163 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java @@ -23,7 +23,7 @@ import android.os.Parcelable; import java.util.Objects; /** @hide */ -public final class InternalNetworkUpdateRequest implements Parcelable { +public final class EthernetNetworkUpdateRequest implements Parcelable { @NonNull private final StaticIpConfiguration mIpConfig; @NonNull @@ -40,7 +40,7 @@ public final class InternalNetworkUpdateRequest implements Parcelable { } /** @hide */ - public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig, + public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig, @NonNull final NetworkCapabilities networkCapabilities) { Objects.requireNonNull(ipConfig); Objects.requireNonNull(networkCapabilities); @@ -48,7 +48,7 @@ public final class InternalNetworkUpdateRequest implements Parcelable { mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); } - private InternalNetworkUpdateRequest(@NonNull final Parcel source) { + private EthernetNetworkUpdateRequest(@NonNull final Parcel source) { Objects.requireNonNull(source); mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source); mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source); @@ -56,7 +56,7 @@ public final class InternalNetworkUpdateRequest implements Parcelable { @Override public String toString() { - return "InternalNetworkUpdateRequest{" + return "EthernetNetworkUpdateRequest{" + "mIpConfig=" + mIpConfig + ", mNetworkCapabilities=" + mNetworkCapabilities + '}'; } @@ -65,7 +65,7 @@ public final class InternalNetworkUpdateRequest implements Parcelable { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o; + EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o; return Objects.equals(that.getIpConfig(), mIpConfig) && Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities); @@ -88,16 +88,16 @@ public final class InternalNetworkUpdateRequest implements Parcelable { } @NonNull - public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR = - new Parcelable.Creator<InternalNetworkUpdateRequest>() { + public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR = + new Parcelable.Creator<EthernetNetworkUpdateRequest>() { @Override - public InternalNetworkUpdateRequest[] newArray(int size) { - return new InternalNetworkUpdateRequest[size]; + public EthernetNetworkUpdateRequest[] newArray(int size) { + return new EthernetNetworkUpdateRequest[size]; } @Override - public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) { - return new InternalNetworkUpdateRequest(source); + public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) { + return new EthernetNetworkUpdateRequest(source); } }; } diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl index e688bea1cfac..544d02ba76ff 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl @@ -18,8 +18,8 @@ package android.net; import android.net.IpConfiguration; import android.net.IEthernetServiceListener; -import android.net.IInternalNetworkManagementListener; -import android.net.InternalNetworkUpdateRequest; +import android.net.IEthernetNetworkManagementListener; +import android.net.EthernetNetworkUpdateRequest; import android.net.ITetheredInterfaceCallback; /** @@ -38,8 +38,8 @@ interface IEthernetManager void setIncludeTestInterfaces(boolean include); void requestTetheredInterface(in ITetheredInterfaceCallback callback); void releaseTetheredInterface(in ITetheredInterfaceCallback callback); - void updateConfiguration(String iface, in InternalNetworkUpdateRequest request, - in IInternalNetworkManagementListener listener); - void connectNetwork(String iface, in IInternalNetworkManagementListener listener); - void disconnectNetwork(String iface, in IInternalNetworkManagementListener listener); + void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request, + in IEthernetNetworkManagementListener listener); + void connectNetwork(String iface, in IEthernetNetworkManagementListener listener); + void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl index 69cde3bd14e8..93edccfdafd9 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl @@ -16,10 +16,10 @@ package android.net; -import android.net.InternalNetworkManagementException; +import android.net.EthernetNetworkManagementException; import android.net.Network; /** @hide */ -oneway interface IInternalNetworkManagementListener { - void onComplete(in Network network, in InternalNetworkManagementException exception); +oneway interface IEthernetNetworkManagementListener { + void onComplete(in Network network, in EthernetNetworkManagementException exception); }
\ No newline at end of file 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 24bc91d91ef7..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,8 +112,6 @@ filegroup { name: "services.connectivity-tiramisu-sources", srcs: [ ":services.connectivity-ethernet-sources", - ":services.connectivity-ipsec-sources", - ":services.connectivity-netstats-sources", ], path: "src", visibility: ["//frameworks/base/services/core"], @@ -93,6 +120,8 @@ filegroup { 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/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java index 179d9459fd84..4bc40eae4404 100644 --- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java +++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java @@ -1008,16 +1008,10 @@ public class IpSecService extends IIpSecService.Stub { * * @param context Binder context for this service */ - private IpSecService(Context context) { + public IpSecService(Context context) { this(context, new Dependencies()); } - static IpSecService create(Context context) - throws InterruptedException { - final IpSecService service = new IpSecService(context); - return service; - } - @NonNull private AppOpsManager getAppOpsManager() { AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); @@ -1054,26 +1048,6 @@ public class IpSecService extends IIpSecService.Stub { } } - /** Called by system server when system is ready. */ - public void systemReady() { - if (isNetdAlive()) { - Log.d(TAG, "IpSecService is ready"); - } else { - Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!"); - } - } - - synchronized boolean isNetdAlive() { - try { - if (mNetd == null) { - return false; - } - return mNetd.isAlive(); - } catch (RemoteException re) { - return false; - } - } - /** * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1. @@ -1896,7 +1870,6 @@ public class IpSecService extends IIpSecService.Stub { mContext.enforceCallingOrSelfPermission(DUMP, TAG); pw.println("IpSecService dump:"); - pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead")); pw.println(); pw.println("mUserResourceTracker:"); diff --git a/packages/ConnectivityT/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java index 497107dbf989..ddf6d2c4ab15 100644 --- a/packages/ConnectivityT/service/src/com/android/server/NsdService.java +++ b/packages/ConnectivityT/service/src/com/android/server/NsdService.java @@ -19,6 +19,9 @@ package com.android.server; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; import android.net.nsd.INsdManager; import android.net.nsd.INsdManagerCallback; import android.net.nsd.INsdServiceConnector; @@ -44,6 +47,9 @@ import com.android.net.module.util.DnsSdTxtRecord; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.CountDownLatch; @@ -60,6 +66,7 @@ public class NsdService extends INsdManager.Stub { private static final boolean DBG = true; private static final long CLEANUP_DELAY_MS = 10000; + private static final int IFACE_IDX_ANY = 0; private final Context mContext; private final NsdStateMachine mNsdStateMachine; @@ -297,7 +304,7 @@ public class NsdService extends INsdManager.Stub { maybeStartDaemon(); id = getUniqueId(); - if (discoverServices(id, args.serviceInfo.getServiceType())) { + if (discoverServices(id, args.serviceInfo)) { if (DBG) { Log.d(TAG, "Discover " + msg.arg2 + " " + id + args.serviceInfo.getServiceType()); @@ -430,13 +437,38 @@ public class NsdService extends INsdManager.Stub { } switch (code) { case NativeResponseCode.SERVICE_FOUND: - /* NNN uniqueId serviceName regType domain */ + /* NNN uniqueId serviceName regType domain interfaceIdx netId */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); + final int foundNetId; + try { + foundNetId = Integer.parseInt(cooked[6]); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]); + break; + } + if (foundNetId == 0L) { + // Ignore services that do not have a Network: they are not usable + // by apps, as they would need privileged permissions to use + // interfaces that do not have an associated Network. + break; + } + servInfo.setNetwork(new Network(foundNetId)); clientInfo.onServiceFound(clientId, servInfo); break; case NativeResponseCode.SERVICE_LOST: - /* NNN uniqueId serviceName regType domain */ + /* NNN uniqueId serviceName regType domain interfaceIdx netId */ + final int lostNetId; + try { + lostNetId = Integer.parseInt(cooked[6]); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]); + break; + } servInfo = new NsdServiceInfo(cooked[2], cooked[3]); + // The network could be null if it was torn down when the service is lost + // TODO: avoid returning null in that case, possibly by remembering found + // services on the same interface index and their network at the time + servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId)); clientInfo.onServiceLost(clientId, servInfo); break; case NativeResponseCode.SERVICE_DISCOVERY_FAILED: @@ -461,7 +493,7 @@ public class NsdService extends INsdManager.Stub { /* NNN regId errorCode */ break; case NativeResponseCode.SERVICE_RESOLVED: - /* NNN resolveId fullName hostName port txtlen txtdata */ + /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */ int index = 0; while (index < cooked[2].length() && cooked[2].charAt(index) != '.') { if (cooked[2].charAt(index) == '\\') { @@ -473,6 +505,7 @@ public class NsdService extends INsdManager.Stub { Log.e(TAG, "Invalid service found " + raw); break; } + String name = cooked[2].substring(0, index); String rest = cooked[2].substring(index); String type = rest.replace(".local.", ""); @@ -483,12 +516,13 @@ public class NsdService extends INsdManager.Stub { clientInfo.mResolvedService.setServiceType(type); clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4])); clientInfo.mResolvedService.setTxtRecords(cooked[6]); + // Network will be added after SERVICE_GET_ADDR_SUCCESS stopResolveService(id); removeRequestMap(clientId, id, clientInfo); int id2 = getUniqueId(); - if (getAddrInfo(id2, cooked[3])) { + if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) { storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); } else { clientInfo.onResolveServiceFailed( @@ -513,12 +547,31 @@ public class NsdService extends INsdManager.Stub { clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: - /* NNN resolveId hostname ttl addr */ + /* NNN resolveId hostname ttl addr interfaceIdx netId */ + Network network = null; + try { + final int netId = Integer.parseInt(cooked[6]); + network = netId == 0L ? null : new Network(netId); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e); + } + + InetAddress serviceHost = null; try { - clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); + serviceHost = InetAddress.getByName(cooked[4]); + } catch (UnknownHostException e) { + Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); + } + + // If the resolved service is on an interface without a network, consider it + // as a failure: it would not be usable by apps as they would need + // privileged permissions. + if (network != null && serviceHost != null) { + clientInfo.mResolvedService.setHost(serviceHost); + clientInfo.mResolvedService.setNetwork(network); clientInfo.onResolveServiceSucceeded( clientId, clientInfo.mResolvedService); - } catch (java.net.UnknownHostException e) { + } else { clientInfo.onResolveServiceFailed( clientId, NsdManager.FAILURE_INTERNAL_ERROR); } @@ -815,8 +868,15 @@ public class NsdService extends INsdManager.Stub { return mDaemon.execute("update", regId, t.size(), t.getRawData()); } - private boolean discoverServices(int discoveryId, String serviceType) { - return mDaemon.execute("discover", discoveryId, serviceType); + private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) { + final Network network = serviceInfo.getNetwork(); + final int discoverInterface = getNetworkInterfaceIndex(network); + if (network != null && discoverInterface == IFACE_IDX_ANY) { + Log.e(TAG, "Interface to discover service on not found"); + return false; + } + return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(), + discoverInterface); } private boolean stopServiceDiscovery(int discoveryId) { @@ -824,17 +884,61 @@ public class NsdService extends INsdManager.Stub { } private boolean resolveService(int resolveId, NsdServiceInfo service) { - String name = service.getServiceName(); - String type = service.getServiceType(); - return mDaemon.execute("resolve", resolveId, name, type, "local."); + final String name = service.getServiceName(); + final String type = service.getServiceType(); + final Network network = service.getNetwork(); + final int resolveInterface = getNetworkInterfaceIndex(network); + if (network != null && resolveInterface == IFACE_IDX_ANY) { + Log.e(TAG, "Interface to resolve service on not found"); + return false; + } + return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface); + } + + /** + * Guess the interface to use to resolve or discover a service on a specific network. + * + * This is an imperfect guess, as for example the network may be gone or not yet fully + * registered. This is fine as failing is correct if the network is gone, and a client + * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also + * this is to support the legacy mdnsresponder implementation, which historically resolved + * services on an unspecified network. + */ + private int getNetworkInterfaceIndex(Network network) { + if (network == null) return IFACE_IDX_ANY; + + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + if (cm == null) { + Log.wtf(TAG, "No ConnectivityManager for resolveService"); + return IFACE_IDX_ANY; + } + final LinkProperties lp = cm.getLinkProperties(network); + if (lp == null) return IFACE_IDX_ANY; + + // Only resolve on non-stacked interfaces + final NetworkInterface iface; + try { + iface = NetworkInterface.getByName(lp.getInterfaceName()); + } catch (SocketException e) { + Log.e(TAG, "Error querying interface", e); + return IFACE_IDX_ANY; + } + + if (iface == null) { + Log.e(TAG, "Interface not found: " + lp.getInterfaceName()); + return IFACE_IDX_ANY; + } + + return iface.getIndex(); } private boolean stopResolveService(int resolveId) { return mDaemon.execute("stop-resolve", resolveId); } - private boolean getAddrInfo(int resolveId, String hostname) { - return mDaemon.execute("getaddrinfo", resolveId, hostname); + private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) { + // interfaceIdx is always obtained (as string) from the service resolved callback + return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx); } private boolean stopGetAddrInfo(int resolveId) { diff --git a/packages/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/core/java/android/view/TransactionCommittedListener.java b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java index 6abded23a2e7..2849f94c9383 100644 --- a/core/java/android/view/TransactionCommittedListener.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 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. @@ -14,17 +14,20 @@ * limitations under the License. */ -package android.view; +package com.android.server.net; -import java.util.concurrent.Executor; +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; /** - * Interface to handle request to - * {@link SurfaceControl.Transaction#addTransactionCommittedListener(Executor, TransactionCommittedListener)} + * Key for uid stats map. */ -public interface TransactionCommittedListener { - /** - * Invoked when the transaction has been committed in SurfaceFlinger. - */ - void onTransactionCommitted(); +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 99e3160fcbe3..df19c67f00e5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -173,8 +173,10 @@ public class A2dpProfile implements LocalBluetoothProfile { } public BluetoothDevice getActiveDevice() { - if (mService == null) return null; - return mService.getActiveDevice(); + if (mBluetoothAdapter == null) return null; + final List<BluetoothDevice> activeDevices = mBluetoothAdapter + .getActiveDevices(BluetoothProfile.A2DP); + return (activeDevices.size() > 0) ? activeDevices.get(0) : null; } @Override @@ -221,7 +223,7 @@ public class A2dpProfile implements LocalBluetoothProfile { } public boolean supportsHighQualityAudio(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return false; } @@ -234,7 +236,7 @@ public class A2dpProfile implements LocalBluetoothProfile { */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public boolean isHighQualityAudioEnabled(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return false; } @@ -260,7 +262,7 @@ public class A2dpProfile implements LocalBluetoothProfile { } public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); if (bluetoothDevice == null) { return; } @@ -286,7 +288,7 @@ public class A2dpProfile implements LocalBluetoothProfile { */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public String getHighQualityAudioOptionLabel(BluetoothDevice device) { - BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); + BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice(); int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; if (bluetoothDevice == null || !supportsHighQualityAudio(device) || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index b11bbdec191f..7e5c1240cc63 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -132,10 +132,12 @@ public class HeadsetProfile implements LocalBluetoothProfile { } public BluetoothDevice getActiveDevice() { - if (mService == null) { + if (mBluetoothAdapter == null) { return null; } - return mService.getActiveDevice(); + final List<BluetoothDevice> activeDevices = mBluetoothAdapter + .getActiveDevices(BluetoothProfile.HEADSET); + return (activeDevices.size() > 0) ? activeDevices.get(0) : null; } public int getAudioState(BluetoothDevice device) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index dc109cac37b2..6f2d4decf033 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -173,8 +173,10 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getActiveDevices() { - if (mService == null) return new ArrayList<>(); - return mService.getActiveDevices(); + if (mBluetoothAdapter == null) { + return new ArrayList<>(); + } + return mBluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java index 209507ac7088..e203cbaa3da5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java @@ -21,12 +21,12 @@ import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -144,20 +144,20 @@ public class LeAudioProfile implements LocalBluetoothProfile { * @hide */ public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); } /* * @hide */ public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.disconnect(device); + if (mService == null) { + return false; + } + return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); } public int getConnectionStatus(BluetoothDevice device) { @@ -177,10 +177,10 @@ public class LeAudioProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getActiveDevices() { - if (mService == null) { + if (mBluetoothAdapter == null) { return new ArrayList<>(); } - return mService.getActiveDevices(); + return mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/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/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index f167721f94bf..d7b366e60a1d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -60,6 +60,8 @@ public class A2dpProfileTest { private BluetoothDevice mDevice; @Mock private BluetoothA2dp mBluetoothA2dp; + @Mock + private BluetoothAdapter mBluetoothAdapter; private BluetoothProfile.ServiceListener mServiceListener; private A2dpProfile mProfile; @@ -72,7 +74,8 @@ public class A2dpProfileTest { mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager); mServiceListener = mShadowBluetoothAdapter.getServiceListener(); mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp); - when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP))) + .thenReturn(Arrays.asList(mDevice)); } @Test diff --git a/packages/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 ca90fbedd4e0..ef5849c73b72 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -533,6 +533,7 @@ <!-- Permission needed for CTS test - WifiManagerTest --> <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" /> <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" /> <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> @@ -561,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 f83431b58c62..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" /> @@ -873,12 +874,6 @@ android:singleUser="true" android:permission="android.permission.BIND_DREAM_SERVICE" /> - <!-- Service for external clients to do media transfer --> - <!-- TODO(b/203800643): Export and guard with a permission. --> - <service - android:name=".media.taptotransfer.sender.MediaTttSenderService" - /> - <!-- Service for external clients to notify us of nearby media devices --> <!-- TODO(b/216313420): Export and guard with a permission. --> <service diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 08d217d15a5a..4540b77d0a40 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -301,6 +301,7 @@ class ActivityLaunchAnimator( * The intent was started. If [willAnimate] is false, nothing else will happen and the * animation will not be started. */ + @JvmDefault fun onIntentStarted(willAnimate: Boolean) {} /** @@ -308,6 +309,7 @@ class ActivityLaunchAnimator( * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called * before the cancellation. */ + @JvmDefault fun onLaunchAnimationCancelled() {} } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index f7a7603944f6..3051d8056a89 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -19,6 +19,7 @@ package com.android.systemui.animation import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.app.ActivityManager import android.app.Dialog import android.graphics.Color import android.graphics.Rect @@ -45,7 +46,8 @@ private const val TAG = "DialogLaunchAnimator" class DialogLaunchAnimator @JvmOverloads constructor( private val dreamManager: IDreamManager, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), - private var isForTesting: Boolean = false + // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed + private var forceDisableSynchronization: Boolean = ActivityManager.isLowRamDeviceStatic() ) { private companion object { private val TIMINGS = ActivityLaunchAnimator.TIMINGS @@ -111,7 +113,7 @@ class DialogLaunchAnimator @JvmOverloads constructor( dialog = dialog, animateBackgroundBoundsChange, animatedParent, - isForTesting + forceDisableSynchronization ) openedDialogs.add(animatedDialog) @@ -187,10 +189,9 @@ private class AnimatedDialog( private val parentAnimatedDialog: AnimatedDialog? = null, /** - * Whether we are currently running in a test, in which case we need to disable - * synchronization. + * Whether synchronization should be disabled, which can be useful if we are running in a test. */ - private val isForTesting: Boolean + private val forceDisableSynchronization: Boolean ) { /** * The DecorView of this dialog window. @@ -420,8 +421,9 @@ private class AnimatedDialog( * (or inversely, removed from the UI when the touch surface is made visible). */ private fun synchronizeNextDraw(then: () -> Unit) { - if (isForTesting || !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null || - !decorView.isAttachedToWindow || decorView.viewRootImpl == null) { + if (forceDisableSynchronization || + !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null || + !decorView.isAttachedToWindow || decorView.viewRootImpl == null) { // No need to synchronize if either the touch surface or dialog view is not attached // to a window. then() diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index ebe96ebf2988..77386cf2ff10 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -100,9 +100,11 @@ class LaunchAnimator( * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding * fully above the [launchContainer]. */ + @JvmDefault fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {} /** The animation made progress and the expandable view [state] should be updated. */ + @JvmDefault fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {} /** @@ -110,6 +112,7 @@ class LaunchAnimator( * called previously. This is typically used to clean up the resources initialized when the * animation was started. */ + @JvmDefault fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {} } diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 29221aa04699..208825ccc8cf 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -103,6 +103,20 @@ enum class Style(internal val coreSpec: CoreSpec) { n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0)) )), + RAINBOW(CoreSpec( + a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)), + a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0)) + )), + FRUIT_SALAD(CoreSpec( + a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.GTE, 48.0)), + a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.EQ, 36.0)), + a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 36.0)), + n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 10.0)), + n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)) + )), } class ColorScheme( diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml index ff5740609b84..989115697d6f 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml +++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -14,12 +13,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@android:color/system_neutral2_400" - android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/> -</vector>
\ No newline at end of file + +<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/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml index a41d34f8ab41..82c8d5f04327 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="@dimen/dream_overlay_complication_clock_time_padding_left" - android:fontFamily="sans-serif-thin" + android:fontFamily="@font/clock" android:textColor="@android:color/white" android:format12Hour="h:mm" android:format24Hour="kk:mm" diff --git a/packages/SystemUI/res/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/overlay_action_chip.xml b/packages/SystemUI/res/layout/overlay_action_chip.xml index 6d2d93124234..e0c20ff4269c 100644 --- a/packages/SystemUI/res/layout/overlay_action_chip.xml +++ b/packages/SystemUI/res/layout/overlay_action_chip.xml @@ -17,6 +17,7 @@ <com.android.systemui.screenshot.OverlayActionChip xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/overlay_action_chip" + android:theme="@style/FloatingOverlay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/overlay_action_chip_margin_start" 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/mediattt/DeviceInfo.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt deleted file mode 100644 index d41aaf30a12f..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.mediattt - -import android.os.Parcel -import android.os.Parcelable - -/** - * Represents a device that can send or receive media. Includes any device information necessary for - * SysUI to display an informative chip to the user. - */ -class DeviceInfo(val name: String) : Parcelable { - constructor(parcel: Parcel) : this(parcel.readString()) - - override fun writeToParcel(dest: Parcel?, flags: Int) { - dest?.writeString(name) - } - - override fun describeContents() = 0 - - override fun toString() = "name: $name" - - companion object CREATOR : Parcelable.Creator<DeviceInfo> { - override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel) - override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size) - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl deleted file mode 100644 index eb1c9d058e20..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.mediattt; - -import android.media.MediaRoute2Info; -import com.android.systemui.shared.mediattt.DeviceInfo; -import com.android.systemui.shared.mediattt.IUndoTransferCallback; - -/** - * An interface that can be invoked to trigger media transfer events on System UI. - * - * This interface is for the *sender* device, which is the device currently playing media. This - * sender device can transfer the media to a different device, called the receiver. - * - * System UI will implement this interface and other services will invoke it. - */ -interface IDeviceSenderService { - /** - * Invoke to notify System UI that this device (the sender) is close to a receiver device, so - * the user can potentially *start* a cast to the receiver device if the user moves their device - * a bit closer. - * - * Important notes: - * - When this callback triggers, the device is close enough to inform the user that - * transferring is an option, but the device is *not* close enough to actually initiate a - * transfer yet. - * - This callback is for *starting* a cast. It should be used when this device is currently - * playing media locally and the media should be transferred to be played on the receiver - * device instead. - */ - oneway void closeToReceiverToStartCast( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that this device (the sender) is close to a receiver device, so - * the user can potentially *end* a cast on the receiver device if the user moves this device a - * bit closer. - * - * Important notes: - * - When this callback triggers, the device is close enough to inform the user that - * transferring is an option, but the device is *not* close enough to actually initiate a - * transfer yet. - * - This callback is for *ending* a cast. It should be used when media is currently being - * played on the receiver device and the media should be transferred to play locally - * instead. - */ - oneway void closeToReceiverToEndCast( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver - * device has been started. - * - * Important notes: - * - This callback is for *starting* a cast. It should be used when this device is currently - * playing media locally and the media has started being transferred to the receiver device - * instead. - */ - oneway void transferToReceiverTriggered( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from the receiver and back to this device - * (the sender) has been started. - * - * Important notes: - * - This callback is for *ending* a cast. It should be used when media is currently being - * played on the receiver device and the media has started being transferred to play locally - * instead. - */ - oneway void transferToThisDeviceTriggered( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver - * device has finished successfully. - * - * Important notes: - * - This callback is for *starting* a cast. It should be used when this device had previously - * been playing media locally and the media has successfully been transferred to the - * receiver device instead. - * - * @param undoCallback will be invoked if the user chooses to undo this transfer. - */ - oneway void transferToReceiverSucceeded( - in MediaRoute2Info mediaInfo, - in DeviceInfo otherDeviceInfo, - in IUndoTransferCallback undoCallback); - - /** - * Invoke to notify System UI that a media transfer from the receiver and back to this device - * (the sender) has finished successfully. - * - * Important notes: - * - This callback is for *ending* a cast. It should be used when media was previously being - * played on the receiver device and has been successfully transferred to play locally on - * this device instead. - * - * @param undoCallback will be invoked if the user chooses to undo this transfer. - */ - oneway void transferToThisDeviceSucceeded( - in MediaRoute2Info mediaInfo, - in DeviceInfo otherDeviceInfo, - in IUndoTransferCallback undoCallback); - - /** - * Invoke to notify System UI that the attempted transfer has failed. - * - * This callback will be used for both the transfer that should've *started* playing the media - * on the receiver and the transfer that should've *ended* the playing on the receiver. - */ - oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); - - /** - * Invoke to notify System UI that this device is no longer close to the receiver device. - */ - oneway void noLongerCloseToReceiver( - in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo); -} 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/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index cc10b02a8bb6..d7a8a7aa2c10 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -680,6 +680,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Whether the secure camera is currently showing over the keyguard. + */ + public boolean isSecureCameraLaunchedOverKeyguard() { + return mSecureCameraLaunched; + } + + /** * @return a cached version of DreamManager.isDreaming() */ public boolean isDreaming() { diff --git a/packages/SystemUI/src/com/android/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/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5a52fd0516ea..59c291cf9e5c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -437,7 +437,7 @@ class ControlsUiControllerImpl @Inject constructor ( * number of columns. This helps prevent text truncation on these devices. */ private fun findMaxColumns(): Int { - val res = context.resources + val res = activityContext.resources var maxColumns = res.getInteger(R.integer.controls_max_columns) val maxColumnsAdjustWidth = res.getInteger(R.integer.controls_max_columns_adjust_below_width_dp) 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/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 2160744c6803..77997e437e33 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -31,8 +31,10 @@ import androidx.lifecycle.ViewModelStore; import com.android.internal.policy.PhoneWindow; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.Complication; +import com.android.systemui.dreams.complication.ComplicationUtils; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; @@ -57,6 +59,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final DreamBackend mDreamBackend; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -109,6 +112,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ setCurrentState(Lifecycle.State.CREATED); mLifecycleRegistry = component.getLifecycleRegistry(); mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); + mDreamBackend = component.getDreamBackend(); mDreamOverlayTouchMonitor.init(); } @@ -130,6 +134,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { setCurrentState(Lifecycle.State.STARTED); mExecutor.execute(() -> { + mStateController.setAvailableComplicationTypes( + ComplicationUtils.convertComplicationTypes( + mDreamBackend.getEnabledComplications())); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index ac7457d90e49..bc5a52a40a35 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -131,9 +131,7 @@ public class DreamOverlayStateController implements .filter(complication -> { @Complication.ComplicationType final int requiredTypes = complication.getRequiredTypeAvailability(); - - return requiredTypes == Complication.COMPLICATION_TYPE_NONE - || (requiredTypes & getAvailableComplicationTypes()) == requiredTypes; + return (requiredTypes & getAvailableComplicationTypes()) == requiredTypes; }) .collect(Collectors.toCollection(HashSet::new)) : mComplications); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java index 3a2a6ef60f03..a4a0075e742a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java @@ -25,6 +25,8 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION import com.android.settingslib.dream.DreamBackend; +import java.util.Set; + /** * A collection of utility methods for working with {@link Complication}. */ @@ -50,4 +52,14 @@ public class ComplicationUtils { return COMPLICATION_TYPE_NONE; } } + + /** + * Converts a set of {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to + * a combined complications types state. + */ + @Complication.ComplicationType + public static int convertComplicationTypes(@DreamBackend.ComplicationType Set<Integer> types) { + return types.stream().mapToInt(ComplicationUtils::convertComplicationType).reduce( + COMPLICATION_TYPE_NONE, (a, b) -> a | b); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java index 59c52b9e402b..6861c7479161 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java @@ -44,6 +44,11 @@ public class DreamClockDateComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_DATE; + } + /** * Create {@link DreamClockDateViewHolder}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java index b0c3a424cc92..936767a60233 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java @@ -44,6 +44,11 @@ public class DreamClockTimeComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_TIME; + } + /** * Create {@link DreamClockTimeViewHolder}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java index cbdbef3ae57e..f5c5a434a077 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java @@ -53,6 +53,11 @@ public class DreamWeatherComplication implements Complication { mComponentFactory = componentFactory; } + @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_WEATHER; + } + /** * Create {@link DreamWeatherViewHolder}. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java index 053c5b345760..d539f5c2b870 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java @@ -22,6 +22,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextClock; import com.android.internal.util.Preconditions; import com.android.systemui.R; @@ -78,6 +79,8 @@ public interface DreamClockTimeComplicationComponent { "clock_time_complication_layout_params"; // Order weight of insert into parent container int INSERT_ORDER_WEIGHT = 0; + String TAG_WEIGHT = "'wght' "; + int WEIGHT = 200; /** * Provides the complication view. @@ -86,10 +89,12 @@ public interface DreamClockTimeComplicationComponent { @DreamClockTimeComplicationScope @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) static View provideComplicationView(LayoutInflater layoutInflater) { - return Preconditions.checkNotNull( - layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, - null, false), + final TextClock view = Preconditions.checkNotNull((TextClock) + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, + null, false), "R.layout.dream_overlay_complication_clock_time did not properly inflated"); + view.setFontVariationSettings(TAG_WEIGHT + WEIGHT); + return view; } /** diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java index 05ab9015fecc..60278a9da7eb 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java @@ -22,6 +22,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.ViewModelStore; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dreams.DreamOverlayContainerViewController; import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.dagger.ComplicationModule; @@ -68,4 +69,7 @@ public interface DreamOverlayComponent { /** Builds a {@link DreamOverlayTouchMonitor} */ DreamOverlayTouchMonitor getDreamOverlayTouchMonitor(); + + /** Builds a ${@link DreamBackend} */ + DreamBackend getDreamBackend(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 4eb5cb97607a..efa063f4dfc8 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams.dagger; import android.content.ContentResolver; +import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.view.LayoutInflater; @@ -27,6 +28,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import com.android.internal.util.Preconditions; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.battery.BatteryMeterViewController; @@ -147,4 +149,10 @@ public abstract class DreamOverlayModule { static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) { return lifecycleOwner.getLifecycle(); } + + @Provides + @DreamOverlayComponent.DreamOverlayScope + static DreamBackend providesDreamBackend(Context context) { + return DreamBackend.getInstance(context); + } } diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt deleted file mode 100644 index 42f3512129a3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.fgsmanager - -import android.content.Context -import android.os.Bundle -import android.text.format.DateUtils -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.ImageView -import android.widget.TextView -import androidx.annotation.GuardedBy -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.android.systemui.R -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.fgsmanager.FgsManagerDialogController.RunningApp -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor - -/** - * Dialog which shows a list of running foreground services and offers controls to them - */ -class FgsManagerDialog( - context: Context, - private val executor: Executor, - @Background private val backgroundExecutor: Executor, - private val systemClock: SystemClock, - private val fgsManagerDialogController: FgsManagerDialogController -) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog) { - - private val appListRecyclerView: RecyclerView = RecyclerView(this.context) - private val adapter: AppListAdapter = AppListAdapter() - - init { - setTitle(R.string.fgs_manager_dialog_title) - setView(appListRecyclerView) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - appListRecyclerView.layoutManager = LinearLayoutManager(context) - fgsManagerDialogController.registerDialogForChanges( - object : FgsManagerDialogController.FgsManagerDialogCallback { - override fun onRunningAppsChanged(apps: List<RunningApp>) { - executor.execute { - adapter.setData(apps) - } - } - } - ) - appListRecyclerView.adapter = adapter - backgroundExecutor.execute { adapter.setData(fgsManagerDialogController.runningAppList) } - } - - private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() { - private val lock = Any() - - @GuardedBy("lock") - private val data: MutableList<RunningApp> = ArrayList() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder { - return AppItemViewHolder(LayoutInflater.from(context) - .inflate(R.layout.fgs_manager_app_item, parent, false)) - } - - override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) { - var runningApp: RunningApp - synchronized(lock) { - runningApp = data[position] - } - with(holder) { - iconView.setImageDrawable(runningApp.mIcon) - appLabelView.text = runningApp.mAppLabel - durationView.text = DateUtils.formatDuration( - Math.max(systemClock.elapsedRealtime() - runningApp.mTimeStarted, 60000), - DateUtils.LENGTH_MEDIUM) - stopButton.setOnClickListener { - fgsManagerDialogController - .stopAllFgs(runningApp.mUserId, runningApp.mPackageName) - } - } - } - - override fun getItemCount(): Int { - synchronized(lock) { return data.size } - } - - fun setData(newData: List<RunningApp>) { - var oldData: List<RunningApp> - synchronized(lock) { - oldData = ArrayList(data) - data.clear() - data.addAll(newData) - } - - DiffUtil.calculateDiff(object : DiffUtil.Callback() { - override fun getOldListSize(): Int { - return oldData.size - } - - override fun getNewListSize(): Int { - return newData.size - } - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): - Boolean { - return oldData[oldItemPosition] == newData[newItemPosition] - } - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): - Boolean { - return true // TODO, look into updating the time subtext - } - }).dispatchUpdatesTo(this) - } - } - - private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) { - val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label) - val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration) - val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon) - val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt deleted file mode 100644 index 159ed39025a1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.fgsmanager - -import android.content.pm.PackageManager -import android.content.pm.PackageManager.NameNotFoundException -import android.graphics.drawable.Drawable -import android.os.Handler -import android.os.UserHandle -import android.util.ArrayMap -import android.util.Log -import androidx.annotation.GuardedBy -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.policy.RunningFgsController -import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime -import javax.inject.Inject - -/** - * Controls events relevant to FgsManagerDialog - */ -class FgsManagerDialogController @Inject constructor( - private val packageManager: PackageManager, - @Background private val backgroundHandler: Handler, - private val runningFgsController: RunningFgsController -) : RunningFgsController.Callback { - private val lock = Any() - private val clearCacheToken = Any() - - @GuardedBy("lock") - private var runningApps: Map<UserPackageTime, RunningApp>? = null - @GuardedBy("lock") - private var listener: FgsManagerDialogCallback? = null - - interface FgsManagerDialogCallback { - fun onRunningAppsChanged(apps: List<RunningApp>) - } - - data class RunningApp( - val mUserId: Int, - val mPackageName: String, - val mAppLabel: CharSequence, - val mIcon: Drawable, - val mTimeStarted: Long - ) - - val runningAppList: List<RunningApp> - get() { - synchronized(lock) { - if (runningApps == null) { - onFgsPackagesChangedLocked(runningFgsController.getPackagesWithFgs()) - } - return convertToRunningAppList(runningApps!!) - } - } - - fun registerDialogForChanges(callback: FgsManagerDialogCallback) { - synchronized(lock) { - runningFgsController.addCallback(this) - listener = callback - backgroundHandler.removeCallbacksAndMessages(clearCacheToken) - } - } - - fun onFinishDialog() { - synchronized(lock) { - listener = null - // Keep data such as icons cached for some time since loading can be slow - backgroundHandler.postDelayed( - { - synchronized(lock) { - runningFgsController.removeCallback(this) - runningApps = null - } - }, clearCacheToken, RUNNING_APP_CACHE_TIMEOUT_MILLIS) - } - } - - private fun onRunningAppsChanged(apps: ArrayMap<UserPackageTime, RunningApp>) { - listener?.let { - backgroundHandler.post { it.onRunningAppsChanged(convertToRunningAppList(apps)) } - } - } - - override fun onFgsPackagesChanged(packages: List<UserPackageTime>) { - backgroundHandler.post { - synchronized(lock) { onFgsPackagesChangedLocked(packages) } - } - } - - /** - * Run on background thread - */ - private fun onFgsPackagesChangedLocked(packages: List<UserPackageTime>) { - val newRunningApps = ArrayMap<UserPackageTime, RunningApp>() - for (packageWithFgs in packages) { - val ra = runningApps?.get(packageWithFgs) - if (ra == null) { - val userId = packageWithFgs.userId - val packageName = packageWithFgs.packageName - try { - val ai = packageManager.getApplicationInfo(packageName, 0) - var icon = packageManager.getApplicationIcon(ai) - icon = packageManager.getUserBadgedIcon(icon, - UserHandle.of(userId)) - val label = packageManager.getApplicationLabel(ai) - newRunningApps[packageWithFgs] = RunningApp(userId, packageName, - label, icon, packageWithFgs.startTimeMillis) - } catch (e: NameNotFoundException) { - Log.e(LOG_TAG, - "Application info not found: $packageName", e) - } - } else { - newRunningApps[packageWithFgs] = ra - } - } - runningApps = newRunningApps - onRunningAppsChanged(newRunningApps) - } - - fun stopAllFgs(userId: Int, packageName: String) { - runningFgsController.stopFgs(userId, packageName) - } - - companion object { - private val LOG_TAG = FgsManagerDialogController::class.java.simpleName - private const val RUNNING_APP_CACHE_TIMEOUT_MILLIS: Long = 20_000 - - private fun convertToRunningAppList(apps: Map<UserPackageTime, RunningApp>): - List<RunningApp> { - val result = mutableListOf<RunningApp>() - result.addAll(apps.values) - result.sortWith { a: RunningApp, b: RunningApp -> - b.mTimeStarted.compareTo(a.mTimeStarted) - } - return result - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt deleted file mode 100644 index 28749296c4cf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.fgsmanager - -import android.content.Context -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.animation.DialogLaunchAnimator -import android.content.DialogInterface -import android.view.View -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor -import javax.inject.Inject - -/** - * Factory to create [FgsManagerDialog] instances - */ -@SysUISingleton -class FgsManagerDialogFactory -@Inject constructor( - private val context: Context, - @Main private val executor: Executor, - @Background private val backgroundExecutor: Executor, - private val systemClock: SystemClock, - private val dialogLaunchAnimator: DialogLaunchAnimator, - private val fgsManagerDialogController: FgsManagerDialogController -) { - - val lock = Any() - - companion object { - private var fgsManagerDialog: FgsManagerDialog? = null - } - - /** - * Creates the dialog if it doesn't exist - */ - fun create(viewLaunchedFrom: View?) { - if (fgsManagerDialog == null) { - fgsManagerDialog = FgsManagerDialog(context, executor, backgroundExecutor, - systemClock, fgsManagerDialogController) - fgsManagerDialog!!.setOnDismissListener { i: DialogInterface? -> - fgsManagerDialogController.onFinishDialog() - fgsManagerDialog = null - } - dialogLaunchAnimator.showFromView(fgsManagerDialog!!, viewLaunchedFrom!!) - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/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/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java index d1a103e3a8fa..de67ba8a6ded 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java @@ -40,6 +40,7 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { private boolean mIsDropDownMode; private int mMenuVerticalPadding = 0; private int mGlobalActionsSidePadding = 0; + private int mMaximumWidthThresholdDp = 800; private ListAdapter mAdapter; private AdapterView.OnItemLongClickListener mOnItemLongClickListener; @@ -92,6 +93,8 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { // width should be between [.5, .9] of screen int parentWidth = res.getSystem().getDisplayMetrics().widthPixels; + float parentDensity = res.getSystem().getDisplayMetrics().density; + float parentWidthDp = parentWidth / parentDensity; int widthSpec = MeasureSpec.makeMeasureSpec( (int) (parentWidth * 0.9), MeasureSpec.AT_MOST); int maxWidth = 0; @@ -101,9 +104,12 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { int w = child.getMeasuredWidth(); maxWidth = Math.max(w, maxWidth); } - int width = Math.max(maxWidth, (int) (parentWidth * 0.5)); - listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding); + int width = maxWidth; + if (parentWidthDp < mMaximumWidthThresholdDp) { + width = Math.max(maxWidth, (int) (parentWidth * 0.5)); + } + listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding); setWidth(width); if (getAnchorView().getLayoutDirection() == LayoutDirection.LTR) { setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index e88011e0d3fe..88555edd1e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -233,10 +233,13 @@ public class KeyguardService extends Service { mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; mShellTransitions = shellTransitions; + } - if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) { - // Nothing here. Initialization for this happens in onCreate. - } else { + @Override + public void onCreate() { + ((SystemUIApplication) getApplication()).startServicesIfNeeded(); + + if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); if (sEnableRemoteKeyguardGoingAwayAnimation) { final RemoteAnimationAdapter exitAnimationAdapter = @@ -248,22 +251,19 @@ public class KeyguardService extends Service { } if (sEnableRemoteKeyguardOccludeAnimation) { final RemoteAnimationAdapter occludeAnimationAdapter = - new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0); + new RemoteAnimationAdapter( + mKeyguardViewMediator.getOccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter); + + final RemoteAnimationAdapter unoccludeAnimationAdapter = + new RemoteAnimationAdapter( + mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0); definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, - occludeAnimationAdapter); + unoccludeAnimationAdapter); } ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay( DEFAULT_DISPLAY, definition); - } - } - - @Override - public void onCreate() { - ((SystemUIApplication) getApplication()).startServicesIfNeeded(); - - if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) { return; } if (sEnableRemoteKeyguardGoingAwayAnimation) { @@ -354,33 +354,6 @@ public class KeyguardService extends Service { } }; - private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner = - new IRemoteAnimationRunner.Stub() { - @Override // Binder interface - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback) { - Slog.d(TAG, "mOccludeAnimationRunner.onAnimationStart: transit=" + transit); - try { - if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) { - mBinder.setOccluded(true /* isOccluded */, true /* animate */); - } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) { - mBinder.setOccluded(false /* isOccluded */, false /* animate */); - } - // TODO(bc-unlock): Implement (un)occlude animation. - finishedCallback.onAnimationFinished(); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException"); - } - } - - @Override // Binder interface - public void onAnimationCancelled() { - } - }; - final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() { @Override public void startAnimation(IBinder transition, TransitionInfo info, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/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/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index fd2c6dd3ca36..0f08a1863e08 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -77,11 +77,13 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.jank.InteractionJankMonitor; @@ -89,6 +91,7 @@ import com.android.internal.jank.InteractionJankMonitor.Configuration; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardStateCallback; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardConstants; @@ -102,7 +105,10 @@ import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; +import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.Interpolators; +import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -375,6 +381,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private int mUnlockSoundId; private int mTrustedSoundId; private int mLockSoundStreamId; + private final float mPowerButtonY; + private final float mWindowCornerRadius; + /** * The animation used for hiding keyguard. This is used to fetch the animation timings if * WindowManager is not providing us with them. @@ -815,6 +824,109 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } }; + /** + * Animation launch controller for activities that occlude the keyguard. + */ + private final ActivityLaunchAnimator.Controller mOccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationStart(boolean isExpandingFullyAbove) { + setOccluded(true /* occluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(true /* occluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + final float initialHeight = height / 3f; + final float initialWidth = width / 3f; + + if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) { + // Start the animation near the power button, at one-third size, since the + // camera was launched from the power button. + return new LaunchAnimator.State( + (int) (mPowerButtonY - initialHeight / 2f) /* top */, + (int) (mPowerButtonY + initialHeight / 2f) /* bottom */, + (int) (width - initialWidth) /* left */, + width /* right */, + mWindowCornerRadius, mWindowCornerRadius); + } else { + // Start the animation in the center of the screen, scaled down. + return new LaunchAnimator.State( + height / 2, height / 2, width / 2, width / 2, + mWindowCornerRadius, mWindowCornerRadius); + } + } + }; + + /** + * Animation controller for activities that unocclude the keyguard. This will play the launch + * animation in reverse. + */ + private final ActivityLaunchAnimator.Controller mUnoccludeAnimationController = + new ActivityLaunchAnimator.Controller() { + @Override + public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @Override + public void onLaunchAnimationCancelled() { + setOccluded(false /* isOccluded */, false /* animate */); + } + + @NonNull + @Override + public ViewGroup getLaunchContainer() { + return ((ViewGroup) mKeyguardViewControllerLazy.get() + .getViewRootImpl().getView()); + } + + @Override + public void setLaunchContainer(@NonNull ViewGroup launchContainer) { + // No-op, launch container is always the shade. + Log.wtf(TAG, "Someone tried to change the launch container for the " + + "ActivityLaunchAnimator, which should never happen."); + } + + @NonNull + @Override + public LaunchAnimator.State createAnimatorState() { + final int width = getLaunchContainer().getWidth(); + final int height = getLaunchContainer().getHeight(); + + // TODO(b/207399883): Unocclude animation. This currently ends instantly. + return new LaunchAnimator.State( + 0, height, 0, width, mWindowCornerRadius, mWindowCornerRadius); + } + }; + + private IRemoteAnimationRunner mOccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); + private IRemoteAnimationRunner mUnoccludeAnimationRunner = + new ActivityLaunchRemoteAnimationRunner(mUnoccludeAnimationController); + private DeviceConfigProxy mDeviceConfig; private DozeParameters mDozeParameters; @@ -824,6 +936,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private boolean mWallpaperSupportsAmbientMode; private ScreenOnCoordinator mScreenOnCoordinator; + private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator; + /** * Injected constructor. See {@link KeyguardModule}. */ @@ -850,7 +964,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) { + Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, + Lazy<ActivityLaunchAnimator> activityLaunchAnimator) { super(context); mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; @@ -890,6 +1005,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mScreenOffAnimationController = screenOffAnimationController; mInteractionJankMonitor = interactionJankMonitor; mDreamOverlayStateController = dreamOverlayStateController; + + mActivityLaunchAnimator = activityLaunchAnimator; + + mPowerButtonY = context.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y); + mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); } public void userActivity() { @@ -1440,6 +1561,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.endSection(); } + public IRemoteAnimationRunner getOccludeAnimationRunner() { + return mOccludeAnimationRunner; + } + + public IRemoteAnimationRunner getUnoccludeAnimationRunner() { + return mUnoccludeAnimationRunner; + } + public boolean isHiding() { return mHiding; } @@ -2868,4 +2997,36 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, return mMessage; } } + + /** + * Implementation of RemoteAnimationRunner that creates a new + * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the + * remote animation methods to that runner. + */ + private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub { + + private final ActivityLaunchAnimator.Controller mActivityLaunchController; + @Nullable private ActivityLaunchAnimator.Runner mRunner; + + ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) { + mActivityLaunchController = controller; + } + + @Override + public void onAnimationCancelled() throws RemoteException { + if (mRunner != null) { + mRunner.onAnimationCancelled(); + } + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) + throws RemoteException { + mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController); + mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index b49b49cbbb6d..195ef1a84c43 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -32,6 +32,7 @@ import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.keyguard.mediator.ScreenOnCoordinator; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; @@ -102,7 +103,8 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - Lazy<NotificationShadeWindowController> notificationShadeWindowController) { + Lazy<NotificationShadeWindowController> notificationShadeWindowController, + Lazy<ActivityLaunchAnimator> activityLaunchAnimator) { return new KeyguardViewMediator( context, falsingCollector, @@ -128,8 +130,8 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, - notificationShadeWindowController - ); + notificationShadeWindowController, + activityLaunchAnimator); } @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt index b15807c0475c..6d589aac2079 100644 --- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt @@ -176,14 +176,9 @@ class LogBuffer @JvmOverloads constructor( buffer.removeFirst() } buffer.add(message as LogMessageImpl) - if (systrace) { - val messageStr = message.printer(message) - Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "$name - $messageStr") - } - if (logcatEchoTracker.isBufferLoggable(name, message.level) || - logcatEchoTracker.isTagLoggable(message.tag, message.level)) { - echo(message) - } + val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) || + logcatEchoTracker.isTagLoggable(message.tag, message.level) + echo(message, toLogcat = includeInLogcat, toSystrace = systrace) } /** Converts the entire buffer to a newline-delimited string */ @@ -232,8 +227,24 @@ class LogBuffer @JvmOverloads constructor( pw.println(message.printer(message)) } - private fun echo(message: LogMessage) { - val strMessage = message.printer(message) + private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) { + if (toLogcat || toSystrace) { + val strMessage = message.printer(message) + if (toSystrace) { + echoToSystrace(message, strMessage) + } + if (toLogcat) { + echoToLogcat(message, strMessage) + } + } + } + + private fun echoToSystrace(message: LogMessage, strMessage: String) { + Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", + "$name - ${message.level.shortString} ${message.tag}: $strMessage") + } + + private fun echoToLogcat(message: LogMessage, strMessage: String) { when (message.level) { LogLevel.VERBOSE -> Log.v(message.tag, strMessage) LogLevel.DEBUG -> Log.d(message.tag, strMessage) 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 29938a05a327..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; @@ -31,10 +32,11 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; -import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService; +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,12 +101,13 @@ public interface MediaModule { @SysUISingleton static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, + CommandQueue commandQueue, Context context, WindowManager windowManager) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerSender(context, windowManager)); + return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager)); } /** */ @@ -112,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)); } /** */ @@ -127,23 +132,14 @@ 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 MediaTttSenderService. */ - @Binds - @IntoMap - @ClassKey(MediaTttSenderService.class) - Service bindMediaTttSenderService(MediaTttSenderService service); - /** Inject into NearbyMediaDevicesService. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java index 2c35db337cda..7c0481049098 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java +++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java @@ -37,6 +37,11 @@ public class MediaDreamComplication implements Complication { } @Override + public int getRequiredTypeAvailability() { + return COMPLICATION_TYPE_CAST_INFO; + } + + @Override public ViewHolder createView(ComplicationViewModel model) { return mComponentFactory.create().getViewHolder(); } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt index 37208515120a..26f31cd11704 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -16,34 +16,24 @@ package com.android.systemui.media.taptotransfer -import android.content.ComponentName +import android.app.StatusBarManager import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.graphics.Color -import android.graphics.drawable.Icon import android.media.MediaRoute2Info -import android.os.IBinder 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.MediaTttSenderService -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 import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService -import com.android.systemui.shared.mediattt.IUndoTransferCallback 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 /** @@ -54,210 +44,133 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, private val context: Context, - private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver, + @Main private val mainExecutor: Executor ) { - private var senderService: IDeviceSenderService? = null - private val senderServiceConnection = SenderServiceConnection() - - 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. */ inner class SenderCommand : Command { override fun execute(pw: PrintWriter, args: List<String>) { - val otherDeviceName = args[0] - val mediaInfo = MediaRoute2Info.Builder("id", "Test Name") - .addFeature("feature") - .build() - val otherDeviceInfo = DeviceInfo(otherDeviceName) - - when (args[1]) { - MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> { - runOnService { senderService -> - senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo) - } - } - MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> { - runOnService { senderService -> - senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo) - } - } - TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() { - Log.i(TAG, "Undo transfer to receiver callback triggered") - // The external services that implement this callback would kick off a - // transfer back to this device, so mimic that here. - runOnService { senderService -> - senderService - .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo) - } - } - } - runOnService { senderService -> - senderService - .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback) - } - } - TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() { - Log.i(TAG, "Undo transfer to this device callback triggered") - // The external services that implement this callback would kick off a - // transfer back to the receiver, so mimic that here. - runOnService { senderService -> - senderService - .transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - } - } - runOnService { senderService -> - senderService - .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback) - } - } - TRANSFER_FAILED_COMMAND_NAME -> { - runOnService { senderService -> - senderService.transferFailed(mediaInfo, otherDeviceInfo) - } - } - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> { - runOnService { senderService -> - senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo) - context.unbindService(senderServiceConnection) - } - } - else -> { - pw.println("Sender command must be one of " + - "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " + - "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " + - "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " + - "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " + - "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " + - "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " + - "$TRANSFER_FAILED_COMMAND_NAME, " + - NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME - ) - } + val routeInfo = MediaRoute2Info.Builder("id", args[0]) + .addFeature("feature") + .build() + + val commandName = args[1] + @StatusBarManager.MediaTransferSenderState + val displayState = stateStringToStateInt[commandName] + if (displayState == null) { + pw.println("Invalid command name $commandName") + return } - } - override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>") + val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) + as StatusBarManager + statusBarManager.updateMediaTapToTransferSenderDisplay( + displayState, + routeInfo, + getUndoExecutor(displayState), + getUndoCallback(displayState) + ) } - private fun runOnService(command: SenderServiceCommand) { - val currentService = senderService - if (currentService != null) { - command.run(currentService) + private fun getUndoExecutor( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Executor? { + return if (isSucceededState(displayState)) { + mainExecutor } else { - bindService(command) + null } } - private fun bindService(command: SenderServiceCommand) { - senderServiceConnection.pendingCommand = command - val binding = context.bindService( - Intent(context, MediaTttSenderService::class.java), - senderServiceConnection, - Context.BIND_AUTO_CREATE - ) - Log.i(TAG, "Starting service binding? $binding") + private fun getUndoCallback( + @StatusBarManager.MediaTransferSenderState displayState: Int + ): Runnable? { + return if (isSucceededState(displayState)) { + Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") } + } 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 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") - } - } - /** A command to REMOVE the media ttt chip on the RECEIVER device. */ - inner class RemoveChipCommandReceiver : Command { - override fun execute(pw: PrintWriter, args: List<String>) { - mediaTttChipControllerReceiver.removeChip() - } override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG") + pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>") } } - /** A service connection for [IDeviceSenderService]. */ - private inner class SenderServiceConnection : ServiceConnection { - // A command that should be run when the service gets connected. - var pendingCommand: SenderServiceCommand? = null - - override fun onServiceConnected(className: ComponentName, service: IBinder) { - val newCallback = IDeviceSenderService.Stub.asInterface(service) - senderService = newCallback - pendingCommand?.run(newCallback) - pendingCommand = null + /** All commands for the receiver device. */ + inner class ReceiverCommand : Command { + override fun execute(pw: PrintWriter, args: List<String>) { + 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 onServiceDisconnected(className: ComponentName) { - senderService = null + override fun help(pw: PrintWriter) { + pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND <chipState>") } } - - /** An interface defining a command that should be run on the sender service. */ - private fun interface SenderServiceCommand { - /** Runs the command on the provided [senderService]. */ - fun run(senderService: IDeviceSenderService) - } } @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 c656df2e0a35..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 @@ -19,9 +19,9 @@ package com.android.systemui.media.taptotransfer.sender import android.content.Context import android.graphics.drawable.Drawable import android.view.View +import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.media.taptotransfer.common.MediaTttChipState -import com.android.systemui.shared.mediattt.IUndoTransferCallback /** * A class that stores all the information necessary to display the media tap-to-transfer chip on @@ -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, @@ -130,7 +130,7 @@ class TransferToReceiverSucceeded( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, - val undoCallback: IUndoTransferCallback? = null + val undoCallback: IUndoMediaTransferCallback? = null ) : ChipStateSender(appIconDrawable, appIconContentDescription) { override fun getChipTextString(context: Context): String { return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName) @@ -169,7 +169,7 @@ class TransferToThisDeviceSucceeded( appIconDrawable: Drawable, appIconContentDescription: String, private val otherDeviceName: String, - val undoCallback: IUndoTransferCallback? = null + val undoCallback: IUndoMediaTransferCallback? = null ) : ChipStateSender(appIconDrawable, appIconContentDescription) { override fun getChipTextString(context: Context): String { return context.getString(R.string.media_transfer_playing_this_device) 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 453e3d627bc8..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 @@ -16,14 +16,21 @@ package com.android.systemui.media.taptotransfer.sender +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.View import android.view.ViewGroup import android.view.WindowManager import android.widget.TextView +import com.android.internal.statusbar.IUndoMediaTransferCallback 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 /** @@ -32,11 +39,93 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipControllerSender @Inject constructor( + commandQueue: CommandQueue, context: Context, windowManager: WindowManager, ) : MediaTttChipControllerCommon<ChipStateSender>( context, windowManager, R.layout.media_ttt_chip ) { + // TODO(b/216141276): 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 updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState displayState: Int, + routeInfo: MediaRoute2Info, + undoCallback: IUndoMediaTransferCallback? + ) { + this@MediaTttChipControllerSender.updateMediaTapToTransferSenderDisplay( + displayState, routeInfo, undoCallback + ) + } + } + + init { + 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. */ override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) { @@ -64,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/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt deleted file mode 100644 index 717752e536b0..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.media.taptotransfer.sender - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.graphics.Color -import android.graphics.drawable.Icon -import android.media.MediaRoute2Info -import android.os.IBinder -import com.android.systemui.R -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IUndoTransferCallback -import com.android.systemui.shared.mediattt.IDeviceSenderService -import javax.inject.Inject - -/** - * Service that allows external handlers to trigger the media chip on the sender device. - */ -class MediaTttSenderService @Inject constructor( - context: Context, - val controller: MediaTttChipControllerSender -) : Service() { - - // TODO(b/203800643): Add logging when callbacks trigger. - private val binder: IBinder = object : IDeviceSenderService.Stub() { - override fun closeToReceiverToStartCast( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo) - } - - override fun closeToReceiverToEndCast( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo) - } - - override fun transferFailed( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferFailed(mediaInfo) - } - - override fun transferToReceiverTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo) - } - - override fun transferToThisDeviceTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo) - } - - override fun transferToReceiverSucceeded( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo, - undoCallback: IUndoTransferCallback - ) { - this@MediaTttSenderService.transferToReceiverSucceeded( - mediaInfo, otherDeviceInfo, undoCallback - ) - } - - override fun transferToThisDeviceSucceeded( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo, - undoCallback: IUndoTransferCallback - ) { - this@MediaTttSenderService.transferToThisDeviceSucceeded( - mediaInfo, otherDeviceInfo, undoCallback - ) - } - - override fun noLongerCloseToReceiver( - mediaInfo: MediaRoute2Info, - otherDeviceInfo: DeviceInfo - ) { - this@MediaTttSenderService.noLongerCloseToReceiver() - } - } - - // TODO(b/203800643): Use the app icon from the media info instead of a fake one. - private val fakeAppIconDrawable = - Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { - it.setTint(Color.YELLOW) - } - - override fun onBind(intent: Intent?): IBinder = binder - - private fun closeToReceiverToStartCast( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - val chipState = MoveCloserToStartCast( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) { - val chipState = MoveCloserToEndCast( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun transferFailed(mediaInfo: MediaRoute2Info) { - val chipState = TransferFailed( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString() - ) - controller.displayChip(chipState) - } - - private fun transferToReceiverTriggered( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo - ) { - val chipState = TransferToReceiverTriggered( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name - ) - controller.displayChip(chipState) - } - - private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) { - val chipState = TransferToThisDeviceTriggered( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString() - ) - controller.displayChip(chipState) - } - - private fun transferToReceiverSucceeded( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback - ) { - val chipState = TransferToReceiverSucceeded( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name, - undoCallback = undoCallback - ) - controller.displayChip(chipState) - } - - private fun transferToThisDeviceSucceeded( - mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback - ) { - val chipState = TransferToThisDeviceSucceeded( - appIconDrawable = fakeAppIconDrawable, - appIconContentDescription = mediaInfo.name.toString(), - otherDeviceName = otherDeviceInfo.name, - undoCallback = undoCallback - ) - controller.displayChip(chipState) - } - - private fun noLongerCloseToReceiver() { - controller.removeChip() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index d0eedac5127a..d16c019769b0 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; @@ -1401,7 +1401,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/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java index 9f585bdfaeb0..7cf63f678c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java @@ -19,7 +19,6 @@ import android.content.Context; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; import com.android.systemui.plugins.qs.QSTile; -import com.android.systemui.qs.external.TileServices; import java.util.Collection; @@ -35,7 +34,6 @@ public interface QSHost { Collection<QSTile> getTiles(); void addCallback(Callback callback); void removeCallback(Callback callback); - TileServices getTileServices(); void removeTile(String tileSpec); void removeTiles(Collection<String> specs); void unmarkTileAsAutoAdded(String tileSpec); 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/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index cca491343f76..c69307548b6e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -24,7 +24,6 @@ import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings.Secure; -import android.service.quicksettings.Tile; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; @@ -50,7 +49,6 @@ import com.android.systemui.qs.external.CustomTileStatePersister; import com.android.systemui.qs.external.TileLifecycleManager; import com.android.systemui.qs.external.TileServiceKey; import com.android.systemui.qs.external.TileServiceRequestController; -import com.android.systemui.qs.external.TileServices; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; @@ -89,7 +87,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private final Context mContext; private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>(); protected final ArrayList<String> mTileSpecs = new ArrayList<>(); - private final TileServices mServices; private final TunerService mTunerService; private final PluginManager mPluginManager; private final DumpManager mDumpManager; @@ -111,6 +108,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private SecureSettings mSecureSettings; private final TileServiceRequestController mTileServiceRequestController; + private TileLifecycleManager.Factory mTileLifeCycleManagerFactory; @Inject public QSTileHost(Context context, @@ -129,7 +127,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, - TileServiceRequestController.Builder tileServiceRequestControllerBuilder + TileServiceRequestController.Builder tileServiceRequestControllerBuilder, + TileLifecycleManager.Factory tileLifecycleManagerFactory ) { mIconController = iconController; mContext = context; @@ -141,9 +140,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mUiEventLogger = uiEventLogger; mBroadcastDispatcher = broadcastDispatcher; mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this); + mTileLifeCycleManagerFactory = tileLifecycleManagerFactory; mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID); - mServices = new TileServices(this, bgLooper, mBroadcastDispatcher, userTracker); mStatusBarOptional = statusBarOptional; mQsFactories.add(defaultFactory); @@ -177,7 +176,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mTiles.values().forEach(tile -> tile.destroy()); mAutoTiles.destroy(); mTunerService.removeTunable(this); - mServices.destroy(); mPluginManager.removePluginListener(this); mDumpManager.unregisterDumpable(TAG); mTileServiceRequestController.destroy(); @@ -257,11 +255,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D return mCurrentUser; } - @Override - public TileServices getTileServices() { - return mServices; - } - public int indexOf(String spec) { return mTileSpecs.indexOf(spec); } @@ -460,10 +453,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D if (!newTiles.contains(tileSpec)) { ComponentName component = CustomTile.getComponentFromSpec(tileSpec); Intent intent = new Intent().setComponent(component); - TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(), - mContext, mServices, new Tile(), intent, - new UserHandle(mCurrentUser), - mBroadcastDispatcher); + TileLifecycleManager lifecycleManager = mTileLifeCycleManagerFactory.create( + intent, new UserHandle(mCurrentUser)); lifecycleManager.onStopListening(); lifecycleManager.onTileRemoved(); mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser)); 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..5d4b3da58cf0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -28,14 +28,13 @@ import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.ReduceBrightColorsController; +import com.android.systemui.qs.external.QSExternalModule; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.ManagedProfileController; 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; @@ -49,7 +48,7 @@ import dagger.Provides; * Module for QS dependencies */ @Module(subcomponents = {QSFragmentComponent.class}, - includes = {MediaModule.class, QSFlagsModule.class}) + includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class}) public interface QSModule { @Provides @@ -91,9 +90,4 @@ public interface QSModule { /** */ @Binds QSHost provideQsHost(QSTileHost controllerImpl); - - /** */ - @Binds - RunningFgsController provideRunningFgsController( - RunningFgsControllerImpl runningFgsController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 4f15351322ee..c4386ab9a3df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -51,7 +51,6 @@ import androidx.annotation.WorkerThread; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.Dependency; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -103,6 +102,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener private final TileServiceKey mKey; private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false); + private final TileServices mTileServices; private CustomTile( QSHost host, @@ -115,10 +115,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener QSLogger qsLogger, String action, Context userContext, - CustomTileStatePersister customTileStatePersister + CustomTileStatePersister customTileStatePersister, + TileServices tileServices ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); + mTileServices = tileServices; mWindowManager = WindowManagerGlobal.getWindowManagerService(); mComponent = ComponentName.unflattenFromString(action); mTile = new Tile(); @@ -126,7 +128,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mUser = mUserContext.getUserId(); mKey = new TileServiceKey(mComponent, mUser); - mServiceManager = host.getTileServices().getTileWrapper(this); + mServiceManager = tileServices.getTileWrapper(this); mService = mServiceManager.getTileService(); mCustomTileStatePersister = customTileStatePersister; } @@ -349,7 +351,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } catch (RemoteException e) { } } - mHost.getTileServices().freeService(this, mServiceManager); + mTileServices.freeService(this, mServiceManager); } @Override @@ -473,7 +475,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } public void startUnlockAndRun() { - Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> { + mActivityStarter.postQSRunnableDismissingKeyguard(() -> { try { mService.onUnlockComplete(); } catch (RemoteException e) { @@ -529,6 +531,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener final ActivityStarter mActivityStarter; final QSLogger mQSLogger; final CustomTileStatePersister mCustomTileStatePersister; + private TileServices mTileServices; Context mUserContext; String mSpec = ""; @@ -543,7 +546,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - CustomTileStatePersister customTileStatePersister + CustomTileStatePersister customTileStatePersister, + TileServices tileServices ) { mQSHostLazy = hostLazy; mBackgroundLooper = backgroundLooper; @@ -554,6 +558,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mActivityStarter = activityStarter; mQSLogger = qsLogger; mCustomTileStatePersister = customTileStatePersister; + mTileServices = tileServices; } Builder setSpec(@NonNull String spec) { @@ -583,7 +588,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mQSLogger, action, mUserContext, - mCustomTileStatePersister + mCustomTileStatePersister, + mTileServices ); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java index ffe66f4c9aae..6cf4441cfc55 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.os.RemoteException; +import javax.inject.Inject; + // Adapter that wraps calls to PackageManager or IPackageManager for {@link TileLifecycleManager}. // TODO: This is very much an intermediate step to allow for PackageManager mocking and should be // abstracted into something more useful for other tests in systemui. @@ -37,6 +39,7 @@ public class PackageManagerAdapter { // Uses the PackageManager for the provided context. // When necessary, uses the IPackagemanger in AppGlobals. + @Inject public PackageManagerAdapter(Context context) { mPackageManager = context.getPackageManager(); mIPackageManager = AppGlobals.getPackageManager(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt index 861a4ed8eaf9..f7db80bddd3b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl +++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt @@ -14,6 +14,14 @@ * limitations under the License. */ -package com.android.systemui.shared.mediattt; +package com.android.systemui.qs.external -parcelable DeviceInfo; +import android.service.quicksettings.IQSService +import dagger.Binds +import dagger.Module + +@Module +interface QSExternalModule { + @Binds + fun bindsIQSService(impl: TileServices): IQSService +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index 17ae7ffa9980..32e0805b91ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -31,21 +31,24 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.quicksettings.IQSService; import android.service.quicksettings.IQSTileService; -import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; import android.util.ArraySet; import android.util.Log; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + /** * Manages the lifecycle of a TileService. * <p> @@ -102,16 +105,10 @@ public class TileLifecycleManager extends BroadcastReceiver implements // Return value from bindServiceAsUser, determines whether safe to call unbind. private boolean mIsBound; - public TileLifecycleManager(Handler handler, Context context, IQSService service, Tile tile, - Intent intent, UserHandle user, BroadcastDispatcher broadcastDispatcher) { - this(handler, context, service, tile, intent, user, new PackageManagerAdapter(context), - broadcastDispatcher); - } - - @VisibleForTesting - TileLifecycleManager(Handler handler, Context context, IQSService service, Tile tile, - Intent intent, UserHandle user, PackageManagerAdapter packageManagerAdapter, - BroadcastDispatcher broadcastDispatcher) { + @AssistedInject + TileLifecycleManager(@Main Handler handler, Context context, IQSService service, + PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher, + @Assisted Intent intent, @Assisted UserHandle user) { mContext = context; mHandler = handler; mIntent = intent; @@ -123,6 +120,13 @@ public class TileLifecycleManager extends BroadcastReceiver implements if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser); } + /** Injectable factory for TileLifecycleManager. */ + @AssistedFactory + public interface Factory { + /** */ + TileLifecycleManager create(Intent intent, UserHandle userHandle); + } + public ComponentName getComponent() { return mIntent.getComponent(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java index fda755bd5c33..bf565a8c52e0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java @@ -26,7 +26,6 @@ import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.service.quicksettings.IQSTileService; -import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; import android.util.Log; @@ -73,10 +72,11 @@ public class TileServiceManager { private boolean mStarted = false; TileServiceManager(TileServices tileServices, Handler handler, ComponentName component, - Tile tile, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { + BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { this(tileServices, handler, userTracker, new TileLifecycleManager(handler, - tileServices.getContext(), tileServices, tile, new Intent().setComponent(component), - userTracker.getUserHandle(), broadcastDispatcher)); + tileServices.getContext(), tileServices, + new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher, + new Intent().setComponent(component), userTracker.getUserHandle())); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 0a3c17c9045a..32515a258b46 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -38,8 +38,8 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.internal.statusbar.StatusBarIcon; -import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -50,6 +50,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.Objects; +import javax.inject.Inject; + /** * Runs the day-to-day operations of which tiles should be bound and when. */ @@ -65,14 +67,21 @@ public class TileServices extends IQSService.Stub { private final Handler mHandler; private final Handler mMainHandler; private final QSTileHost mHost; + private final KeyguardStateController mKeyguardStateController; private final BroadcastDispatcher mBroadcastDispatcher; private final UserTracker mUserTracker; private int mMaxBound = DEFAULT_MAX_BOUND; - public TileServices(QSTileHost host, Looper looper, BroadcastDispatcher broadcastDispatcher, - UserTracker userTracker) { + @Inject + public TileServices( + QSTileHost host, + @Main Looper looper, + BroadcastDispatcher broadcastDispatcher, + UserTracker userTracker, + KeyguardStateController keyguardStateController) { mHost = host; + mKeyguardStateController = keyguardStateController; mContext = mHost.getContext(); mBroadcastDispatcher = broadcastDispatcher; mHandler = new Handler(looper); @@ -96,8 +105,7 @@ public class TileServices extends IQSService.Stub { public TileServiceManager getTileWrapper(CustomTile tile) { ComponentName component = tile.getComponent(); - TileServiceManager service = onCreateTileService(component, tile.getQsTile(), - mBroadcastDispatcher); + TileServiceManager service = onCreateTileService(component, mBroadcastDispatcher); synchronized (mServices) { mServices.put(tile, service); mTiles.put(component, tile); @@ -108,9 +116,9 @@ public class TileServices extends IQSService.Stub { return service; } - protected TileServiceManager onCreateTileService(ComponentName component, Tile tile, + protected TileServiceManager onCreateTileService(ComponentName component, BroadcastDispatcher broadcastDispatcher) { - return new TileServiceManager(this, mHandler, component, tile, + return new TileServiceManager(this, mHandler, component, broadcastDispatcher, mUserTracker); } @@ -321,16 +329,12 @@ public class TileServices extends IQSService.Stub { @Override public boolean isLocked() { - KeyguardStateController keyguardStateController = - Dependency.get(KeyguardStateController.class); - return keyguardStateController.isShowing(); + return mKeyguardStateController.isShowing(); } @Override public boolean isSecure() { - KeyguardStateController keyguardStateController = - Dependency.get(KeyguardStateController.class); - return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing(); + return mKeyguardStateController.isMethodSecure() && mKeyguardStateController.isShowing(); } @Nullable 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/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java index 77c61a4f1845..597f7b7053a2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java @@ -112,8 +112,6 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation { final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get(); if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) { statusBarOptional.get().executeRunnableDismissingKeyguard(() -> { - // Flush trustmanager before checking device locked per user - mTrustManager.reportKeyguardShowingChanged(); mHandler.post(toggleRecents); }, null, true /* dismissShade */, false /* afterKeyguardGone */, true /* deferred */); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index becc3c7e036c..6b251b070036 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/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 83d8d19a6d4d..30456a8c4cc7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -344,7 +344,7 @@ public class ScreenshotController { }; mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter( ClipboardOverlayController.COPY_OVERLAY_ACTION), - ClipboardOverlayController.SELF_PERMISSION, null); + ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED); } void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 9d43d303b834..2f5eaa621283 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -43,6 +43,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IUdfpsHbmListener; import android.inputmethodservice.InputMethodService.BackDispositionMode; +import android.media.MediaRoute2Info; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -63,6 +64,7 @@ import androidx.annotation.NonNull; import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.GcUtils; import com.android.internal.view.AppearanceRegion; @@ -156,6 +158,8 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT; private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT; private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT; + private static final int MSG_MEDIA_TRANSFER_SENDER_STATE = 64 << MSG_SHIFT; + private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -439,6 +443,17 @@ public class CommandQueue extends IStatusBar.Stub implements * @see IStatusBar#cancelRequestAddTile */ default void cancelRequestAddTile(@NonNull String packageName) {} + + /** @see IStatusBar#updateMediaTapToTransferSenderDisplay */ + default void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable IUndoMediaTransferCallback undoCallback) {} + + /** @see IStatusBar#updateMediaTapToTransferReceiverDisplay */ + default void updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState int displayState, + @NonNull MediaRoute2Info routeInfo) {} } public CommandQueue(Context context) { @@ -1177,6 +1192,29 @@ public class CommandQueue extends IStatusBar.Stub implements mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_CANCEL, s).sendToTarget(); } + @Override + public void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + MediaRoute2Info routeInfo, + IUndoMediaTransferCallback undoCallback + ) throws RemoteException { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState; + args.arg2 = routeInfo; + args.arg3 = undoCallback; + mHandler.obtainMessage(MSG_MEDIA_TRANSFER_SENDER_STATE, args).sendToTarget(); + } + + @Override + public void updateMediaTapToTransferReceiverDisplay( + int displayState, + MediaRoute2Info routeInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = displayState; + args.arg2 = routeInfo; + mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget(); + } + private final class H extends Handler { private H(Looper l) { super(l); @@ -1574,6 +1612,29 @@ public class CommandQueue extends IStatusBar.Stub implements for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).cancelRequestAddTile(packageName); } + break; + case MSG_MEDIA_TRANSFER_SENDER_STATE: + args = (SomeArgs) msg.obj; + int displayState = (int) args.arg1; + MediaRoute2Info routeInfo = (MediaRoute2Info) args.arg2; + IUndoMediaTransferCallback undoCallback = + (IUndoMediaTransferCallback) args.arg3; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).updateMediaTapToTransferSenderDisplay( + displayState, routeInfo, undoCallback); + } + args.recycle(); + break; + case MSG_MEDIA_TRANSFER_RECEIVER_STATE: + args = (SomeArgs) msg.obj; + int receiverDisplayState = (int) args.arg1; + MediaRoute2Info receiverRouteInfo = (MediaRoute2Info) args.arg2; + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).updateMediaTapToTransferReceiverDisplay( + receiverDisplayState, receiverRouteInfo); + } + args.recycle(); + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 391525e11866..092e86daddb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -179,6 +179,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) { return; } + Trace.beginSection("NotificationViewHierarchyManager.updateNotificationViews"); beginUpdate(); @@ -353,6 +354,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle mListContainer.onNotificationViewUpdateFinished(); endUpdate(); + Trace.endSection(); } /** @@ -362,6 +364,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle * {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator} */ private void updateNotifStats() { + Trace.beginSection("NotificationViewHierarchyManager.updateNotifStats"); boolean hasNonClearableAlertingNotifs = false; boolean hasClearableAlertingNotifs = false; boolean hasNonClearableSilentNotifs = false; @@ -403,6 +406,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */, hasClearableSilentNotifs /* hasClearableSilentNotifs */ )); + Trace.endSection(); } /** @@ -520,7 +524,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } private void updateRowStatesInternal() { - Trace.beginSection("NotificationViewHierarchyManager#updateRowStates"); + Trace.beginSection("NotificationViewHierarchyManager.updateRowStates"); final int N = mListContainer.getContainerChildCount(); int visibleNotifications = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 5302188ccb31..4a7606c316e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -22,6 +22,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.app.ActivityManager; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -33,7 +34,6 @@ import android.graphics.Color; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; @@ -57,6 +57,7 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.notification.NotificationIconDozeHelper; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.util.drawable.DrawableSize; import java.text.NumberFormat; import java.util.Arrays; @@ -84,16 +85,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public static final int STATE_DOT = 1; public static final int STATE_HIDDEN = 2; - /** - * Maximum allowed byte count for an icon bitmap - * @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE - */ - private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB - /** - * Maximum allowed width or height for an icon drawable, if we can't get byte count - */ - private static final int MAX_IMAGE_SIZE = 5000; - private static final String TAG = "StatusBarIconView"; private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT = new FloatProperty<StatusBarIconView>("iconAppearAmount") { @@ -390,21 +381,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi return false; } - if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) { - // If it's a bitmap we can check the size directly - int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount(); - if (byteCount > MAX_BITMAP_SIZE) { - Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon); - return false; - } - } else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE - || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) { - // Otherwise, check dimensions - Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x" - + drawable.getIntrinsicHeight() + ") " + mIcon); - return false; - } - if (withClear) { setImageDrawable(null); } @@ -432,7 +408,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi * @return Drawable for this item, or null if the package or item could not * be found */ - public static Drawable getIcon(Context sysuiContext, + private Drawable getIcon(Context sysuiContext, Context context, StatusBarIcon statusBarIcon) { int userId = statusBarIcon.user.getIdentifier(); if (userId == UserHandle.USER_ALL) { @@ -446,6 +422,16 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi typedValue, true); float scaleFactor = typedValue.getFloat(); + // We downscale the loaded drawable to reasonable size to protect against applications + // using too much memory. The size can be tweaked in config.xml. Drawables + // that are already sized properly won't be touched. + boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); + Resources res = sysuiContext.getResources(); + int maxIconSize = res.getDimensionPixelSize(isLowRamDevice + ? com.android.internal.R.dimen.notification_small_icon_size_low_ram + : com.android.internal.R.dimen.notification_small_icon_size); + icon = DrawableSize.downscaleToSize(res, icon, maxIconSize, maxIconSize); + // No need to scale the icon, so return it as is. if (scaleFactor == 1.f) { return icon; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/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/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index c33160858d0f..ad9f12ec3bc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -24,6 +24,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; @@ -343,11 +344,14 @@ public class NotificationEntryManager implements private final InflationCallback mInflationCallback = new InflationCallback() { @Override public void handleInflationException(NotificationEntry entry, Exception e) { + Trace.beginSection("NotificationEntryManager.handleInflationException"); NotificationEntryManager.this.handleInflationException(entry.getSbn(), e); + Trace.endSection(); } @Override public void onAsyncInflationFinished(NotificationEntry entry) { + Trace.beginSection("NotificationEntryManager.onAsyncInflationFinished"); mPendingNotifications.remove(entry.getKey()); // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. @@ -369,6 +373,7 @@ public class NotificationEntryManager implements } } } + Trace.endSection(); } }; @@ -463,6 +468,7 @@ public class NotificationEntryManager implements boolean forceRemove, DismissedByUserStats dismissedByUserStats, int reason) { + Trace.beginSection("NotificationEntryManager.removeNotificationInternal"); final NotificationEntry entry = getActiveNotificationUnfiltered(key); @@ -470,6 +476,7 @@ public class NotificationEntryManager implements if (interceptor.onNotificationRemoveRequested(key, entry, reason)) { // Remove intercepted; log and skip mLogger.logRemovalIntercepted(key); + Trace.endSection(); return; } } @@ -557,6 +564,7 @@ public class NotificationEntryManager implements mLeakDetector.trackGarbage(entry); } } + Trace.endSection(); } private void sendNotificationRemovalToServer( @@ -620,6 +628,7 @@ public class NotificationEntryManager implements private void addNotificationInternal( StatusBarNotification notification, RankingMap rankingMap) throws InflationException { + Trace.beginSection("NotificationEntryManager.addNotificationInternal"); String key = notification.getKey(); if (DEBUG) { Log.d(TAG, "addNotification key=" + key); @@ -667,6 +676,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } public void addNotification(StatusBarNotification notification, RankingMap ranking) { @@ -679,12 +689,14 @@ public class NotificationEntryManager implements private void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) throws InflationException { + Trace.beginSection("NotificationEntryManager.updateNotificationInternal"); if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final String key = notification.getKey(); abortExistingInflation(key, "updateNotification"); final NotificationEntry entry = getActiveNotificationUnfiltered(key); if (entry == null) { + Trace.endSection(); return; } @@ -721,6 +733,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } public void updateNotification(StatusBarNotification notification, RankingMap ranking) { @@ -740,14 +753,17 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("updateNotifications", reason); return; } + Trace.beginSection("NotificationEntryManager.updateNotifications"); reapplyFilterAndSort(reason); if (mPresenter != null) { mPresenter.updateNotificationViews(reason); } mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications()); + Trace.endSection(); } public void updateNotificationRanking(RankingMap rankingMap) { + Trace.beginSection("NotificationEntryManager.updateNotificationRanking"); List<NotificationEntry> entries = new ArrayList<>(); entries.addAll(getVisibleNotifications()); entries.addAll(mPendingNotifications.values()); @@ -788,6 +804,7 @@ public class NotificationEntryManager implements for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } void notifyChannelModified( @@ -887,6 +904,7 @@ public class NotificationEntryManager implements /** @return list of active notifications filtered for the current user */ public List<NotificationEntry> getActiveNotificationsForCurrentUser() { + Trace.beginSection("NotificationEntryManager.getActiveNotificationsForCurrentUser"); Assert.isMainThread(); ArrayList<NotificationEntry> filtered = new ArrayList<>(); @@ -898,7 +916,7 @@ public class NotificationEntryManager implements } filtered.add(entry); } - + Trace.endSection(); return filtered; } @@ -908,10 +926,12 @@ public class NotificationEntryManager implements * @param reason the reason for calling this method, which will be logged */ public void updateRanking(RankingMap rankingMap, String reason) { + Trace.beginSection("NotificationEntryManager.updateRanking"); updateRankingAndSort(rankingMap, reason); for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onRankingApplied(); } + Trace.endSection(); } /** Resorts / filters the current notification set with the current RankingMap */ @@ -920,7 +940,9 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason); return; } + Trace.beginSection("NotificationEntryManager.reapplyFilterAndSort"); updateRankingAndSort(mRanker.getRankingMap(), reason); + Trace.endSection(); } /** Calls to NotificationRankingManager and updates mSortedAndFiltered */ @@ -929,9 +951,11 @@ public class NotificationEntryManager implements mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason); return; } + Trace.beginSection("NotificationEntryManager.updateRankingAndSort"); mSortedAndFiltered.clear(); mSortedAndFiltered.addAll(mRanker.updateRanking( rankingMap, mActiveNotifications.values(), reason)); + Trace.endSection(); } /** dump the current active notification list. Called from StatusBar */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/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 74c97fdbddca..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); } /** @@ -226,8 +230,13 @@ public class ShadeListBuilder implements Dumpable { mNotifSections.clear(); for (NotifSectioner sectioner : sectioners) { - mNotifSections.add(new NotifSection(sectioner, mNotifSections.size())); + final NotifSection section = new NotifSection(sectioner, mNotifSections.size()); + final NotifComparator sectionComparator = section.getComparator(); + mNotifSections.add(section); sectioner.setInvalidationListener(this::onNotifSectionInvalidated); + if (sectionComparator != null) { + sectionComparator.setInvalidationListener(this::onNotifComparatorInvalidated); + } } mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); @@ -285,7 +294,7 @@ public class ShadeListBuilder implements Dumpable { mLogger.logOnBuildList(); mAllEntries = entries; - buildList(); + mChoreographer.schedule(); } }; @@ -426,14 +435,18 @@ public class ShadeListBuilder implements Dumpable { } Trace.endSection(); + Trace.beginSection("ShadeListBuilder.logEndBuildList"); // Step 9: We're done! mLogger.logEndBuildList( mIterationCount, mReadOnlyNotifList.size(), countChildren(mReadOnlyNotifList)); if (mAlwaysLogList || mIterationCount % 10 == 0) { + Trace.beginSection("ShadeListBuilder.logFinalList"); mLogger.logFinalList(mNotifList); + Trace.endSection(); } + Trace.endSection(); mPipelineState.setState(STATE_IDLE); mIterationCount++; Trace.endSection(); @@ -996,16 +1009,20 @@ public class ShadeListBuilder implements Dumpable { } private void freeEmptyGroups() { + Trace.beginSection("ShadeListBuilder.freeEmptyGroups"); mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty()); + Trace.endSection(); } private void logChanges() { + Trace.beginSection("ShadeListBuilder.logChanges"); for (NotificationEntry entry : mAllEntries) { logAttachStateChanges(entry); } for (GroupEntry group : mGroups.values()) { logAttachStateChanges(group); } + Trace.endSection(); } private void logAttachStateChanges(ListEntry entry) { @@ -1083,16 +1100,23 @@ public class ShadeListBuilder implements Dumpable { } private void cleanupPluggables() { + Trace.beginSection("ShadeListBuilder.cleanupPluggables"); callOnCleanup(mNotifPreGroupFilters); callOnCleanup(mNotifPromoters); callOnCleanup(mNotifFinalizeFilters); callOnCleanup(mNotifComparators); for (int i = 0; i < mNotifSections.size(); i++) { - mNotifSections.get(i).getSectioner().onCleanup(); + final NotifSection notifSection = mNotifSections.get(i); + notifSection.getSectioner().onCleanup(); + final NotifComparator comparator = notifSection.getComparator(); + if (comparator != null) { + comparator.onCleanup(); + } } callOnCleanup(List.of(getStabilityManager())); + Trace.endSection(); } private void callOnCleanup(List<? extends Pluggable<?>> pluggables) { @@ -1101,6 +1125,19 @@ public class ShadeListBuilder implements Dumpable { } } + @Nullable + private NotifComparator getSectionComparator( + @NonNull ListEntry o1, @NonNull ListEntry o2) { + final NotifSection section = o1.getSection(); + if (section != o2.getSection()) { + throw new RuntimeException("Entry ordering should only be done within sections"); + } + if (section != null) { + return section.getComparator(); + } + return null; + } + private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> { int cmp = Integer.compare( o1.getSectionIndex(), @@ -1112,6 +1149,12 @@ public class ShadeListBuilder implements Dumpable { cmp = Integer.compare(index1, index2); if (cmp != 0) return cmp; + NotifComparator sectionComparator = getSectionComparator(o1, o2); + if (sectionComparator != null) { + cmp = sectionComparator.compare(o1, o2); + if (cmp != 0) return cmp; + } + for (int i = 0; i < mNotifComparators.size(); i++) { cmp = mNotifComparators.get(i).compare(o1, o2); if (cmp != 0) return cmp; @@ -1242,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/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index ba88ad7844f1..a390e9f9b09d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -51,21 +51,18 @@ class ConversationCoordinator @Inject constructor( val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean = isConversation(entry) - override fun getHeaderNodeController() = - // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController - if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null - } - val comparator = object : NotifComparator("People") { - override fun compare(entry1: ListEntry, entry2: ListEntry): Int { - assert(entry1.section === entry2.section) - if (entry1.section?.sectioner !== sectioner) { - return 0 + override fun getComparator() = object : NotifComparator("People") { + override fun compare(entry1: ListEntry, entry2: ListEntry): Int { + val type1 = getPeopleType(entry1) + val type2 = getPeopleType(entry2) + return type2.compareTo(type1) } - val type1 = getPeopleType(entry1) - val type2 = getPeopleType(entry2) - return type2.compareTo(type1) } + + override fun getHeaderNodeController() = + // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController + if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null } override fun attach(pipeline: NotifPipeline) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 031132424115..41b070635d4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener @@ -456,6 +457,13 @@ class HeadsUpCoordinator @Inject constructor( // TODO: This check won't notice if a child of the group is going to HUN... isGoingToShowHunNoRetract(entry) + override fun getComparator(): NotifComparator { + return object : NotifComparator("HeadsUp") { + override fun compare(o1: ListEntry, o2: ListEntry): Int = + mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry) + } + } + override fun getHeaderNodeController(): NodeController? = // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 850cb4b88154..757fb5a2fe9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -20,7 +20,6 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import java.io.FileDescriptor import java.io.PrintWriter @@ -64,7 +63,6 @@ class NotifCoordinatorsImpl @Inject constructor( private val mCoordinators: MutableList<Coordinator> = ArrayList() private val mOrderedSections: MutableList<NotifSectioner> = ArrayList() - private val mOrderedComparators: MutableList<NotifComparator> = ArrayList() /** * Creates all the coordinators. @@ -119,9 +117,6 @@ class NotifCoordinatorsImpl @Inject constructor( mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized - - // Manually add ordered comparators - mOrderedComparators.add(conversationCoordinator.comparator) } /** @@ -133,7 +128,6 @@ class NotifCoordinatorsImpl @Inject constructor( c.attach(pipeline) } pipeline.setSections(mOrderedSections) - pipeline.setComparators(mOrderedComparators) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index c6a8a69cfb0d..1c96e8ceb27f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -23,6 +23,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -38,10 +39,11 @@ class StackCoordinator @Inject internal constructor( pipeline.addOnAfterRenderListListener(::onAfterRenderList) } - fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) { - controller.setNotifStats(calculateNotifStats(entries)) - notificationIconAreaController.updateNotificationIcons(entries) - } + fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) = + traceSection("StackCoordinator.onAfterRenderList") { + controller.setNotifStats(calculateNotifStats(entries)) + notificationIconAreaController.updateNotificationIcons(entries) + } private fun calculateNotifStats(entries: List<ListEntry>): NotifStats { var hasNonClearableAlertingNotifs = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt index 8444287cbf60..263737e20a13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.listbuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.stack.PriorityBucket @@ -29,5 +30,7 @@ data class NotifSection( val headerController: NodeController? = sectioner.headerNodeController + val comparator: NotifComparator? = sectioner.comparator + @PriorityBucket val bucket: Int = sectioner.bucket } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java index ef9ee11ef116..8c52c53ea6b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java @@ -55,8 +55,22 @@ public abstract class NotifSectioner extends Pluggable<NotifSectioner> { public abstract boolean isInSection(ListEntry entry); /** + * Returns an optional {@link NotifComparator} to sort entries only in this section. + * {@link ListEntry} instances passed to this comparator are guaranteed to have this section, + * and this ordering will take precedence over any global comparators supplied to {@link + * com.android.systemui.statusbar.notification.collection.NotifPipeline#setComparators(List)}. + * + * NOTE: this method is only called once when the Sectioner is attached. + */ + public @Nullable NotifComparator getComparator() { + return null; + } + + /** * Returns an optional {@link NodeSpec} for the section header. If {@code null}, no header will * be used for the section. + * + * NOTE: this method is only called once when the Sectioner is attached. */ public @Nullable NodeController getHeaderNodeController() { return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt index 4de8e7a5641c..b76169f111db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.render import android.view.View +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationListContainer @@ -41,6 +42,7 @@ class RootNodeController( override fun addChildAt(child: NodeController, index: Int) { listContainer.addContainerViewAt(child.view, index) listContainer.onNotificationViewUpdateFinished() + (child.view as? ExpandableNotificationRow)?.isChangingPosition = false } override fun moveChildTo(child: NodeController, index: Int) { @@ -50,6 +52,7 @@ class RootNodeController( override fun removeChild(child: NodeController, isTransfer: Boolean) { if (isTransfer) { listContainer.setChildTransferInProgress(true) + (child.view as? ExpandableNotificationRow)?.isChangingPosition = true } listContainer.removeContainerView(child.view) if (isTransfer) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/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/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index b02dc0cffdb9..54e26c34522d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.children import com.android.systemui.util.foldToSparseArray import com.android.systemui.util.takeUntil +import com.android.systemui.util.traceSection import javax.inject.Inject /** @@ -157,7 +158,9 @@ class NotificationSectionsManager @Inject internal constructor( } } } - private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild) + private fun logShadeContents() = traceSection("NotifSectionsManager.logShadeContents") { + parent.children.forEachIndexed(::logShadeChild) + } private val isUsingMultipleSections: Boolean get() = sectionsFeatureManager.getNumberOfBuckets() > 1 @@ -221,10 +224,10 @@ class NotificationSectionsManager @Inject internal constructor( * Should be called whenever notifs are added, removed, or updated. Updates section boundary * bookkeeping and adds/moves/removes section headers if appropriate. */ - fun updateSectionBoundaries(reason: String) { + fun updateSectionBoundaries(reason: String) = traceSection("NotifSectionsManager.update") { notifPipelineFlags.checkLegacyPipelineEnabled() if (!isUsingMultipleSections) { - return + return@traceSection } logger.logStartSectionUpdate(reason) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index dd72615768b0..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 @@ -43,7 +43,6 @@ import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; -import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -204,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 */ @@ -696,8 +698,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable && mQsExpansionFraction != 1 && !mScreenOffAnimationController.shouldHideNotificationsFooter() && !mIsRemoteInputActive; - boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + boolean showHistory = mController.isHistoryEnabled(); updateFooterView(showFooterView, showDismissView, showHistory); } @@ -1272,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? */ @@ -1285,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); @@ -1327,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; @@ -1370,7 +1386,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } } else { - stackHeight = (int) height; + stackHeight = (int) (skipHeightUpdate ? mExpandedHeight : height); } } else { appearFraction = calculateAppearFraction(height); @@ -1388,7 +1404,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } mAmbientState.setAppearFraction(appearFraction); - if (stackHeight != mCurrentStackHeight) { + if (stackHeight != mCurrentStackHeight && !skipHeightUpdate) { mCurrentStackHeight = stackHeight; updateAlgorithmHeightAndPadding(); requestChildrenUpdate(); @@ -5003,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; @@ -5243,11 +5266,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable R.layout.status_bar_no_notifications, this, false); view.setText(R.string.empty_shade_text); view.setOnClickListener(v -> { - final boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; - Intent intent = showHistory ? new Intent( - Settings.ACTION_NOTIFICATION_HISTORY) : new Intent( - Settings.ACTION_NOTIFICATION_SETTINGS); + final boolean showHistory = mController.isHistoryEnabled(); + Intent intent = showHistory + ? new Intent(Settings.ACTION_NOTIFICATION_HISTORY) + : new Intent(Settings.ACTION_NOTIFICATION_SETTINGS); mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP); }); setEmptyShadeView(view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 334128a2b4ca..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 @@ -35,6 +35,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; +import android.os.Trace; +import android.os.UserHandle; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; @@ -180,7 +182,6 @@ public class NotificationStackScrollLayoutController { private final NotificationLockscreenUserManager mLockscreenUserManager; // TODO: StatusBar should be encapsulated behind a Controller private final StatusBar mStatusBar; - private final NotificationGroupManagerLegacy mLegacyGroupManager; private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; @@ -191,6 +192,7 @@ public class NotificationStackScrollLayoutController { private boolean mFadeNotificationsOnDismiss; private NotificationSwipeHelper mSwipeHelper; private boolean mShowEmptyShadeView; + @Nullable private Boolean mHistoryEnabled; private int mBarState; private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -340,6 +342,8 @@ public class NotificationStackScrollLayoutController { @Override public void onUserChanged(int userId) { mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode()); + mHistoryEnabled = null; + updateFooter(); } }; @@ -699,8 +703,6 @@ public class NotificationStackScrollLayoutController { } }); mNotifPipelineFlags = notifPipelineFlags; - mLegacyGroupManager = mNotifPipelineFlags.isNewPipelineEnabled() - ? null : legacyGroupManager; mSilentHeaderController = silentHeaderController; mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; @@ -802,6 +804,7 @@ public class NotificationStackScrollLayoutController { (key, newValue) -> { switch (key) { case Settings.Secure.NOTIFICATION_HISTORY_ENABLED: + mHistoryEnabled = null; // invalidate updateFooter(); break; case HIGH_PRIORITY: @@ -1009,6 +1012,20 @@ public class NotificationStackScrollLayoutController { return mNotifStats.getNumActiveNotifs(); } + public boolean isHistoryEnabled() { + Boolean historyEnabled = mHistoryEnabled; + if (historyEnabled == null) { + if (mView == null || mView.getContext() == null) { + Log.wtf(TAG, "isHistoryEnabled failed to initialize its value"); + return false; + } + mHistoryEnabled = historyEnabled = + Settings.Secure.getIntForUser(mView.getContext().getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + } + return historyEnabled; + } + public int getIntrinsicContentHeight() { return mView.getIntrinsicContentHeight(); } @@ -1178,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(); } @@ -1199,6 +1223,7 @@ public class NotificationStackScrollLayoutController { * are true. */ public void updateShowEmptyShadeView() { + Trace.beginSection("NSSLC.updateShowEmptyShadeView"); mShowEmptyShadeView = mBarState != KEYGUARD && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade()) && getVisibleNotificationCount() == 0; @@ -1206,6 +1231,7 @@ public class NotificationStackScrollLayoutController { mView.updateEmptyShadeView( mShowEmptyShadeView, mZenModeController.areNotificationsHiddenInShade()); + Trace.endSection(); } public boolean areNotificationsHiddenInShade() { @@ -1323,11 +1349,15 @@ public class NotificationStackScrollLayoutController { if (mNotifPipelineFlags.isNewPipelineEnabled()) { return; } + Trace.beginSection("NSSLC.updateSectionBoundaries"); mView.updateSectionBoundaries(reason); + Trace.endSection(); } public void updateFooter() { + Trace.beginSection("NSSLC.updateFooter"); mView.updateFooter(); + Trace.endSection(); } public void onUpdateRowStates() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/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/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 3084a957aeb5..784a54681484 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -350,11 +350,14 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * @return -1 if the first argument should be ranked higher than the second, 1 if the second * one should be ranked higher and 0 if they are equal. */ - public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) { + public int compare(@Nullable NotificationEntry a, @Nullable NotificationEntry b) { + if (a == null || b == null) { + return Boolean.compare(a == null, b == null); + } AlertEntry aEntry = getHeadsUpEntry(a.getKey()); AlertEntry bEntry = getHeadsUpEntry(b.getKey()); if (aEntry == null || bEntry == null) { - return aEntry == null ? 1 : -1; + return Boolean.compare(aEntry == null, bEntry == null); } return aEntry.compareTo(bEntry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt deleted file mode 100644 index c6dbdb1b09a5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.policy - -/** - * Interface for tracking packages with running foreground services and demoting foreground status - */ -interface RunningFgsController : CallbackController<RunningFgsController.Callback> { - - /** - * @return A list of [UserPackageTime]s which have running foreground service(s) - */ - fun getPackagesWithFgs(): List<UserPackageTime> - - /** - * Stops all foreground services running as a package - * @param userId the userId the package is running under - * @param packageName the packageName - */ - fun stopFgs(userId: Int, packageName: String) - - /** - * Returns when the list of packages with foreground services changes - */ - interface Callback { - /** - * The thing that - * @param packages the list of packages - */ - fun onFgsPackagesChanged(packages: List<UserPackageTime>) - } - - /** - * A triplet <user, packageName, timeMillis> where each element is a package running - * under a user that has had at least one foreground service running since timeMillis. - * Time should be derived from [SystemClock.elapsedRealtime]. - */ - data class UserPackageTime(val userId: Int, val packageName: String, val startTimeMillis: Long) -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt deleted file mode 100644 index 6d3345df4f3e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.policy - -import android.app.IActivityManager -import android.app.IForegroundServiceObserver -import android.os.IBinder -import android.os.RemoteException -import android.util.Log -import androidx.annotation.GuardedBy -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.policy.RunningFgsController.Callback -import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime -import com.android.systemui.util.time.SystemClock -import java.util.concurrent.Executor -import javax.inject.Inject - -/** - * Implementation for [RunningFgsController] - */ -@SysUISingleton -class RunningFgsControllerImpl @Inject constructor( - @Background private val executor: Executor, - private val systemClock: SystemClock, - private val activityManager: IActivityManager -) : RunningFgsController, IForegroundServiceObserver.Stub() { - - companion object { - private val LOG_TAG = RunningFgsControllerImpl::class.java.simpleName - } - - private val lock = Any() - - @GuardedBy("lock") - var initialized = false - - @GuardedBy("lock") - private val runningServiceTokens = mutableMapOf<UserPackageKey, StartTimeAndTokensValue>() - - @GuardedBy("lock") - private val callbacks = mutableSetOf<Callback>() - - fun init() { - synchronized(lock) { - if (initialized) { - return - } - try { - activityManager.registerForegroundServiceObserver(this) - } catch (e: RemoteException) { - e.rethrowFromSystemServer() - } - - initialized = true - } - } - - override fun addCallback(listener: Callback) { - init() - synchronized(lock) { callbacks.add(listener) } - } - - override fun removeCallback(listener: Callback) { - init() - synchronized(lock) { - if (!callbacks.remove(listener)) { - Log.e(LOG_TAG, "Callback was not registered.", RuntimeException()) - } - } - } - - override fun observe(lifecycle: Lifecycle?, listener: Callback?): Callback { - init() - return super.observe(lifecycle, listener) - } - - override fun observe(owner: LifecycleOwner?, listener: Callback?): Callback { - init() - return super.observe(owner, listener) - } - - override fun getPackagesWithFgs(): List<UserPackageTime> { - init() - return synchronized(lock) { getPackagesWithFgsLocked() } - } - - private fun getPackagesWithFgsLocked(): List<UserPackageTime> = - runningServiceTokens.map { - UserPackageTime(it.key.userId, it.key.packageName, it.value.fgsStartTime) - } - - override fun stopFgs(userId: Int, packageName: String) { - init() - try { - activityManager.stopAppForUser(packageName, userId) - } catch (e: RemoteException) { - e.rethrowFromSystemServer() - } - } - - private data class UserPackageKey( - val userId: Int, - val packageName: String - ) - - private class StartTimeAndTokensValue(systemClock: SystemClock) { - val fgsStartTime = systemClock.elapsedRealtime() - var tokens = mutableSetOf<IBinder>() - fun addToken(token: IBinder): Boolean { - return tokens.add(token) - } - - fun removeToken(token: IBinder): Boolean { - return tokens.remove(token) - } - - val isEmpty: Boolean - get() = tokens.isEmpty() - } - - override fun onForegroundStateChanged( - token: IBinder, - packageName: String, - userId: Int, - isForeground: Boolean - ) { - val result = synchronized(lock) { - val userPackageKey = UserPackageKey(userId, packageName) - if (isForeground) { - var addedNew = false - runningServiceTokens.getOrPut(userPackageKey) { - addedNew = true - StartTimeAndTokensValue(systemClock) - }.addToken(token) - if (!addedNew) { - return - } - } else { - val startTimeAndTokensValue = runningServiceTokens[userPackageKey] - if (startTimeAndTokensValue?.removeToken(token) == false) { - Log.e(LOG_TAG, - "Stopped foreground service was not known to be running.") - return - } - if (!startTimeAndTokensValue!!.isEmpty) { - return - } - runningServiceTokens.remove(userPackageKey) - } - getPackagesWithFgsLocked().toList() - } - - callbacks.forEach { executor.execute { it.onFgsPackagesChanged(result) } } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/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/src/com/android/systemui/util/leak/DumpTruck.java b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java index 089650c0fc54..82153600e473 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java @@ -50,7 +50,7 @@ public class DumpTruck { private static final int BUFSIZ = 1024 * 1024; // 1MB private final Context context; - private GarbageMonitor mGarbageMonitor; + private final GarbageMonitor mGarbageMonitor; private Uri hprofUri; private long rss; final StringBuilder body = new StringBuilder(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt index b951345a145b..61e78f5cb2fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt @@ -43,7 +43,7 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { @Before fun setUp() { dialogLaunchAnimator = DialogLaunchAnimator( - dreamManager, launchAnimator, isForTesting = true) + dreamManager, launchAnimator, forceDisableSynchronization = true) } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/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/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index d5bd67adcf09..8adb55b8d6e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -36,7 +36,9 @@ import androidx.lifecycle.LifecycleRegistry; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; import com.android.systemui.util.concurrency.FakeExecutor; @@ -50,6 +52,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayServiceTest extends SysuiTestCase { @@ -94,11 +100,13 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock DreamOverlayStateController mStateController; + @Mock + DreamBackend mDreamBackend; DreamOverlayService mService; @Before - public void setup() throws Exception { + public void setup() { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(WindowManager.class, mWindowManager); @@ -110,6 +118,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { .thenReturn(mLifecycleRegistry); when(mDreamOverlayComponent.getDreamOverlayTouchMonitor()) .thenReturn(mDreamOverlayTouchMonitor); + when(mDreamOverlayComponent.getDreamBackend()) + .thenReturn(mDreamBackend); when(mDreamOverlayComponentFactory .create(any(), any())) .thenReturn(mDreamOverlayComponent); @@ -120,28 +130,34 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor); + } + + @Test + public void testOverlayContainerViewAddedToWindow() throws Exception { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); mMainExecutor.runAllReady(); - } - @Test - public void testOverlayContainerViewAddedToWindow() { verify(mWindowManager).addView(any(), any()); } @Test - public void testDreamOverlayContainerViewControllerInitialized() { + public void testDreamOverlayContainerViewControllerInitialized() throws Exception { + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + // Inform the overlay service of dream starting. + overlay.startDream(mWindowParams, mDreamOverlayCallback); + mMainExecutor.runAllReady(); + verify(mDreamOverlayContainerViewController).init(); } @Test public void testShouldShowComplicationsTrueByDefault() { - assertThat(mService.shouldShowComplications()).isTrue(); - mService.onBind(new Intent()); assertThat(mService.shouldShowComplications()).isTrue(); @@ -155,4 +171,24 @@ public class DreamOverlayServiceTest extends SysuiTestCase { assertThat(mService.shouldShowComplications()).isFalse(); } + + @Test + public void testSetAvailableComplicationTypes() throws Exception { + final Set<Integer> enabledComplications = new HashSet<>( + Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME, + DreamBackend.COMPLICATION_TYPE_DATE, + DreamBackend.COMPLICATION_TYPE_WEATHER)); + when(mDreamBackend.getEnabledComplications()).thenReturn(enabledComplications); + + final IBinder proxy = mService.onBind(new Intent()); + final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); + + overlay.startDream(mWindowParams, mDreamOverlayCallback); + mMainExecutor.runAllReady(); + + final int expectedTypes = + Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE + | Complication.COMPLICATION_TYPE_WEATHER; + verify(mStateController).setAvailableComplicationTypes(expectedTypes); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java index f1978b214594..365c5291b118 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java @@ -22,6 +22,7 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME; import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER; import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType; +import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationTypes; import static com.google.common.truth.Truth.assertThat; @@ -36,6 +37,11 @@ import com.android.systemui.SysuiTestCase; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + @SmallTest @RunWith(AndroidTestingRunner.class) public class ComplicationUtilsTest extends SysuiTestCase { @@ -52,4 +58,37 @@ public class ComplicationUtilsTest extends SysuiTestCase { assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO)) .isEqualTo(COMPLICATION_TYPE_CAST_INFO); } + + @Test + public void testConvertComplicationTypesEmpty() { + final Set<Integer> input = new HashSet<>(); + final int expected = Complication.COMPLICATION_TYPE_NONE; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } + + @Test + public void testConvertComplicationTypesSingleValue() { + final Set<Integer> input = new HashSet<>( + Collections.singleton(DreamBackend.COMPLICATION_TYPE_WEATHER)); + final int expected = Complication.COMPLICATION_TYPE_WEATHER; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } + + @Test + public void testConvertComplicationTypesSingleValueMultipleValues() { + final Set<Integer> input = new HashSet<>( + Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME, + DreamBackend.COMPLICATION_TYPE_DATE, + DreamBackend.COMPLICATION_TYPE_WEATHER, + DreamBackend.COMPLICATION_TYPE_AIR_QUALITY, + DreamBackend.COMPLICATION_TYPE_CAST_INFO)); + final int expected = + Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE + | Complication.COMPLICATION_TYPE_WEATHER | COMPLICATION_TYPE_AIR_QUALITY + | COMPLICATION_TYPE_CAST_INFO; + + assertThat(convertComplicationTypes(input)).isEqualTo(expected); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/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/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d94e2eee9ffa..210cb82e1606 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -46,6 +46,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dreams.DreamOverlayStateController; @@ -100,6 +101,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ScreenOnCoordinator mScreenOnCoordinator; private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy; private @Mock DreamOverlayStateController mDreamOverlayStateController; + private @Mock ActivityLaunchAnimator mActivityLaunchAnimator; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -205,7 +207,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, - mNotificationShadeWindowControllerLazy); + mNotificationShadeWindowControllerLazy, + () -> mActivityLaunchAnimator); mViewMediator.start(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/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 81ae2093c830..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,27 +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.MediaTttSenderService -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService +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 @@ -53,25 +59,17 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper @Mock - private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver - @Mock - private lateinit var mediaSenderService: IDeviceSenderService.Stub - private lateinit var mediaSenderServiceComponentName: ComponentName + private lateinit var statusBarManager: StatusBarManager @Before fun setUp() { MockitoAnnotations.initMocks(this) - - mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java) - context.addMockService(mediaSenderServiceComponentName, mediaSenderService) - whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService) - whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService) - + context.addMockSystemService(Context.STATUS_BAR_SERVICE, statusBarManager) mediaTttCommandLineHelper = MediaTttCommandLineHelper( commandRegistry, context, - mediaTttChipControllerReceiver, + FakeExecutor(FakeSystemClock()), ) } @@ -83,174 +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() } - } - - @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() } + commandRegistry.registerCommand(RECEIVER_COMMAND) { EmptyCommand() } } @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)) + fun receiver_farFromSender_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE)) - verify(mediaTttChipControllerReceiver).removeChip() - } - - private fun getMoveCloserToStartCastCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - MOVE_CLOSER_TO_START_CAST_COMMAND_NAME - ) - - private fun getMoveCloserToEndCastCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - MOVE_CLOSER_TO_END_CAST_COMMAND_NAME - ) - - private fun getTransferToReceiverTriggeredCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME - ) - - private fun getTransferToThisDeviceTriggeredCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME - ) - - private fun getTransferToReceiverSucceededCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME - ) - - private fun getTransferToThisDeviceSucceededCommand(): Array<String> = - arrayOf( - SENDER_COMMAND, - DEVICE_NAME, - TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME + 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 6b4eebe680f9..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,17 +16,20 @@ 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 import android.widget.LinearLayout import android.widget.TextView import androidx.test.filters.SmallTest +import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.shared.mediattt.IUndoTransferCallback +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -34,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 @@ -46,17 +50,150 @@ class MediaTttChipControllerSenderTest : 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) appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) - controllerSender = MediaTttChipControllerSender(context, windowManager) + 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() @@ -69,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() @@ -133,7 +270,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) @@ -146,7 +283,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() { var undoCallbackCalled = false - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() { undoCallbackCalled = true } @@ -160,7 +297,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToReceiverSucceeded(undoCallback)) @@ -194,7 +331,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) @@ -207,7 +344,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() { var undoCallbackCalled = false - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() { undoCallbackCalled = true } @@ -221,7 +358,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { @Test fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() { - val undoCallback = object : IUndoTransferCallback.Stub() { + val undoCallback = object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback)) @@ -247,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) @@ -267,7 +404,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(transferToReceiverTriggered()) controllerSender.displayChip( transferToReceiverSucceeded( - object : IUndoTransferCallback.Stub() { + object : IUndoMediaTransferCallback.Stub() { override fun onUndoTriggered() {} } ) @@ -277,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) } @@ -311,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() = @@ -327,13 +464,13 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC) /** Helper method providing default parameters to not clutter up the tests. */ - private fun transferToReceiverSucceeded(undoCallback: IUndoTransferCallback? = null) = + private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToReceiverSucceeded( appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback ) /** Helper method providing default parameters to not clutter up the tests. */ - private fun transferToThisDeviceSucceeded(undoCallback: IUndoTransferCallback? = null) = + private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToThisDeviceSucceeded( appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback ) @@ -344,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/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt deleted file mode 100644 index 64542cb2f647..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.android.systemui.media.taptotransfer.sender - -import android.media.MediaRoute2Info -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.shared.mediattt.DeviceInfo -import com.android.systemui.shared.mediattt.IDeviceSenderService -import com.android.systemui.shared.mediattt.IUndoTransferCallback -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.capture -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.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@Ignore("b/216286227") -class MediaTttSenderServiceTest : SysuiTestCase() { - - private lateinit var service: IDeviceSenderService - - @Mock - private lateinit var controller: MediaTttChipControllerSender - - private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name") - .addFeature("feature") - .build() - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - val mediaTttSenderService = MediaTttSenderService(context, controller) - service = IDeviceSenderService.Stub.asInterface(mediaTttSenderService.onBind(null)) - } - - @Test - fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() { - service.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).displayChip(any<TransferToThisDeviceTriggered>()) - } - - @Test - fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() { - val name = "Fake name" - service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name)) - - val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - } - - @Test - fun transferToReceiverSucceeded_controllerTriggeredWithCorrectState() { - val name = "Fake name" - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() {} - } - service.transferToReceiverSucceeded(mediaInfo, DeviceInfo(name), undoCallback) - - val chipStateCaptor = argumentCaptor<TransferToReceiverSucceeded>() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.getChipTextString(context)).contains(name) - assertThat(chipState.undoCallback).isEqualTo(undoCallback) - } - - @Test - fun transferToThisDeviceSucceeded_controllerTriggeredWithCorrectState() { - val undoCallback = object : IUndoTransferCallback.Stub() { - override fun onUndoTriggered() {} - } - service.transferToThisDeviceSucceeded(mediaInfo, DeviceInfo("name"), undoCallback) - - val chipStateCaptor = argumentCaptor<TransferToThisDeviceSucceeded>() - verify(controller).displayChip(capture(chipStateCaptor)) - - val chipState = chipStateCaptor.value!! - assertThat(chipState.undoCallback).isEqualTo(undoCallback) - } - - @Test - fun transferFailed_controllerTriggeredWithTransferFailedState() { - service.transferFailed(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).displayChip(any<TransferFailed>()) - } - - @Test - fun noLongerCloseToReceiver_controllerRemoveChipTriggered() { - service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name")) - - verify(controller).removeChip() - } -} 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 7d3015cca64f..090ce436340b 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; @@ -289,20 +290,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 @@ -320,6 +327,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. @@ -328,6 +337,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. @@ -337,6 +348,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/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 8b353d94e25d..3266d6ac84a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -47,6 +47,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.dagger.QSFragmentComponent; import com.android.systemui.qs.external.CustomTileStatePersister; +import com.android.systemui.qs.external.TileLifecycleManager; import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSFactoryImpl; @@ -136,6 +137,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { QSFragment qs = (QSFragment) mFragment; mFragments.dispatchResume(); processAllMessages(); + QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class), mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(), mock(PluginManager.class), mock(TunerService.class), @@ -143,7 +145,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)), mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class), mock(SecureSettings.class), mock(CustomTileStatePersister.class), - mTileServiceRequestControllerBuilder); + mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class)); qs.setHost(host); qs.setListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 1e651bef318b..8872e28647a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -34,6 +34,7 @@ import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Looper; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -50,13 +51,13 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.external.CustomTileStatePersister; +import com.android.systemui.qs.external.TileLifecycleManager; import com.android.systemui.qs.external.TileServiceKey; import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.logging.QSLogger; @@ -127,6 +128,10 @@ public class QSTileHostTest extends SysuiTestCase { private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder; @Mock private TileServiceRequestController mTileServiceRequestController; + @Mock + private TileLifecycleManager.Factory mTileLifecycleManagerFactory; + @Mock + private TileLifecycleManager mTileLifecycleManager; private Handler mHandler; private TestableLooper mLooper; @@ -139,6 +144,8 @@ public class QSTileHostTest extends SysuiTestCase { mHandler = new Handler(mLooper.getLooper()); when(mTileServiceRequestControllerBuilder.create(any())) .thenReturn(mTileServiceRequestController); + when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class))) + .thenReturn(mTileLifecycleManager); mSecureSettings = new FakeSettings(); mSecureSettings.putStringForUser( @@ -146,7 +153,8 @@ public class QSTileHostTest extends SysuiTestCase { mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, - mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder); + mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder, + mTileLifecycleManagerFactory); setUpTileFactory(); } @@ -432,11 +440,13 @@ public class QSTileHostTest extends SysuiTestCase { BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, - TileServiceRequestController.Builder tileServiceRequestControllerBuilder) { + TileServiceRequestController.Builder tileServiceRequestControllerBuilder, + TileLifecycleManager.Factory tileLifecycleManagerFactory) { super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, tunerService, autoTiles, dumpManager, broadcastDispatcher, Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings, - customTileStatePersister, tileServiceRequestControllerBuilder); + customTileStatePersister, tileServiceRequestControllerBuilder, + tileLifecycleManagerFactory); } @Override 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/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index 97ad8bc6fab4..f3fcdbf329b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -92,7 +92,6 @@ class CustomTileTest : SysuiTestCase() { mContext.addMockSystemService("window", windowService) mContext.setMockPackageManager(packageManager) - `when`(tileHost.tileServices).thenReturn(tileServices) `when`(tileHost.context).thenReturn(mContext) `when`(tileServices.getTileWrapper(any(CustomTile::class.java))) .thenReturn(tileServiceManager) @@ -113,7 +112,8 @@ class CustomTileTest : SysuiTestCase() { statusBarStateController, activityStarter, qsLogger, - customTileStatePersister + customTileStatePersister, + tileServices ) customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java index f2303c26b6db..b559d18d9520 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java @@ -43,7 +43,6 @@ import android.os.HandlerThread; import android.os.UserHandle; import android.service.quicksettings.IQSService; import android.service.quicksettings.IQSTileService; -import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; import android.test.suitebuilder.annotation.SmallTest; @@ -96,11 +95,11 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mThread.start(); mHandler = Handler.createAsync(mThread.getLooper()); mStateManager = new TileLifecycleManager(mHandler, mWrappedContext, - Mockito.mock(IQSService.class), new Tile(), - mTileServiceIntent, - mUser, + Mockito.mock(IQSService.class), mMockPackageManagerAdapter, - mMockBroadcastDispatcher); + mMockBroadcastDispatcher, + mTileServiceIntent, + mUser); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index d604b2cdbad8..e39d6a1bfc01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -34,7 +34,6 @@ import android.content.IntentFilter; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -54,6 +53,7 @@ import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.settings.SecureSettings; @@ -104,6 +104,12 @@ public class TileServicesTest extends SysuiTestCase { private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder; @Mock private TileServiceRequestController mTileServiceRequestController; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private TileLifecycleManager.Factory mTileLifecycleManagerFactory; + @Mock + private TileLifecycleManager mTileLifecycleManager; @Before public void setUp() throws Exception { @@ -113,6 +119,8 @@ public class TileServicesTest extends SysuiTestCase { when(mTileServiceRequestControllerBuilder.create(any())) .thenReturn(mTileServiceRequestController); + when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class))) + .thenReturn(mTileLifecycleManager); QSTileHost host = new QSTileHost(mContext, mStatusBarIconController, @@ -130,14 +138,16 @@ public class TileServicesTest extends SysuiTestCase { mUserTracker, mSecureSettings, mock(CustomTileStatePersister.class), - mTileServiceRequestControllerBuilder); + mTileServiceRequestControllerBuilder, + mTileLifecycleManagerFactory); mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher, - mUserTracker); + mUserTracker, mKeyguardStateController); } @After public void tearDown() throws Exception { mTileService.getHost().destroy(); + mTileService.destroy(); TestableLooper.get(this).processAllMessages(); } @@ -217,13 +227,14 @@ public class TileServicesTest extends SysuiTestCase { private class TestTileServices extends TileServices { TestTileServices(QSTileHost host, Looper looper, - BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { - super(host, looper, broadcastDispatcher, userTracker); + BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, + KeyguardStateController keyguardStateController) { + super(host, looper, broadcastDispatcher, userTracker, keyguardStateController); } @Override - protected TileServiceManager onCreateTileService(ComponentName component, Tile qsTile, - BroadcastDispatcher broadcastDispatcher) { + protected TileServiceManager onCreateTileService( + ComponentName component, BroadcastDispatcher broadcastDispatcher) { TileServiceManager manager = mock(TileServiceManager.class); mManagers.add(manager); when(manager.isLifecycleStarted()).thenReturn(true); 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 8fb066bb4a39..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 @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; @@ -46,6 +47,7 @@ import android.testing.TestableLooper; import android.util.ArrayMap; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -109,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<>(); @@ -125,11 +129,12 @@ public class ShadeListBuilderTest extends SysuiTestCase { allowTestableLooperAsMainThread(); mListBuilder = new ShadeListBuilder( - mSystemClock, + mDumpManager, + mPipelineChoreographer, mNotifPipelineFlags, + mInteractionTracker, mLogger, - mDumpManager, - mInteractionTracker + mSystemClock ); mListBuilder.setOnRenderListListener(mOnRenderListListener); @@ -565,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()); @@ -872,6 +878,73 @@ public class ShadeListBuilderTest extends SysuiTestCase { } @Test + public void testThatSectionComparatorsAreCalled() { + // GIVEN a section with a comparator that elevates some packages over others + NotifComparator comparator = spy(new HypeComparator(PACKAGE_2, PACKAGE_4)); + NotifSectioner sectioner = new PackageSectioner( + List.of(PACKAGE_1, PACKAGE_2, PACKAGE_4, PACKAGE_5), comparator); + mListBuilder.setSectioners(List.of(sectioner)); + + // WHEN the pipeline is kicked off on a bunch of notifications + addNotif(0, PACKAGE_0); + addNotif(1, PACKAGE_1); + addNotif(2, PACKAGE_2); + addNotif(3, PACKAGE_3); + addNotif(4, PACKAGE_4); + addNotif(5, PACKAGE_5); + dispatchBuild(); + + // THEN the notifs are sorted according to both sectioning and the section's comparator + verifyBuiltList( + notif(2), + notif(4), + notif(1), + notif(5), + notif(0), + notif(3) + ); + + // VERIFY that the comparator is invoked at least 3 times + verify(comparator, atLeast(3)).compare(any(), any()); + + // VERIFY that the comparator is never invoked with the entry from package 0 or 3. + final NotificationEntry package0Entry = mEntrySet.get(0); + verify(comparator, never()).compare(eq(package0Entry), any()); + verify(comparator, never()).compare(any(), eq(package0Entry)); + final NotificationEntry package3Entry = mEntrySet.get(3); + verify(comparator, never()).compare(eq(package3Entry), any()); + verify(comparator, never()).compare(any(), eq(package3Entry)); + } + + @Test + public void testThatSectionComparatorsAreNotCalledForSectionWithSingleEntry() { + // GIVEN a section with a comparator that will have only 1 element + NotifComparator comparator = spy(new HypeComparator(PACKAGE_3)); + NotifSectioner sectioner = new PackageSectioner(List.of(PACKAGE_3), comparator); + mListBuilder.setSectioners(List.of(sectioner)); + + // WHEN the pipeline is kicked off on a bunch of notifications + addNotif(0, PACKAGE_1); + addNotif(1, PACKAGE_2); + addNotif(2, PACKAGE_3); + addNotif(3, PACKAGE_4); + addNotif(4, PACKAGE_5); + dispatchBuild(); + + // THEN the notifs are sorted according to the sectioning + verifyBuiltList( + notif(2), + notif(0), + notif(1), + notif(3), + notif(4) + ); + + // VERIFY that the comparator is never invoked + verify(comparator, never()).compare(any(), any()); + } + + @Test public void testListenersAndPluggablesAreFiredInOrder() { // GIVEN a bunch of registered listeners and pluggables NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1)); @@ -934,7 +1007,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a variety of pluggables NotifFilter packageFilter = new PackageFilter(PACKAGE_1); NotifPromoter idPromoter = new IdPromoter(4); - NotifSectioner section = new PackageSectioner(PACKAGE_1); + NotifComparator sectionComparator = new HypeComparator(PACKAGE_1); + NotifSectioner section = new PackageSectioner(List.of(PACKAGE_1), sectionComparator); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {}; @@ -954,22 +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()); } @@ -1441,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( @@ -1479,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()); @@ -1699,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)); @@ -1840,6 +1956,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } mReadyForBuildListener.onBuildList(mEntrySet); + mPipelineChoreographer.runIfScheduled(); } private void verifyBuiltList(ExpectedEntry ...expectedEntries) { @@ -2037,16 +2154,30 @@ public class ShadeListBuilderTest extends SysuiTestCase { /** Represents a section for the passed pkg */ private static class PackageSectioner extends NotifSectioner { - private final String mPackage; + private final List<String> mPackages; + private final NotifComparator mComparator; + + PackageSectioner(List<String> pkgs, NotifComparator comparator) { + super("PackageSection_" + pkgs, 0); + mPackages = pkgs; + mComparator = comparator; + } PackageSectioner(String pkg) { super("PackageSection_" + pkg, 0); - mPackage = pkg; + mPackages = List.of(pkg); + mComparator = null; + } + + @Nullable + @Override + public NotifComparator getComparator() { + return mComparator; } @Override public boolean isInSection(ListEntry entry) { - return entry.getRepresentativeEntry().getSbn().getPackageName().equals(mPackage); + return mPackages.contains(entry.getRepresentativeEntry().getSbn().getPackageName()); } } @@ -2157,6 +2288,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { } } + private static final String PACKAGE_0 = "com.test0"; private static final String PACKAGE_1 = "com.test1"; private static final String PACKAGE_2 = "com.test2"; private static final String PACKAGE_3 = "org.test3"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index 8deac94214bd..7692a05eb5fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -32,7 +32,6 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON -import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse @@ -41,7 +40,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever @@ -79,7 +77,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { } peopleSectioner = coordinator.sectioner - peopleComparator = coordinator.comparator + peopleComparator = peopleSectioner.comparator!! entry = NotificationEntryBuilder().setChannel(channel).build() @@ -108,16 +106,6 @@ class ConversationCoordinatorTest : SysuiTestCase() { } @Test - fun testComparatorIgnoresFromOtherSection() { - val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build() - val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build() - - // wrong section -- never classify - assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0) - verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any()) - } - - @Test fun testComparatorPutsImportantPeopleFirst() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_IMPORTANT_PERSON) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/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/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index bdcbbbc99ea3..4f731ed5f72c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.stack; -import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static android.view.View.GONE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; @@ -41,8 +40,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.os.UserHandle; -import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.MathUtils; @@ -112,10 +109,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void setUp() throws Exception { allowTestableLooperAsMainThread(); - Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED, - 1, UserHandle.USER_CURRENT); - - // Interact with real instance of AmbientState. mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController); @@ -150,6 +143,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); mStackScroller.setEmptyShadeView(mEmptyShadeView); + when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true); when(mStackScrollLayoutController.getNoticationRoundessManager()) .thenReturn(mNotificationRoundnessManager); mStackScroller.setController(mStackScrollLayoutController); @@ -404,6 +398,22 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + public void testUpdateFooter_withoutHistory() { + setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); + + when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false); + when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); + when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) + .thenReturn(true); + + FooterView view = mock(FooterView.class); + mStackScroller.setFooterView(view); + mStackScroller.updateFooter(); + verify(mStackScroller).updateFooterView(true, true, false); + } + + @Test public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { setBarStateForTest(StatusBarState.SHADE); mStackScroller.setCurrentUserSetup(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/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/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index d3258408b33a..424a40058997 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -128,6 +128,28 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { } @Test + public void testCompareTo_withNullEntries() { + NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); + mHeadsUpManager.showNotification(alertEntry); + + assertThat(mHeadsUpManager.compare(alertEntry, null)).isLessThan(0); + assertThat(mHeadsUpManager.compare(null, alertEntry)).isGreaterThan(0); + assertThat(mHeadsUpManager.compare(null, null)).isEqualTo(0); + } + + @Test + public void testCompareTo_withNonAlertEntries() { + NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag("nae1").build(); + NotificationEntry nonAlertEntry2 = new NotificationEntryBuilder().setTag("nae2").build(); + NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); + mHeadsUpManager.showNotification(alertEntry); + + assertThat(mHeadsUpManager.compare(alertEntry, nonAlertEntry1)).isLessThan(0); + assertThat(mHeadsUpManager.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0); + assertThat(mHeadsUpManager.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0); + } + + @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry(); ongoingCall.setEntry(new NotificationEntryBuilder() diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt new file mode 100644 index 000000000000..ac357ea34be0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -0,0 +1,73 @@ +package com.android.systemui.util.drawable + +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ShapeDrawable +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class DrawableSizeTest : SysuiTestCase() { + + lateinit var resources: Resources + + @Before + fun setUp() { + resources = context.resources + } + + @Test + fun testDownscaleToSize_drawableZeroSize_unchanged() { + val drawable = ShapeDrawable() + val result = DrawableSize.downscaleToSize(resources, drawable, 100, 100) + assertThat(result).isSameInstanceAs(drawable) + } + + @Test + fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() { + val drawable = BitmapDrawable(resources, + Bitmap.createBitmap( + resources.displayMetrics, + 150, + 150, + Bitmap.Config.ARGB_8888 + ) + ) + val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) + assertThat(result).isSameInstanceAs(drawable) + } + + @Test + fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() { + // This bitmap would actually fail to resize if the method doesn't check for + // bitmap dimensions inside drawable. + val drawable = BitmapDrawable(resources, + Bitmap.createBitmap( + resources.displayMetrics, + 150, + 75, + Bitmap.Config.ARGB_8888 + ) + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) + assertThat(result).isNotSameInstanceAs(drawable) + assertThat(result.intrinsicWidth).isEqualTo(75) + assertThat(result.intrinsicHeight).isEqualTo(37) + } + + @Test + fun testDownscaleToSize_drawableAnimated_unchanged() { + val drawable = resources.getDrawable(android.R.drawable.stat_sys_download, + resources.newTheme()) + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + assertThat(result).isSameInstanceAs(drawable) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 9c49e98b9e36..ca37a40474e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -1339,6 +1339,22 @@ public class BubblesTest extends SysuiTestCase { assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); } + @Test + public void testStackViewOnBackPressed_updatesBubbleDataExpandState() { + mBubbleController.updateBubble(mBubbleEntry); + + // Expand the stack + mBubbleData.setExpanded(true); + assertStackExpanded(); + + // Hit back + BubbleStackView stackView = mBubbleController.getStackView(); + stackView.onBackPressed(); + + // Make sure we're collapsed + assertStackCollapsed(); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index e12a82a1c62b..d82671d6ecb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -1158,6 +1158,22 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse(); } + @Test + public void testStackViewOnBackPressed_updatesBubbleDataExpandState() { + mBubbleController.updateBubble(mBubbleEntry); + + // Expand the stack + mBubbleData.setExpanded(true); + assertStackExpanded(); + + // Hit back + BubbleStackView stackView = mBubbleController.getStackView(); + stackView.onBackPressed(); + + // Make sure we're collapsed + assertStackCollapsed(); + } + /** * Sets the bubble metadata flags for this entry. These flags are normally set by * NotificationManagerService when the notification is sent, however, these tests do not diff --git a/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 b0a5c66b53dd..4e7e3c1ed4d1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -16,6 +16,10 @@ java_defaults { // "-Xep:AndroidFrameworkBinderIdentity:ERROR", "-Xep:AndroidFrameworkCompatChange:ERROR", // "-Xep:AndroidFrameworkUid:ERROR", + "-Xep:SelfEquals:ERROR", + "-Xep:NullTernary:ERROR", + "-Xep:TryFailThrowable:ERROR", + "-Xep:HashtableContains:ERROR", // NOTE: only enable to generate local patchfiles // "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster", // "-XepPatchLocation:/tmp/refaster/", @@ -183,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/TEST_MAPPING b/services/companion/TEST_MAPPING index 63f54fa35158..4a37cb812981 100644 --- a/services/companion/TEST_MAPPING +++ b/services/companion/TEST_MAPPING @@ -1,6 +1,12 @@ { "presubmit": [ { + "name": "CtsCompanionDeviceManagerCoreTestCases" + }, + { + "name": "CtsCompanionDeviceManagerUiAutomationTestCases" + }, + { "name": "CtsOsTestCases", "options": [ { diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java index f1d98f09aba3..0509e0cf5ccc 100644 --- a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java +++ b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java @@ -36,7 +36,6 @@ import com.android.server.LocalServices; * will be killed if association/role are revoked. */ public class AssociationCleanUpService extends JobService { - private static final String TAG = LOG_TAG + ".AssociationCleanUpService"; private static final int JOB_ID = AssociationCleanUpService.class.hashCode(); private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService( @@ -56,7 +55,7 @@ public class AssociationCleanUpService extends JobService { @Override public boolean onStopJob(final JobParameters params) { - Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId() + Slog.i(LOG_TAG, "Association cleanup job stopped; id=" + params.getJobId() + ", reason=" + JobParameters.getInternalReasonCodeDescription( params.getInternalStopReasonCode())); diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java index 3ccabaaea2fa..21a677b8383c 100644 --- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java +++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java @@ -18,6 +18,7 @@ package com.android.server.companion; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.companion.AssociationInfo; import android.net.MacAddress; @@ -52,9 +53,10 @@ import java.util.StringJoiner; * Other system component (both inside and outside if the com.android.server.companion package) * should use public {@link AssociationStore} interface. */ +@SuppressLint("LongLogTag") class AssociationStoreImpl implements AssociationStore { private static final boolean DEBUG = false; - private static final String TAG = "AssociationStore"; + private static final String TAG = "CompanionDevice_AssociationStore"; private final Object mLock = new Object(); 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/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index f2e660779e9e..fd130852a43a 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -16,16 +16,17 @@ package com.android.server.companion; -import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; - import android.companion.AssociationInfo; +import android.os.ShellCommand; import android.util.Log; import android.util.Slog; import java.io.PrintWriter; import java.util.List; -class CompanionDeviceShellCommand extends android.os.ShellCommand { +class CompanionDeviceShellCommand extends ShellCommand { + private static final String TAG = "CompanionDevice_ShellCommand"; + private final CompanionDeviceManagerService mService; private final AssociationStore mAssociationStore; @@ -84,7 +85,7 @@ class CompanionDeviceShellCommand extends android.os.ShellCommand { } return 0; } catch (Throwable t) { - Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t); + Slog.e(TAG, "Error running a command: $ " + cmd, t); getErrPrintWriter().println(Log.getStackTraceString(t)); return 1; } diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java index 6055a81c7ca7..8ac741a44ee5 100644 --- a/services/companion/java/com/android/server/companion/DataStoreUtils.java +++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java @@ -25,7 +25,7 @@ import android.os.Environment; import android.util.AtomicFile; import android.util.Slog; -import com.android.internal.util.FunctionalUtils; +import com.android.internal.util.FunctionalUtils.ThrowingConsumer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -34,8 +34,7 @@ import java.io.File; import java.io.FileOutputStream; final class DataStoreUtils { - - private static final String LOG_TAG = DataStoreUtils.class.getSimpleName(); + private static final String TAG = "CompanionDevice_DataStoreUtils"; static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag) throws XmlPullParserException { @@ -71,12 +70,12 @@ final class DataStoreUtils { * Writing to file could fail, for example, if the user has been recently removed and so was * their DE (/data/system_de/<user-id>/) directory. */ - static void writeToFileSafely(@NonNull AtomicFile file, - @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) { + static void writeToFileSafely( + @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) { try { file.write(consumer); } catch (Exception e) { - Slog.e(LOG_TAG, "Error while writing to file " + file, e); + Slog.e(TAG, "Error while writing to file " + file, e); } } 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/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java index da33b4446840..d0cc12286b12 100644 --- a/services/companion/java/com/android/server/companion/PersistentDataStore.java +++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java @@ -32,6 +32,7 @@ import static com.android.server.companion.DataStoreUtils.writeToFileSafely; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.companion.AssociationInfo; import android.content.pm.UserInfo; @@ -39,6 +40,7 @@ import android.net.MacAddress; import android.os.Environment; import android.util.ArrayMap; import android.util.AtomicFile; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TypedXmlPullParser; @@ -146,8 +148,9 @@ import java.util.concurrent.ConcurrentMap; * </state> * }</pre> */ +@SuppressLint("LongLogTag") final class PersistentDataStore { - private static final String LOG_TAG = CompanionDeviceManagerService.LOG_TAG + ".DataStore"; + private static final String TAG = "CompanionDevice_PersistentDataStore"; private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG; private static final int CURRENT_PERSISTENCE_VERSION = 1; @@ -208,10 +211,9 @@ final class PersistentDataStore { void readStateForUser(@UserIdInt int userId, @NonNull Collection<AssociationInfo> associationsOut, @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) { - Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk"); - + Slog.i(TAG, "Reading associations for user " + userId + " from disk"); final AtomicFile file = getStorageFileForUser(userId); - if (DEBUG) Slog.d(LOG_TAG, " > File=" + file.getBaseFile().getPath()); + if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath()); // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize // accesses to the file on the file system using this AtomicFile object. @@ -220,12 +222,12 @@ final class PersistentDataStore { final AtomicFile readFrom; final String rootTag; if (!file.getBaseFile().exists()) { - if (DEBUG) Slog.d(LOG_TAG, " > File does not exist -> Try to read legacy file"); + if (DEBUG) Log.d(TAG, " > File does not exist -> Try to read legacy file"); legacyBaseFile = getBaseLegacyStorageFileForUser(userId); - if (DEBUG) Slog.d(LOG_TAG, " > Legacy file=" + legacyBaseFile.getPath()); + if (DEBUG) Log.d(TAG, " > Legacy file=" + legacyBaseFile.getPath()); if (!legacyBaseFile.exists()) { - if (DEBUG) Slog.d(LOG_TAG, " > Legacy file does not exist -> Abort"); + if (DEBUG) Log.d(TAG, " > Legacy file does not exist -> Abort"); return; } @@ -236,13 +238,13 @@ final class PersistentDataStore { rootTag = XML_TAG_STATE; } - if (DEBUG) Slog.d(LOG_TAG, " > Reading associations..."); + if (DEBUG) Log.d(TAG, " > Reading associations..."); final int version = readStateFromFileLocked(userId, readFrom, rootTag, associationsOut, previouslyUsedIdsPerPackageOut); if (DEBUG) { - Slog.d(LOG_TAG, " > Done reading: " + associationsOut); + Log.d(TAG, " > Done reading: " + associationsOut); if (version < CURRENT_PERSISTENCE_VERSION) { - Slog.d(LOG_TAG, " > File used old format: v." + version + " -> Re-write"); + Log.d(TAG, " > File used old format: v." + version + " -> Re-write"); } } @@ -250,13 +252,13 @@ final class PersistentDataStore { // The data is either in the legacy file or in the legacy format, or both. // Save the data to right file in using the current format. if (DEBUG) { - Slog.d(LOG_TAG, " > Writing the data to " + file.getBaseFile().getPath()); + Log.d(TAG, " > Writing the data to " + file.getBaseFile().getPath()); } persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut); if (legacyBaseFile != null) { // We saved the data to the right file, can delete the old file now. - if (DEBUG) Slog.d(LOG_TAG, " > Deleting legacy file"); + if (DEBUG) Log.d(TAG, " > Deleting legacy file"); legacyBaseFile.delete(); } } @@ -273,11 +275,11 @@ final class PersistentDataStore { void persistStateForUser(@UserIdInt int userId, @NonNull Collection<AssociationInfo> associations, @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) { - Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk"); - if (DEBUG) Slog.d(LOG_TAG, " > " + associations); + Slog.i(TAG, "Writing associations for user " + userId + " to disk"); + if (DEBUG) Slog.d(TAG, " > " + associations); final AtomicFile file = getStorageFileForUser(userId); - if (DEBUG) Slog.d(LOG_TAG, " > File=" + file.getBaseFile().getPath()); + if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath()); // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize // accesses to the file on the file system using this AtomicFile object. synchronized (file) { @@ -312,7 +314,7 @@ final class PersistentDataStore { } return version; } catch (XmlPullParserException | IOException e) { - Slog.e(LOG_TAG, "Error while reading associations file", e); + Slog.e(TAG, "Error while reading associations file", e); return -1; } } @@ -528,7 +530,7 @@ final class PersistentDataStore { associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected); } catch (Exception e) { - if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e); + if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e); } return associationInfo; } diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java index 76340fc1d6a8..904283f4e60e 100644 --- a/services/companion/java/com/android/server/companion/RolesUtils.java +++ b/services/companion/java/com/android/server/companion/RolesUtils.java @@ -19,20 +19,23 @@ package com.android.server.companion; import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP; import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; -import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.role.RoleManager; import android.companion.AssociationInfo; import android.content.Context; import android.os.UserHandle; +import android.util.Log; import android.util.Slog; import java.util.List; /** Utility methods for accessing {@link RoleManager} APIs. */ +@SuppressLint("LongLogTag") final class RolesUtils { + private static final String TAG = CompanionDeviceManagerService.LOG_TAG; static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId, @NonNull String packageName, @NonNull String role) { @@ -45,7 +48,7 @@ final class RolesUtils { static void addRoleHolderForAssociation( @NonNull Context context, @NonNull AssociationInfo associationInfo) { if (DEBUG) { - Slog.d(LOG_TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo); + Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo); } final String deviceProfile = associationInfo.getDeviceProfile(); @@ -61,7 +64,7 @@ final class RolesUtils { MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(), success -> { if (!success) { - Slog.e(LOG_TAG, "Failed to add u" + userId + "\\" + packageName + Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName + " to the list of " + deviceProfile + " holders."); } }); @@ -70,7 +73,7 @@ final class RolesUtils { static void removeRoleHolderForAssociation( @NonNull Context context, @NonNull AssociationInfo associationInfo) { if (DEBUG) { - Slog.d(LOG_TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo); + Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo); } final String deviceProfile = associationInfo.getDeviceProfile(); @@ -86,7 +89,7 @@ final class RolesUtils { MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(), success -> { if (!success) { - Slog.e(LOG_TAG, "Failed to remove u" + userId + "\\" + packageName + Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName + " from the list of " + deviceProfile + " holders."); } }); diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java index 627b0bebc905..a771e7b1aa4a 100644 --- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java +++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java @@ -33,6 +33,7 @@ import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_FIRST_MATCH; import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST; import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER; +import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG; import static com.android.server.companion.presence.Utils.btDeviceToString; import static java.util.Objects.requireNonNull; @@ -70,7 +71,6 @@ import java.util.Set; @SuppressLint("LongLogTag") class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener { - private static final boolean DEBUG = false; private static final String TAG = "CompanionDevice_PresenceMonitor_BLE"; /** diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java index 93cbe973b00e..1ba198ae26b0 100644 --- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java +++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java @@ -16,6 +16,7 @@ package com.android.server.companion.presence; +import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG; import static com.android.server.companion.presence.Utils.btDeviceToString; import android.annotation.NonNull; @@ -39,7 +40,6 @@ import java.util.Map; class BluetoothCompanionDeviceConnectionListener extends BluetoothAdapter.BluetoothConnectionCallback implements AssociationStore.OnChangeListener { - private static final boolean DEBUG = false; private static final String TAG = "CompanionDevice_PresenceMonitor_BT"; interface Callback { diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java new file mode 100644 index 000000000000..6371b25e6347 --- /dev/null +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -0,0 +1,228 @@ +/* + * 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.companion.presence; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.companion.AssociationInfo; +import android.content.Context; +import android.util.Log; + +import com.android.server.companion.AssociationStore; + +import java.util.HashSet; +import java.util.Set; + +/** + * Class responsible for monitoring companion devices' "presence" status (i.e. + * connected/disconnected for Bluetooth devices; nearby or not for BLE devices). + * + * <p> + * Should only be used by + * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService} + * to which it provides the following API: + * <ul> + * <li> {@link #onSelfManagedDeviceConnected(int)} + * <li> {@link #onSelfManagedDeviceDisconnected(int)} + * <li> {@link #isDevicePresent(int)} + * <li> {@link Callback#onDeviceAppeared(int) Callback.onDeviceAppeared(int)} + * <li> {@link Callback#onDeviceDisappeared(int) Callback.onDeviceDisappeared(int)} + * </ul> + */ +@SuppressLint("LongLogTag") +public class CompanionDevicePresenceMonitor implements AssociationStore.OnChangeListener, + BluetoothCompanionDeviceConnectionListener.Callback, BleCompanionDeviceScanner.Callback { + static final boolean DEBUG = false; + private static final String TAG = "CompanionDevice_PresenceMonitor"; + + /** Callback for notifying about changes to status of companion devices. */ + public interface Callback { + /** Invoked when companion device is found nearby or connects. */ + void onDeviceAppeared(int associationId); + + /** Invoked when a companion device no longer seen nearby or disconnects. */ + void onDeviceDisappeared(int associationId); + } + + private final @NonNull AssociationStore mAssociationStore; + private final @NonNull Callback mCallback; + private final @NonNull BluetoothCompanionDeviceConnectionListener mBtConnectionListener; + private final @NonNull BleCompanionDeviceScanner mBleScanner; + + // NOTE: Same association may appear in more than one of the following sets at the same time. + // (E.g. self-managed devices that have MAC addresses, could be reported as present by their + // companion applications, while at the same be connected via BT, or detected nearby by BLE + // scanner) + private final @NonNull Set<Integer> mConnectedBtDevices = new HashSet<>(); + private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>(); + private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>(); + + public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore, + @NonNull Callback callback) { + mAssociationStore = associationStore; + mCallback = callback; + + mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore, + /* BluetoothCompanionDeviceConnectionListener.Callback */ this); + mBleScanner = new BleCompanionDeviceScanner(associationStore, + /* BleCompanionDeviceScanner.Callback */ this); + } + + /** Initialize {@link CompanionDevicePresenceMonitor} */ + public void init(Context context) { + if (DEBUG) Log.i(TAG, "init()"); + + final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + if (btAdapter != null) { + mBtConnectionListener.init(btAdapter); + mBleScanner.init(context, btAdapter); + } else { + Log.w(TAG, "BluetoothAdapter is NOT available."); + } + + mAssociationStore.registerListener(this); + } + + /** + * @return whether the associated companion devices is present. I.e. device is nearby (for BLE); + * or devices is connected (for Bluetooth); or reported (by the application) to be + * nearby (for "self-managed" associations). + */ + public boolean isDevicePresent(int associationId) { + return mReportedSelfManagedDevices.contains(associationId) + || mConnectedBtDevices.contains(associationId) + || mNearbyBleDevices.contains(associationId); + } + + /** + * Marks a "self-managed" device as connected. + * + * <p> + * Must ONLY be invoked by the + * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService} + * when an application invokes + * {@link android.companion.CompanionDeviceManager#notifyDeviceAppeared(int) notifyDeviceAppeared()} + */ + public void onSelfManagedDeviceConnected(int associationId) { + onDevicePresent(mReportedSelfManagedDevices, associationId, "application-reported"); + } + + /** + * Marks a "self-managed" device as disconnected. + * + * <p> + * Must ONLY be invoked by the + * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService} + * when an application invokes + * {@link android.companion.CompanionDeviceManager#notifyDeviceDisappeared(int) notifyDeviceDisappeared()} + */ + public void onSelfManagedDeviceDisconnected(int associationId) { + onDeviceGone(mReportedSelfManagedDevices, associationId, "application-reported"); + } + + @Override + public void onBluetoothCompanionDeviceConnected(int associationId) { + onDevicePresent(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt"); + } + + @Override + public void onBluetoothCompanionDeviceDisconnected(int associationId) { + onDeviceGone(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt"); + } + + @Override + public void onBleCompanionDeviceFound(int associationId) { + onDevicePresent(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble"); + } + + @Override + public void onBleCompanionDeviceLost(int associationId) { + onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble"); + } + + private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource, + int newDeviceAssociationId, @NonNull String sourceLoggingTag) { + if (DEBUG) { + Log.i(TAG, "onDevice_Present() id=" + newDeviceAssociationId + + ", source=" + sourceLoggingTag); + Log.d(TAG, " > association=" + + mAssociationStore.getAssociationById(newDeviceAssociationId)); + } + + final boolean alreadyPresent = isDevicePresent(newDeviceAssociationId); + if (DEBUG && alreadyPresent) Log.i(TAG, "Device is already present."); + + final boolean added = presentDevicesForSource.add(newDeviceAssociationId); + if (DEBUG && !added) { + Log.w(TAG, "Association with id " + newDeviceAssociationId + " is ALREADY reported as " + + "present by this source (" + sourceLoggingTag + ")"); + } + + if (alreadyPresent) return; + + mCallback.onDeviceAppeared(newDeviceAssociationId); + } + + private void onDeviceGone(@NonNull Set<Integer> presentDevicesForSource, + int goneDeviceAssociationId, @NonNull String sourceLoggingTag) { + if (DEBUG) { + Log.i(TAG, "onDevice_Gone() id=" + goneDeviceAssociationId + + ", source=" + sourceLoggingTag); + Log.d(TAG, " > association=" + + mAssociationStore.getAssociationById(goneDeviceAssociationId)); + } + + final boolean removed = presentDevicesForSource.remove(goneDeviceAssociationId); + if (!removed) { + if (DEBUG) { + Log.w(TAG, "Association with id " + goneDeviceAssociationId + " was NOT reported " + + "as present by this source (" + sourceLoggingTag + ")"); + } + return; + } + + final boolean stillPresent = isDevicePresent(goneDeviceAssociationId); + if (stillPresent) { + if (DEBUG) Log.i(TAG, " Device is still present."); + return; + } + + mCallback.onDeviceDisappeared(goneDeviceAssociationId); + } + + /** + * Implements + * {@link AssociationStore.OnChangeListener#onAssociationRemoved(AssociationInfo)} + */ + @Override + public void onAssociationRemoved(@NonNull AssociationInfo association) { + final int id = association.getId(); + if (DEBUG) { + Log.i(TAG, "onAssociationRemoved() id=" + id); + Log.d(TAG, " > association=" + association); + } + + mConnectedBtDevices.remove(id); + mNearbyBleDevices.remove(id); + mReportedSelfManagedDevices.remove(id); + + // Do NOT call mCallback.onDeviceDisappeared()! + // CompanionDeviceManagerService will know that the association is removed, and will do + // what's needed. + } +} 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/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index e6bfd1ff7f1a..9d4b50be41fb 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -30,7 +30,6 @@ import android.hardware.input.VirtualMouseRelativeEvent; import android.hardware.input.VirtualMouseScrollEvent; import android.hardware.input.VirtualTouchEvent; import android.os.IBinder; -import android.os.IInputConstants; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; @@ -43,6 +42,7 @@ import com.android.server.LocalServices; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -76,13 +76,6 @@ class InputController { private final DisplayManagerInternal mDisplayManagerInternal; private final InputManagerInternal mInputManagerInternal; - /** - * Because the pointer is a singleton, it can only be targeted at one display at a time. Because - * multiple mice could be concurrently registered, mice that are associated with a different - * display than the current target display should not be allowed to affect the current target. - */ - @VisibleForTesting int mActivePointerDisplayId; - InputController(@NonNull Object lock) { this(lock, new NativeWrapper()); } @@ -91,18 +84,21 @@ class InputController { InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) { mLock = lock; mNativeWrapper = nativeWrapper; - mActivePointerDisplayId = Display.INVALID_DISPLAY; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); } void close() { synchronized (mLock) { - for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) { - mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor()); + final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator = + mInputDeviceDescriptors.entrySet().iterator(); + if (iterator.hasNext()) { + final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next(); + final IBinder token = entry.getKey(); + final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue(); + iterator.remove(); + closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); } - mInputDeviceDescriptors.clear(); - resetMouseValuesLocked(); } } @@ -150,8 +146,6 @@ class InputController { new InputDeviceDescriptor(fd, binderDeathRecipient, InputDeviceDescriptor.TYPE_MOUSE, displayId, phys)); mInputManagerInternal.setVirtualMousePointerDisplayId(displayId); - mInputManagerInternal.setPointerAcceleration(1); - mActivePointerDisplayId = displayId; } try { deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); @@ -197,23 +191,44 @@ class InputController { throw new IllegalArgumentException( "Could not unregister input device for given token"); } - token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0); - mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor()); - InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys()); - - // Reset values to the default if all virtual mice are unregistered, or set display - // id if there's another mouse (choose the most recent). - if (inputDeviceDescriptor.isMouse()) { - updateMouseValuesLocked(); + closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); + } + } + + @GuardedBy("mLock") + private void closeInputDeviceDescriptorLocked(IBinder token, + InputDeviceDescriptor inputDeviceDescriptor) { + token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0); + mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor()); + InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys()); + + // Reset values to the default if all virtual mice are unregistered, or set display + // id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be + // removed from the mInputDeviceDescriptors instance variable prior to this point. + if (inputDeviceDescriptor.isMouse()) { + if (mInputManagerInternal.getVirtualMousePointerDisplayId() + == inputDeviceDescriptor.getDisplayId()) { + updateActivePointerDisplayIdLocked(); } } } + void setShowPointerIcon(boolean visible, int displayId) { + mInputManagerInternal.setPointerIconVisible(visible, displayId); + } + + void setPointerAcceleration(float pointerAcceleration, int displayId) { + mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId); + } + + void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { + mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); + } + @GuardedBy("mLock") - private void updateMouseValuesLocked() { + private void updateActivePointerDisplayIdLocked() { InputDeviceDescriptor mostRecentlyCreatedMouse = null; - for (InputDeviceDescriptor otherInputDeviceDescriptor : - mInputDeviceDescriptors.values()) { + for (InputDeviceDescriptor otherInputDeviceDescriptor : mInputDeviceDescriptors.values()) { if (otherInputDeviceDescriptor.isMouse()) { if (mostRecentlyCreatedMouse == null || (otherInputDeviceDescriptor.getCreationOrderNumber() @@ -225,20 +240,12 @@ class InputController { if (mostRecentlyCreatedMouse != null) { mInputManagerInternal.setVirtualMousePointerDisplayId( mostRecentlyCreatedMouse.getDisplayId()); - mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId(); } else { - // All mice have been unregistered; reset all values. - resetMouseValuesLocked(); + // All mice have been unregistered + mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); } } - private void resetMouseValuesLocked() { - mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); - mInputManagerInternal.setPointerAcceleration( - IInputConstants.DEFAULT_POINTER_ACCELERATION); - mActivePointerDisplayId = Display.INVALID_DISPLAY; - } - private static String createPhys(@PhysType String type) { return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement()); } @@ -269,7 +276,8 @@ class InputController { throw new IllegalArgumentException( "Could not send button event to input device for given token"); } - if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) { + if (inputDeviceDescriptor.getDisplayId() + != mInputManagerInternal.getVirtualMousePointerDisplayId()) { throw new IllegalStateException( "Display id associated with this mouse is not currently targetable"); } @@ -300,7 +308,8 @@ class InputController { throw new IllegalArgumentException( "Could not send relative event to input device for given token"); } - if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) { + if (inputDeviceDescriptor.getDisplayId() + != mInputManagerInternal.getVirtualMousePointerDisplayId()) { throw new IllegalStateException( "Display id associated with this mouse is not currently targetable"); } @@ -317,7 +326,8 @@ class InputController { throw new IllegalArgumentException( "Could not send scroll event to input device for given token"); } - if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) { + if (inputDeviceDescriptor.getDisplayId() + != mInputManagerInternal.getVirtualMousePointerDisplayId()) { throw new IllegalStateException( "Display id associated with this mouse is not currently targetable"); } @@ -334,7 +344,8 @@ class InputController { throw new IllegalArgumentException( "Could not get cursor position for input device for given token"); } - if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) { + if (inputDeviceDescriptor.getDisplayId() + != mInputManagerInternal.getVirtualMousePointerDisplayId()) { throw new IllegalStateException( "Display id associated with this mouse is not currently targetable"); } @@ -354,7 +365,6 @@ class InputController { fout.println(" type: " + inputDeviceDescriptor.getType()); fout.println(" phys: " + inputDeviceDescriptor.getPhys()); } - fout.println(" Active mouse display id: " + mActivePointerDisplayId); } } 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..47e218b48c09 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -34,12 +34,9 @@ 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; -import android.hardware.input.InputManagerInternal; import android.hardware.input.VirtualKeyEvent; import android.hardware.input.VirtualMouseButtonEvent; import android.hardware.input.VirtualMouseRelativeEvent; @@ -58,9 +55,8 @@ import android.util.Slog; import android.util.SparseArray; import android.window.DisplayWindowPolicyController; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.BlockedAppActivity; -import com.android.server.LocalServices; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -86,6 +82,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceParams mParams; private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>(); private final IVirtualDeviceActivityListener mActivityListener; + // The default setting for showing the pointer on new displays. + @GuardedBy("mVirtualDeviceLock") + private boolean mDefaultShowPointerIcon = true; private ActivityListener createListenerAdapter(int displayId) { return new ActivityListener() { @@ -385,6 +384,25 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + @Override // Binder call + public void setShowPointerIcon(boolean showPointerIcon) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CREATE_VIRTUAL_DEVICE, + "Permission required to unregister this input device"); + + final long binderToken = Binder.clearCallingIdentity(); + try { + synchronized (mVirtualDeviceLock) { + mDefaultShowPointerIcon = showPointerIcon; + for (int displayId : mVirtualDisplayIds) { + mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); + } + } + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { fout.println(" VirtualDevice: "); @@ -395,6 +413,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub for (int id : mVirtualDisplayIds) { fout.println(" " + id); } + fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon); } mInputController.dump(fout); } @@ -406,23 +425,23 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub "Virtual device already have a virtual display with ID " + displayId); } mVirtualDisplayIds.add(displayId); + mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId); + mInputController.setPointerAcceleration(1f, displayId); + mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false, + displayId); // Since we're being called in the middle of the display being created, we post a // task to grab the wakelock instead of doing it synchronously here, to avoid // reentrancy problems. mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId)); - LocalServices.getService( - InputManagerInternal.class).setDisplayEligibilityForPointerCapture(displayId, - false); final GenericWindowPolicyController dwpc = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(), mParams.getAllowedActivities(), mParams.getBlockedActivities(), - createListenerAdapter(displayId), - activityInfo -> onActivityBlocked(displayId, activityInfo)); + createListenerAdapter(displayId)); mWindowPolicyControllers.put(displayId, dwpc); return dwpc; } @@ -445,16 +464,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); @@ -485,9 +494,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mPerDisplayWakelocks.remove(displayId); } mVirtualDisplayIds.remove(displayId); - LocalServices.getService( - InputManagerInternal.class).setDisplayEligibilityForPointerCapture( - displayId, true); mWindowPolicyControllers.remove(displayId); } } 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/Dumpable.java b/services/core/java/com/android/server/Dumpable.java deleted file mode 100644 index 004f923774e1..000000000000 --- a/services/core/java/com/android/server/Dumpable.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 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; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.IndentingPrintWriter; - -/** - * Interface used to dump {@link SystemServer} state that is not associated with any service. - * - * <p>See {@link SystemServer.SystemServerDumper} for usage example. - */ -// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require -// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix -public interface Dumpable { - - /** - * Dumps the state. - */ - void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args); - - /** - * Gets the name of the dumpable. - * - * <p>If not overridden, will return the simple class name. - */ - default String getDumpableName() { - return Dumpable.this.getClass().getSimpleName(); - } -} diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index 53b660533ce2..63e7563af6d1 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -19,7 +19,7 @@ package com.android.server; import android.annotation.NonNull; import android.os.Build; import android.os.Process; -import android.util.IndentingPrintWriter; +import android.util.Dumpable; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -28,6 +28,7 @@ import com.android.internal.util.Preconditions; import com.android.server.am.ActivityManagerService; import com.android.server.utils.TimingsTraceAndSlog; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -196,7 +197,12 @@ public final class SystemServerInitThreadPool implements Dumpable { } @Override - public void dump(IndentingPrintWriter pw, String[] args) { + public String getDumpableName() { + return SystemServerInitThreadPool.class.getSimpleName(); + } + + @Override + public void dump(PrintWriter pw, String[] args) { synchronized (LOCK) { pw.printf("has instance: %b\n", (sInstance != null)); } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index ce30f0348f11..12e438d1f2a9 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -27,8 +27,8 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.util.ArraySet; +import android.util.Dumpable; import android.util.EventLog; -import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; @@ -44,6 +44,7 @@ import com.android.server.utils.TimingsTraceAndSlog; import dalvik.system.PathClassLoader; import java.io.File; +import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -509,8 +510,7 @@ public final class SystemServiceManager implements Dumpable { throw new IllegalArgumentException(onWhat + " what?"); } } catch (Exception ex) { - Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser - + " to service " + serviceName, ex); + logFailure(onWhat, curUser, serviceName, ex); } if (!submitToThreadPool) { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, @@ -584,8 +584,7 @@ public final class SystemServiceManager implements Dumpable { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + USER_STARTING + "User-" + curUserId); } catch (Exception e) { - Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser - + " to service " + serviceName, e); + logFailure(USER_STARTING, curUser, serviceName, e); Slog.e(TAG, "Disabling thread pool - please capture a bug report."); sUseLifecycleThreadPool = false; } finally { @@ -608,8 +607,7 @@ public final class SystemServiceManager implements Dumpable { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + USER_COMPLETED_EVENT + "User-" + curUserId); } catch (Exception e) { - Slog.wtf(TAG, "Failure reporting " + USER_COMPLETED_EVENT + " of user " + curUser - + " to service " + serviceName, e); + logFailure(USER_COMPLETED_EVENT, curUser, serviceName, e); throw e; } finally { t.traceEnd(); @@ -617,6 +615,12 @@ public final class SystemServiceManager implements Dumpable { }; } + /** Logs the failure. That's all. Tests may rely on parsing it, so only modify carefully. */ + private void logFailure(String onWhat, TargetUser curUser, String serviceName, Exception ex) { + Slog.wtf(TAG, "SystemService failure: Failure reporting " + onWhat + " of user " + + curUser + " to service " + serviceName, ex); + } + /** Sets the safe mode flag for services to query. */ void setSafeMode(boolean safeMode) { mSafeMode = safeMode; @@ -684,7 +688,12 @@ public final class SystemServiceManager implements Dumpable { } @Override - public void dump(IndentingPrintWriter pw, String[] args) { + public String getDumpableName() { + return SystemServiceManager.class.getSimpleName(); + } + + @Override + public void dump(PrintWriter pw, String[] args) { pw.printf("Current phase: %d\n", mCurrentPhase); synchronized (mTargetUsers) { if (mCurrentUser != null) { @@ -708,14 +717,13 @@ public final class SystemServiceManager implements Dumpable { } } final int startedLen = mServices.size(); + String prefix = " "; if (startedLen > 0) { pw.printf("%d started services:\n", startedLen); - pw.increaseIndent(); for (int i = 0; i < startedLen; i++) { final SystemService service = mServices.get(i); - pw.println(service.getClass().getCanonicalName()); + pw.print(prefix); pw.println(service.getClass().getCanonicalName()); } - pw.decreaseIndent(); } else { pw.println("No started services"); } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 6a7afd90dc96..2d328d8b0949 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -539,7 +539,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void notifyAllPolicyListenersLocked() { for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) { - Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged()); + Binder.withCleanCallingIdentity(() -> { + try { + policyListener.mListener.onPolicyChanged(); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } @@ -548,8 +554,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) { for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, subGroup)) { - Binder.withCleanCallingIdentity( - () -> cbInfo.mCallback.onVcnStatusChanged(statusCode)); + Binder.withCleanCallingIdentity(() -> { + try { + cbInfo.mCallback.onVcnStatusChanged(statusCode); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } } @@ -1222,13 +1233,17 @@ public class VcnManagementService extends IVcnManagementService.Stub { // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (isCallbackPermissioned(cbInfo, mSubGroup)) { - Binder.withCleanCallingIdentity( - () -> - cbInfo.mCallback.onGatewayConnectionError( - gatewayConnectionName, - errorCode, - exceptionClass, - exceptionMessage)); + Binder.withCleanCallingIdentity(() -> { + try { + cbInfo.mCallback.onGatewayConnectionError( + gatewayConnectionName, + errorCode, + exceptionClass, + exceptionMessage); + } catch (RemoteException e) { + logDbg("VcnStatusCallback threw on VCN status change", e); + } + }); } } } diff --git a/services/core/java/com/android/server/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 2f87e4f846a1..0310b0fc4469 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -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 @@ -12798,7 +12799,7 @@ public class ActivityManagerService extends IActivityManager.Stub noAction.add(null); actions = noAction.iterator(); } - boolean onlyProtectedBroadcasts = actions.hasNext(); + boolean onlyProtectedBroadcasts = true; // Collect stickies of users and check if broadcast is only registered for protected // broadcasts @@ -12872,6 +12873,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Change is not enabled, thus not targeting T+. Assume exported. flags |= Context.RECEIVER_EXPORTED; } + } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) { + flags |= Context.RECEIVER_EXPORTED; } } @@ -16087,6 +16090,23 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Returns package name by pid. + */ + @Override + @Nullable + public String getPackageNameByPid(int pid) { + synchronized (mPidsSelfLocked) { + final ProcessRecord app = mPidsSelfLocked.get(pid); + + if (app != null && app.info != null) { + return app.info.packageName; + } + + return null; + } + } + + /** * Sets if the given pid has an overlay UI or not. * * @param pid The pid we are setting overlay UI for. @@ -17886,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/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 1315293abaa4..465623f800c5 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -54,6 +54,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE; +import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP; import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE; @@ -65,6 +66,7 @@ import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI; import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER; import static android.os.PowerExemptionManager.REASON_ROLE_DIALER; import static android.os.PowerExemptionManager.REASON_ROLE_EMERGENCY; +import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED; import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE; import static android.os.PowerExemptionManager.REASON_SYSTEM_UID; import static android.os.Process.SYSTEM_UID; @@ -125,6 +127,7 @@ import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.provider.Settings.Global; +import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -138,6 +141,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.TriConsumer; import com.android.server.AppStateTracker; import com.android.server.LocalServices; +import com.android.server.SystemConfig; import com.android.server.apphibernation.AppHibernationManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; @@ -229,6 +233,24 @@ public final class AppRestrictionController { @GuardedBy("mLock") private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>(); + /** + * The pre-config packages that are exempted from the background restrictions. + */ + private ArraySet<String> mBgRestrictionExemptioFromSysConfig; + + /** + * Lock specifically for bookkeeping around the carrier-privileged app set. + * Do not acquire any other locks while holding this one. Methods that + * require this lock to be held are named with a "CPL" suffix. + */ + private final Object mCarrierPrivilegedLock = new Object(); + + /** + * List of carrier-privileged apps that should be excluded from standby. + */ + @GuardedBy("mCarrierPrivilegedLock") + private List<String> mCarrierPrivilegedApps; + final ActivityManagerService mActivityManagerService; /** @@ -690,6 +712,7 @@ public final class AppRestrictionController { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mConstantsObserver); mConstantsObserver.start(); + initBgRestrictionExemptioFromSysConfig(); initRestrictionStates(); initSystemModuleNames(); registerForUidObservers(); @@ -711,6 +734,22 @@ public final class AppRestrictionController { initRestrictionStates(); } + private void initBgRestrictionExemptioFromSysConfig() { + mBgRestrictionExemptioFromSysConfig = + SystemConfig.getInstance().getBgRestrictionExemption(); + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + final ArraySet<String> exemptedPkgs = mBgRestrictionExemptioFromSysConfig; + for (int i = exemptedPkgs.size() - 1; i >= 0; i--) { + Slog.i(TAG, "bg-restriction-exemption: " + exemptedPkgs.valueAt(i)); + } + } + } + + private boolean isExemptedFromSysConfig(String packageName) { + return mBgRestrictionExemptioFromSysConfig != null + && mBgRestrictionExemptioFromSysConfig.contains(packageName); + } + private void initRestrictionStates() { final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); for (int userId : allUsers) { @@ -1542,14 +1581,11 @@ public final class AppRestrictionController { } } - boolean isOnDeviceIdleAllowlist(int uid, boolean allowExceptIdle) { + boolean isOnDeviceIdleAllowlist(int uid) { final int appId = UserHandle.getAppId(uid); - final int[] allowlist = allowExceptIdle - ? mDeviceIdleExceptIdleAllowlist - : mDeviceIdleAllowlist; - - return Arrays.binarySearch(allowlist, appId) >= 0; + return Arrays.binarySearch(mDeviceIdleAllowlist, appId) >= 0 + || Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, appId) >= 0; } void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) { @@ -1570,7 +1606,7 @@ public final class AppRestrictionController { if (UserHandle.isCore(uid)) { return REASON_SYSTEM_UID; } - if (isOnDeviceIdleAllowlist(uid, false)) { + if (isOnDeviceIdleAllowlist(uid)) { return REASON_ALLOWLISTED_PACKAGE; } final ActivityManagerInternal am = mInjector.getActivityManagerInternal(); @@ -1604,6 +1640,10 @@ public final class AppRestrictionController { return REASON_OP_ACTIVATE_PLATFORM_VPN; } else if (isSystemModule(pkg)) { return REASON_SYSTEM_MODULE; + } else if (isCarrierApp(pkg)) { + return REASON_CARRIER_PRIVILEGED_APP; + } else if (isExemptedFromSysConfig(pkg)) { + return REASON_SYSTEM_ALLOW_LISTED; } } } @@ -1616,6 +1656,37 @@ public final class AppRestrictionController { return REASON_DENIED; } + private boolean isCarrierApp(String packageName) { + synchronized (mCarrierPrivilegedLock) { + if (mCarrierPrivilegedApps == null) { + fetchCarrierPrivilegedAppsCPL(); + } + if (mCarrierPrivilegedApps != null) { + return mCarrierPrivilegedApps.contains(packageName); + } + return false; + } + } + + private void clearCarrierPrivilegedApps() { + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + Slog.i(TAG, "Clearing carrier privileged apps list"); + } + synchronized (mCarrierPrivilegedLock) { + mCarrierPrivilegedApps = null; // Need to be refetched. + } + } + + @GuardedBy("mCarrierPrivilegedLock") + private void fetchCarrierPrivilegedAppsCPL() { + final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); + mCarrierPrivilegedApps = + telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions(); + if (DEBUG_BG_RESTRICTION_CONTROLLER) { + Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); + } + } + private boolean isRoleHeldByUid(@NonNull String roleName, int uid) { synchronized (mLock) { final ArrayList<String> roles = mUidRolesMapping.get(uid); @@ -1791,6 +1862,7 @@ public final class AppRestrictionController { private AppBatteryExemptionTracker mAppBatteryExemptionTracker; private AppFGSTracker mAppFGSTracker; private AppMediaSessionTracker mAppMediaSessionTracker; + private TelephonyManager mTelephonyManager; Injector(Context context) { mContext = context; @@ -1890,6 +1962,13 @@ public final class AppRestrictionController { return mRoleManager; } + TelephonyManager getTelephonyManager() { + if (mTelephonyManager == null) { + mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + } + return mTelephonyManager; + } + AppFGSTracker getAppFGSTracker() { return mAppFGSTracker; } @@ -1939,6 +2018,19 @@ public final class AppRestrictionController { onUidAdded(uid); } } + } + // fall through. + case Intent.ACTION_PACKAGE_CHANGED: { + final String pkgName = intent.getData().getSchemeSpecificPart(); + final String[] cmpList = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); + // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package + // enable/disable event (cmpList is just the package name itself), drop + // our carrier privileged app & system-app caches and let them refresh + if (cmpList == null + || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) { + clearCarrierPrivilegedApps(); + } } break; case Intent.ACTION_PACKAGE_FULLY_REMOVED: { final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -1986,6 +2078,7 @@ public final class AppRestrictionController { }; final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); packageFilter.addDataScheme("package"); mContext.registerReceiverForAllUsers(broadcastReceiver, packageFilter, null, mBgHandler); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 8561b61c2172..1131fa8a32b8 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -451,7 +451,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mUidsToRemove.clear(); mCurrentFuture = null; mUseLatestStates = true; - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { cancelSyncDueToBatteryLevelChangeLocked(); } if ((updateFlags & UPDATE_CPU) != 0) { @@ -496,7 +496,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.wtf(TAG, "Error updating external stats: ", e); } - if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { + if ((updateFlags & RESET) != 0) { + synchronized (BatteryExternalStatsWorker.this) { + mLastCollectionTimeStamp = 0; + } + } else if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { synchronized (BatteryExternalStatsWorker.this) { mLastCollectionTimeStamp = SystemClock.elapsedRealtime(); } @@ -658,7 +662,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC); } - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { mStats.updateKernelWakelocksLocked(elapsedRealtimeUs); mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs); } @@ -731,7 +735,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { uptime, networkStatsManager); } - if (updateFlags == UPDATE_ALL) { + if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) { // This helps mStats deal with ignoring data from prior to resets. mStats.informThatAllExternalStatsAreFlushed(); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5da461d8e392..2f7249ea5d84 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -797,12 +797,19 @@ public final class BatteryStatsService extends IBatteryStats.Stub final BatteryUsageStats bus; switch (atomTag) { case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET: - bus = getBatteryUsageStats(List.of(BatteryUsageStatsQuery.DEFAULT)).get(0); + final BatteryUsageStatsQuery querySinceReset = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .build(); + bus = getBatteryUsageStats(List.of(querySinceReset)).get(0); break; case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL: - final BatteryUsageStatsQuery powerProfileQuery = - new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(); - bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0); + final BatteryUsageStatsQuery queryPowerProfile = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .powerProfileModeledOnly() + .build(); + bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0); break; case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: if (!BATTERY_USAGE_STORE_ENABLED) { @@ -812,10 +819,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long sessionStart = mBatteryUsageStatsStore .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp(); final long sessionEnd = mStats.getStartClockTime(); - final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() - .aggregateSnapshots(sessionStart, sessionEnd) - .build(); - bus = getBatteryUsageStats(List.of(query)).get(0); + final BatteryUsageStatsQuery queryBeforeReset = + new BatteryUsageStatsQuery.Builder() + .includeProcessStateData() + .aggregateSnapshots(sessionStart, sessionEnd) + .build(); + bus = getBatteryUsageStats(List.of(queryBeforeReset)).get(0); mBatteryUsageStatsStore .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd); break; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index e2921e9f5e71..a83fdd0e74cd 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -56,6 +56,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; import android.os.Process; @@ -870,7 +871,7 @@ public final class BroadcastQueue { + " due to receiver " + filter.receiverList.app + " (uid " + filter.receiverList.uid + ")" + " not specifying RECEIVER_EXPORTED"); - // skip = true; + skip = true; } if (skip) { @@ -1857,22 +1858,36 @@ public final class BroadcastQueue { } private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) { - final String targetPackage = getTargetPackage(r); - // Ignore non-explicit broadcasts - if (targetPackage == null) { - return; - } // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set // getIdForResponseEvent. + // TODO (217251579): Temporarily use temp-allowlist reason to identify + // push messages and record response events. + useTemporaryAllowlistReasonAsSignal(r); if (r.options == null || r.options.getIdForResponseEvent() <= 0) { return; } + final String targetPackage = getTargetPackage(r); + // Ignore non-explicit broadcasts + if (targetPackage == null) { + return; + } getUsageStatsManagerInternal().reportBroadcastDispatched( r.callingUid, targetPackage, UserHandle.of(r.userId), r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(), mService.getUidStateLocked(targetUid)); } + private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) { + if (r.options == null || r.options.getIdForResponseEvent() > 0) { + return; + } + final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode(); + if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING + || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) { + r.options.recordResponseEventWhileInBackground(reasonCode); + } + } + @NonNull private UsageStatsManagerInternal getUsageStatsManagerInternal() { final UsageStatsManagerInternal usageStatsManagerInternal = diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 6f22c61d2b2a..7af73ebda0c0 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -977,7 +977,7 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " received sync transactions while frozen, killing"); app.killLocked("Sync transaction while in frozen state", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true); processKilled = true; } @@ -990,7 +990,7 @@ public final class CachedAppOptimizer { Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " " + app.processName + ". Killing it. Exception: " + e); app.killLocked("Unable to query binder frozen stats", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); processKilled = true; } @@ -1007,7 +1007,7 @@ public final class CachedAppOptimizer { Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName + ". Killing it"); app.killLocked("Unable to unfreeze", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); return; } @@ -1423,7 +1423,7 @@ public final class CachedAppOptimizer { mFreezeHandler.post(() -> { synchronized (mAm) { proc.killLocked("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); } }); @@ -1477,7 +1477,7 @@ public final class CachedAppOptimizer { mFreezeHandler.post(() -> { synchronized (mAm) { proc.killLocked("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.REASON_FREEZER, ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true); } }); diff --git a/services/core/java/com/android/server/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 960fbf1d9330..4eba77168b8e 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -43,6 +43,7 @@ import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.util.Slog; +import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import com.android.internal.annotations.GuardedBy; @@ -237,7 +238,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan mTaskSystemBarsVisibilityListener); for (GameSessionRecord gameSessionRecord : mGameSessions.values()) { - destroyGameSessionFromRecord(gameSessionRecord); + destroyGameSessionFromRecordLocked(gameSessionRecord); } mGameSessions.clear(); @@ -472,7 +473,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } try { - mWindowManagerInternal.addTaskOverlay( + mWindowManagerInternal.addTrustedTaskOverlay( taskId, createGameSessionResult.getSurfacePackage()); } catch (IllegalArgumentException ex) { @@ -510,14 +511,15 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } return; } - destroyGameSessionFromRecord(gameSessionRecord); + destroyGameSessionFromRecordLocked(gameSessionRecord); } - private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) { + @GuardedBy("mLock") + private void destroyGameSessionFromRecordLocked(@NonNull GameSessionRecord gameSessionRecord) { SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage(); if (surfacePackage != null) { try { - mWindowManagerInternal.removeTaskOverlay( + mWindowManagerInternal.removeTrustedTaskOverlay( gameSessionRecord.getTaskId(), surfacePackage); } catch (IllegalArgumentException ex) { @@ -586,17 +588,29 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @VisibleForTesting void takeScreenshot(int taskId, @NonNull AndroidFuture callback) { + GameSessionRecord gameSessionRecord; synchronized (mLock) { - boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId); - if (!isTaskAssociatedWithGameSession) { + gameSessionRecord = mGameSessions.get(taskId); + if (gameSessionRecord == null) { Slog.w(TAG, "No game session found for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); return; } } + final SurfacePackage overlaySurfacePackage = gameSessionRecord.getSurfacePackage(); + final SurfaceControl overlaySurfaceControl = + overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null; mBackgroundExecutor.execute(() -> { - final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId); + final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder = + new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null); + if (overlaySurfaceControl != null) { + SurfaceControl[] excludeLayers = new SurfaceControl[1]; + excludeLayers[0] = overlaySurfaceControl; + layerCaptureArgsBuilder.setExcludeLayers(excludeLayers); + } + final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId, + layerCaptureArgsBuilder); if (bitmap == null) { Slog.w(TAG, "Could not get bitmap for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); diff --git a/services/core/java/com/android/server/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 65be5f06d08f..daf3561d75ce 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -29,7 +29,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioRoutesObserver; import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; @@ -500,12 +500,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void setWiredDeviceConnectionState(int type, - @AudioService.ConnectionState int state, String address, String name, - String caller) { + /*package*/ void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { //TODO move logging here just like in setBluetooth* methods synchronized (mDeviceStateLock) { - mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceInventory.setWiredDeviceConnectionState(attributes, state, caller); } } @@ -531,12 +530,12 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ static final class BtDeviceChangedData { final @Nullable BluetoothDevice mNewDevice; final @Nullable BluetoothDevice mPreviousDevice; - final @NonNull BtProfileConnectionInfo mInfo; + final @NonNull BluetoothProfileConnectionInfo mInfo; final @NonNull String mEventSource; BtDeviceChangedData(@Nullable BluetoothDevice newDevice, @Nullable BluetoothDevice previousDevice, - @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) { + @NonNull BluetoothProfileConnectionInfo info, @NonNull String eventSource) { mNewDevice = newDevice; mPreviousDevice = previousDevice; mInfo = info; @@ -568,9 +567,9 @@ import java.util.concurrent.atomic.AtomicBoolean; mDevice = device; mState = state; mProfile = d.mInfo.getProfile(); - mSupprNoisy = d.mInfo.getSuppressNoisyIntent(); + mSupprNoisy = d.mInfo.isSuppressNoisyIntent(); mVolume = d.mInfo.getVolume(); - mIsLeOutput = d.mInfo.getIsLeOutput(); + mIsLeOutput = d.mInfo.isLeOutput(); mEventSource = d.mEventSource; mAudioSystemDevice = audioDevice; mMusicDevice = AudioSystem.DEVICE_NONE; @@ -641,7 +640,7 @@ import java.util.concurrent.atomic.AtomicBoolean; audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID; break; case BluetoothProfile.LE_AUDIO: - if (d.mInfo.getIsLeOutput()) { + if (d.mInfo.isLeOutput()) { audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET; } else { audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET; @@ -1013,11 +1012,9 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) { synchronized (mDeviceStateLock) { - return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName, - false /*for test*/); + return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 4ae1bd371690..0e290410d288 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -230,19 +230,15 @@ public class AudioDeviceInventory { * A class just for packaging up a set of connection parameters. */ /*package*/ class WiredDeviceConnectionState { - public final int mType; + public final AudioDeviceAttributes mAttributes; public final @AudioService.ConnectionState int mState; - public final String mAddress; - public final String mName; public final String mCaller; public boolean mForTest = false; - /*package*/ WiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { - mType = type; + /*package*/ WiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { + mAttributes = attributes; mState = state; - mAddress = address; - mName = name; mCaller = caller; } } @@ -280,11 +276,10 @@ public class AudioDeviceInventory { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { - mAudioSystem.setDeviceConnectionState( - di.mDeviceType, - AudioSystem.DEVICE_STATE_AVAILABLE, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, - di.mDeviceName, + di.mDeviceName), + AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } } @@ -519,41 +514,45 @@ public class AudioDeviceInventory { /*package*/ void onSetWiredDeviceConnectionState( AudioDeviceInventory.WiredDeviceConnectionState wdcs) { + int type = wdcs.mAttributes.getInternalType(); + AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onSetWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType)) + .set(MediaMetrics.Property.ADDRESS, wdcs.mAttributes.getAddress()) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(type)) .set(MediaMetrics.Property.STATE, wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) - && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state DISCONNECTED"); } - if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, - wdcs.mType, wdcs.mAddress, wdcs.mName, wdcs.mForTest)) { + if (!handleDeviceConnection(wdcs.mAttributes, + wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) { // change of connection state failed, bailout mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") .record(); return; } if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { - if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { + if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/, "onSetWiredDeviceConnectionState state not DISCONNECTED"); } - mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller); + mDeviceBroker.checkMusicActive(type, wdcs.mCaller); } - if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) { + if (type == AudioSystem.DEVICE_OUT_HDMI) { mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller); } - sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName); - updateAudioRoutes(wdcs.mType, wdcs.mState); + sendDeviceConnectionIntent(type, wdcs.mState, + wdcs.mAttributes.getAddress(), wdcs.mAttributes.getName()); + updateAudioRoutes(type, wdcs.mState); } mmi.record(); } @@ -572,12 +571,12 @@ public class AudioDeviceInventory { return; } // Toggle HDMI to retrigger broadcast with proper formats. - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "", - "android"); // disconnect - setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI, - AudioSystem.DEVICE_STATE_AVAILABLE, "", "", - "android"); // reconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect + setWiredDeviceConnectionState( + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), + AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect } mmi.record(); } @@ -707,16 +706,17 @@ public class AudioDeviceInventory { /** * Implements the communication with AudioSystem to (dis)connect a device in the native layers + * @param attributes the attributes of the device * @param connect true if connection - * @param device the device type - * @param address the address of the device - * @param deviceName human-readable name of device * @param isForTesting if true, not calling AudioSystem for the connection as this is * just for testing * @return false if an error was reported by AudioSystem */ - /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address, - String deviceName, boolean isForTesting) { + /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect, + boolean isForTesting) { + int device = attributes.getInternalType(); + String address = attributes.getAddress(); + String deviceName = attributes.getName(); if (AudioService.DEBUG_DEVICES) { Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" + Integer.toHexString(device) + " address:" + address @@ -743,9 +743,8 @@ public class AudioDeviceInventory { if (isForTesting) { res = AudioSystem.AUDIO_STATUS_OK; } else { - res = mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + res = mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); } if (res != AudioSystem.AUDIO_STATUS_OK) { final String reason = "not connecting device 0x" + Integer.toHexString(device) @@ -762,9 +761,8 @@ public class AudioDeviceInventory { mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } else if (!connect && isConnected) { - mAudioSystem.setDeviceConnectionState(device, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName, - AudioSystem.AUDIO_FORMAT_DEFAULT); + mAudioSystem.setDeviceConnectionState(attributes, + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); @@ -941,13 +939,13 @@ public class AudioDeviceInventory { return delay; } - /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, - String address, String name, String caller) { + /*package*/ int setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @AudioService.ConnectionState int state, String caller) { synchronized (mDevicesLock) { - int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE); + int delay = checkSendBecomingNoisyIntentInt( + attributes.getInternalType(), state, AudioSystem.DEVICE_NONE); mDeviceBroker.postSetWiredDeviceConnectionState( - new WiredDeviceConnectionState(type, state, address, name, caller), - delay); + new WiredDeviceConnectionState(attributes, state, caller), delay); return delay; } } @@ -955,8 +953,7 @@ public class AudioDeviceInventory { /*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device, @AudioService.ConnectionState int state) { final WiredDeviceConnectionState connection = new WiredDeviceConnectionState( - device.getInternalType(), state, device.getAddress(), - "test device", "com.android.server.audio"); + device, state, "com.android.server.audio"); connection.mForTest = true; onSetWiredDeviceConnectionState(connection); } @@ -972,8 +969,9 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource); // at this point there could be another A2DP device already connected in APM, but it // doesn't matter as this new one will overwrite the previous one - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec); // TODO: log in MediaMetrics once distinction between connection failure and // double connection is made. @@ -1035,8 +1033,9 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.clearAvrcpAbsoluteVolumeSupported(); - final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); + final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( @@ -1074,8 +1073,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_AVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), @@ -1085,8 +1085,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); @@ -1099,8 +1100,9 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_AVAILABLE, address, name, + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address), @@ -1122,8 +1124,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { - mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, - AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", + mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_HEARING_AID, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); @@ -1140,14 +1143,14 @@ public class AudioDeviceInventory { private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device, String eventSource) { if (device != AudioSystem.DEVICE_NONE) { - /* Audio Policy sees Le Audio similar to A2DP. Let's make sure * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set */ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, - address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name), + AudioSystem.DEVICE_STATE_AVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); @@ -1168,8 +1171,9 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeLeAudioDeviceUnavailable(String address, int device) { if (device != AudioSystem.DEVICE_NONE) { - AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, - address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); + AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address), + AudioSystem.DEVICE_STATE_UNAVAILABLE, + AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 407d42ea510d..05955c3cab44 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -85,7 +85,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -6371,23 +6371,23 @@ public class AudioService extends IAudioService.Stub /** * see AudioManager.setWiredDeviceConnectionState() */ - public void setWiredDeviceConnectionState(int type, - @ConnectionState int state, String address, String name, - String caller) { + public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, + @ConnectionState int state, String caller) { enforceModifyAudioRoutingPermission(); if (state != CONNECTION_STATE_CONNECTED && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); } new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ADDRESS, attributes.getAddress()) .set(MediaMetrics.Property.CLIENT_NAME, caller) - .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type)) - .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(attributes.getInternalType())) + .set(MediaMetrics.Property.NAME, attributes.getName()) .set(MediaMetrics.Property.STATE, state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected") .record(); - mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller); + mDeviceBroker.setWiredDeviceConnectionState(attributes, state, caller); } /** @see AudioManager#setTestDeviceConnectionState(AudioDeviceAttributes, boolean) */ @@ -6434,14 +6434,14 @@ public class AudioService extends IAudioService.Stub * See AudioManager.handleBluetoothActiveDeviceChanged(...) */ public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice, - BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + BluetoothDevice previousDevice, @NonNull BluetoothProfileConnectionInfo info) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Bluetooth is the only caller allowed"); } if (info == null) { - throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device " - + previousDevice + " -> " + newDevice); + throw new IllegalArgumentException("Illegal null BluetoothProfileConnectionInfo for" + + " device " + previousDevice + " -> " + newDevice); } final int profile = info.getProfile(); if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 3137fa5784d2..3225274a8a9b 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -116,10 +116,11 @@ public class AudioServiceEvents { @Override public String eventToString() { return new StringBuilder("setWiredDeviceConnectionState(") - .append(" type:").append(Integer.toHexString(mState.mType)) + .append(" type:").append( + Integer.toHexString(mState.mAttributes.getInternalType())) .append(" state:").append(AudioSystem.deviceStateToString(mState.mState)) - .append(" addr:").append(mState.mAddress) - .append(" name:").append(mState.mName) + .append(" addr:").append(mState.mAttributes.getAddress()) + .append(" name:").append(mState.mAttributes.getName()) .append(") from ").append(mState.mCaller).toString(); } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index a2ba76b6fd6a..f572261c09e5 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -258,19 +258,16 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { } /** - * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} - * @param device + * Same as {@link AudioSystem#setDeviceConnectionState(AudioDeviceAttributes, int, int)} + * @param attributes * @param state - * @param deviceAddress - * @param deviceName * @param codecFormat * @return */ - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { invalidateRoutingCache(); - return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, - codecFormat); + return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat); } /** diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 47f31d505867..3491cd59ebb7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -31,7 +31,7 @@ import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.os.Binder; import android.os.UserHandle; import android.provider.Settings; @@ -40,6 +40,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -489,11 +490,13 @@ public class BtHelper { if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) { mDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, - new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + new BluetoothProfileConnectionInfo(profile), + "mBluetoothProfileServiceListener")); } else { mDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(null, btDevice, - new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + new BluetoothProfileConnectionInfo(profile), + "mBluetoothProfileServiceListener")); } } @@ -503,7 +506,12 @@ public class BtHelper { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); mBluetoothHeadset = headset; - setBtScoActiveDevice(headset != null ? headset.getActiveDevice() : null); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + List<BluetoothDevice> activeDevices = Collections.emptyList(); + if (adapter != null) { + activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET); + } + setBtScoActiveDevice((activeDevices.size() > 0) ? activeDevices.get(0) : null); // Refresh SCO audio state checkScoAudioState(); if (mScoAudioState != SCO_STATE_ACTIVATE_REQ @@ -587,8 +595,9 @@ public class BtHelper { String btDeviceName = getName(btDevice); boolean result = false; if (isActive) { - result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(), - audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName), + isActive); } else { int[] outDeviceTypes = { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, @@ -596,13 +605,15 @@ public class BtHelper { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; for (int outDeviceType : outDeviceTypes) { - result |= mDeviceBroker.handleDeviceConnection( - isActive, outDeviceType, audioDevice.getAddress(), btDeviceName); + result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + outDeviceType, audioDevice.getAddress(), btDeviceName), + isActive); } } // handleDeviceConnection() && result to make sure the method get executed - result = mDeviceBroker.handleDeviceConnection( - isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result; + result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( + inDevice, audioDevice.getAddress(), btDeviceName), + isActive) && result; return result; } diff --git a/services/core/java/com/android/server/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/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index d0ce9ef47c35..5de162ce711c 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -222,6 +222,22 @@ abstract class DisplayDevice { } /** + * Returns the system preferred display mode. + */ + public Display.Mode getSystemPreferredDisplayModeLocked() { + return EMPTY_DISPLAY_MODE; + } + + /** + * Returns the display mode that was being used when this display was first found by + * display manager. + * @hide + */ + public Display.Mode getActiveDisplayModeAtStartLocked() { + return EMPTY_DISPLAY_MODE; + } + + /** * Sets the requested color mode. */ public void setRequestedColorModeLocked(int colorMode) { 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/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 4e88acd11fac..7f1482e6dde1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1833,6 +1833,16 @@ public final class DisplayManagerService extends SystemService { } } + Display.Mode getSystemPreferredDisplayModeInternal(int displayId) { + synchronized (mSyncRoot) { + final DisplayDevice device = getDeviceForDisplayLocked(displayId); + if (device == null) { + return null; + } + return device.getSystemPreferredDisplayModeLocked(); + } + } + void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) { mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled); } @@ -2183,6 +2193,16 @@ public final class DisplayManagerService extends SystemService { } } + Display.Mode getActiveDisplayModeAtStart(int displayId) { + synchronized (mSyncRoot) { + final DisplayDevice device = getDeviceForDisplayLocked(displayId); + if (device == null) { + return null; + } + return device.getActiveDisplayModeAtStartLocked(); + } + } + void setAmbientColorTemperatureOverride(float cct) { synchronized (mSyncRoot) { final DisplayPowerController displayPowerController = mDisplayPowerControllers.get( @@ -3471,6 +3491,16 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public Display.Mode getSystemPreferredDisplayMode(int displayId) { + final long token = Binder.clearCallingIdentity(); + try { + return getSystemPreferredDisplayModeInternal(displayId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) { mContext.enforceCallingOrSelfPermission( Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index a9875c873bbb..bfdac5781b75 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -68,6 +68,8 @@ class DisplayManagerShellCommand extends ShellCommand { return clearUserPreferredDisplayMode(); case "get-user-preferred-display-mode": return getUserPreferredDisplayMode(); + case "get-active-display-mode-at-start": + return getActiveDisplayModeAtStart(); case "set-match-content-frame-rate-pref": return setMatchContentFrameRateUserPreference(); case "get-match-content-frame-rate-pref": @@ -125,6 +127,9 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Returns the user preferred display mode or null if no mode is set by user." + "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is " + "returned, else global display mode is returned."); + pw.println(" get-active-display-mode-at-start DISPLAY_ID"); + pw.println(" Returns the display mode which was found at boot time of display with " + + "id = DISPLAY_ID"); pw.println(" set-match-content-frame-rate-pref PREFERENCE"); pw.println(" Sets the match content frame rate preference as PREFERENCE "); pw.println(" get-match-content-frame-rate-pref"); @@ -298,6 +303,30 @@ class DisplayManagerShellCommand extends ShellCommand { return 0; } + private int getActiveDisplayModeAtStart() { + final String displayIdText = getNextArg(); + if (displayIdText == null) { + getErrPrintWriter().println("Error: no displayId specified"); + return 1; + } + final int displayId; + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid displayId"); + return 1; + } + + Display.Mode mode = mService.getActiveDisplayModeAtStart(displayId); + if (mode == null) { + getOutPrintWriter().println("Boot display mode: null"); + return 0; + } + getOutPrintWriter().println("Boot display mode: " + mode.getPhysicalWidth() + " " + + mode.getPhysicalHeight() + " " + mode.getRefreshRate()); + return 0; + } + private int setMatchContentFrameRateUserPreference() { final String matchContentFrameRatePrefText = getNextArg(); if (matchContentFrameRatePrefText == null) { 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/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 3a9ef0a83f6b..a31c2314bd1f 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -192,8 +192,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT; private int mDefaultModeId = INVALID_MODE_ID; + private int mSystemPreferredModeId = INVALID_MODE_ID; private int mDefaultModeGroup; private int mUserPreferredModeId = INVALID_MODE_ID; + // This is used only for the purpose of testing, to verify if the mode was correct when the + // device started or booted. + private int mActiveDisplayModeAtStartId = INVALID_MODE_ID; private Display.Mode mUserPreferredMode; private int mActiveModeId = INVALID_MODE_ID; private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = @@ -208,7 +212,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mSidekickActive; private SidekickInternal mSidekickInternal; private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo; - // The supported display modes according in SurfaceFlinger + // The supported display modes according to SurfaceFlinger private SurfaceControl.DisplayMode[] mSfDisplayModes; // The active display mode in SurfaceFlinger private SurfaceControl.DisplayMode mActiveSfDisplayMode; @@ -230,6 +234,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay, mSurfaceControlProxy); mDisplayDeviceConfig = null; + mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId; } @Override @@ -238,12 +243,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { } /** + * Returns the boot display mode of this display. + * @hide + */ + @Override + public Display.Mode getActiveDisplayModeAtStartLocked() { + return findMode(mActiveDisplayModeAtStartId); + } + + /** * Returns true if there is a change. **/ public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo, SurfaceControl.DynamicDisplayInfo dynamicInfo, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { - boolean changed = updateDisplayModesLocked( + boolean changed = + updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode); + changed |= updateDisplayModesLocked( dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs); changed |= updateStaticInfo(staticInfo); changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, @@ -369,8 +385,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { // For a new display, we need to initialize the default mode ID. if (mDefaultModeId == INVALID_MODE_ID) { - mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = mActiveSfDisplayMode.group; + mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID + ? mSystemPreferredModeId : activeRecord.mMode.getModeId(); + mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID + ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group + : mActiveSfDisplayMode.group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); @@ -531,6 +550,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateSystemPreferredDisplayMode(int modeId) { + if (!mSurfaceControlProxy.getBootDisplayModeSupport() + || mSystemPreferredModeId == modeId) { + return false; + } + mSystemPreferredModeId = modeId; + return true; + } + private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, int modeId) { for (SurfaceControl.DisplayMode mode : supportedModes) { @@ -857,6 +885,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (oldModeId != getPreferredModeId()) { updateDeviceInfoLocked(); } + + if (!mSurfaceControlProxy.getBootDisplayModeSupport()) { + return; + } + if (mUserPreferredMode == null) { + mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked()); + } else { + mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(), + mUserPreferredMode.getModeId()); + } } @Override @@ -865,6 +903,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public Display.Mode getSystemPreferredDisplayModeLocked() { + return findMode(mSystemPreferredModeId); + } + + @Override public void setRequestedColorModeLocked(int colorMode) { requestColorModeLocked(colorMode); } @@ -1072,6 +1115,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { return matchingModeId; } + // Returns a mode with id = modeId. + private Display.Mode findMode(int modeId) { + for (int i = 0; i < mSupportedModes.size(); i++) { + Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode; + if (supportedMode.getModeId() == modeId) { + return supportedMode; + } + } + return null; + } + // Returns a mode with resolution (width, height) and/or refreshRate. If any one of the // resolution or refresh-rate is valid, a mode having the valid parameters is returned. private Display.Mode findMode(int width, int height, float refreshRate) { @@ -1318,6 +1372,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { return SurfaceControl.setActiveColorMode(displayToken, colorMode); } + public boolean getBootDisplayModeSupport() { + return SurfaceControl.getBootDisplayModeSupport(); + } + + public void setBootDisplayMode(IBinder displayToken, int modeId) { + SurfaceControl.setBootDisplayMode(displayToken, modeId); + } + + public void clearBootDisplayMode(IBinder displayToken) { + SurfaceControl.clearBootDisplayMode(displayToken); + } + public void setAutoLowLatencyMode(IBinder displayToken, boolean on) { SurfaceControl.setAutoLowLatencyMode(displayToken, on); @@ -1340,7 +1406,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits, displayBacklight, displayNits); } - } static class BacklightAdapter { 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 64b4da7c5bba..940c25c08ad1 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; @@ -64,6 +63,7 @@ import android.os.CombinedVibration; import android.os.Environment; import android.os.Handler; import android.os.IBinder; +import android.os.IInputConstants; import android.os.IVibratorStateListener; import android.os.InputEventInjectionResult; import android.os.InputEventInjectionSync; @@ -150,8 +150,6 @@ public class InputManagerService extends IInputManager.Stub static final String TAG = "InputManager"; static final boolean DEBUG = false; - private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = false; - 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 +196,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 +216,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,25 +257,36 @@ 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(); + + private final Object mAdditionalDisplayInputPropertiesLock = new Object(); + // Forces the MouseCursorController to target a specific display id. - @GuardedBy("mPointerDisplayIdLock") + @GuardedBy("mAdditionalDisplayInputPropertiesLock") private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY; + @GuardedBy("mAdditionalDisplayInputPropertiesLock") + private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties = + new SparseArray<>(); + @GuardedBy("mAdditionalDisplayInputPropertiesLock") + private int mIconType = PointerIcon.TYPE_NOT_SPECIFIED; + @GuardedBy("mAdditionalDisplayInputPropertiesLock") + private PointerIcon mIcon; // Holds all the registered gesture monitors that are implemented as spy windows. The spy @@ -303,11 +312,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,38 +592,27 @@ 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()]; + synchronized (mAdditionalDisplayInputPropertiesLock) { + final DisplayViewport[] vArray = new DisplayViewport[viewports.size()]; for (int i = viewports.size() - 1; i >= 0; --i) { vArray[i] = viewports.get(i); } - nativeSetDisplayViewports(mPtr, vArray); + nativeSetDisplayViewports(mPtr, vArray); + + if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { + final AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId); + if (properties != null) { + updatePointerIconVisibleLocked(properties.pointerIconVisible); + updatePointerAccelerationLocked(properties.pointerAcceleration); + return; + } + } + updatePointerIconVisibleLocked( + AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE); + updatePointerAccelerationLocked(IInputConstants.DEFAULT_POINTER_ACCELERATION); + } } /** @@ -720,8 +719,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 +788,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 +867,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 +933,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 +1116,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 +1328,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 +1356,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 +1365,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 +1416,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 +1449,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 +1479,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 +1490,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 +1532,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 +1558,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 +1566,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())) { @@ -1850,10 +1821,60 @@ public class InputManagerService extends IInputManager.Stub nativeSetPointerSpeed(mPtr, speed); } - private void setPointerAcceleration(float acceleration) { + private void setPointerAcceleration(float acceleration, int displayId) { + synchronized (mAdditionalDisplayInputPropertiesLock) { + AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(displayId); + if (properties == null) { + properties = new AdditionalDisplayInputProperties(); + mAdditionalDisplayInputProperties.put(displayId, properties); + } + properties.pointerAcceleration = acceleration; + if (properties.allDefaults()) { + mAdditionalDisplayInputProperties.remove(displayId); + } + if (mOverriddenPointerDisplayId == displayId) { + updatePointerAccelerationLocked(acceleration); + } + } + } + + @GuardedBy("mAdditionalDisplayInputPropertiesLock") + private void updatePointerAccelerationLocked(float acceleration) { nativeSetPointerAcceleration(mPtr, acceleration); } + private void setPointerIconVisible(boolean visible, int displayId) { + synchronized (mAdditionalDisplayInputPropertiesLock) { + AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(displayId); + if (properties == null) { + properties = new AdditionalDisplayInputProperties(); + mAdditionalDisplayInputProperties.put(displayId, properties); + } + properties.pointerIconVisible = visible; + if (properties.allDefaults()) { + mAdditionalDisplayInputProperties.remove(displayId); + } + if (mOverriddenPointerDisplayId == displayId) { + updatePointerIconVisibleLocked(visible); + } + } + } + + @GuardedBy("mAdditionalDisplayInputPropertiesLock") + private void updatePointerIconVisibleLocked(boolean visible) { + if (visible) { + if (mIconType == PointerIcon.TYPE_CUSTOM) { + nativeSetCustomPointerIcon(mPtr, mIcon); + } else { + nativeSetPointerIconType(mPtr, mIconType); + } + } else { + nativeSetPointerIconType(mPtr, PointerIcon.TYPE_NULL); + } + } + private void registerPointerSpeedSettingObserver() { mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.POINTER_SPEED), true, @@ -1870,7 +1891,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; } @@ -1988,13 +2009,27 @@ public class InputManagerService extends IInputManager.Stub } private void setVirtualMousePointerDisplayId(int displayId) { - synchronized (mPointerDisplayIdLock) { + synchronized (mAdditionalDisplayInputPropertiesLock) { mOverriddenPointerDisplayId = displayId; + if (displayId != Display.INVALID_DISPLAY) { + final AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(displayId); + if (properties != null) { + updatePointerAccelerationLocked(properties.pointerAcceleration); + updatePointerIconVisibleLocked(properties.pointerIconVisible); + } + } } // TODO(b/215597605): trigger MousePositionTracker update nativeNotifyPointerDisplayIdChanged(mPtr); } + private int getVirtualMousePointerDisplayId() { + synchronized (mAdditionalDisplayInputPropertiesLock) { + return mOverriddenPointerDisplayId; + } + } + private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) { nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible); } @@ -2134,7 +2169,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 +2219,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 +2261,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) { @@ -2283,15 +2319,44 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override - public void setPointerIconType(int iconId) { - nativeSetPointerIconType(mPtr, iconId); + public void setPointerIconType(int iconType) { + if (iconType == PointerIcon.TYPE_CUSTOM) { + throw new IllegalArgumentException("Use setCustomPointerIcon to set custom pointers"); + } + synchronized (mAdditionalDisplayInputPropertiesLock) { + mIcon = null; + mIconType = iconType; + if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { + final AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId); + if (properties == null || properties.pointerIconVisible) { + nativeSetPointerIconType(mPtr, mIconType); + } + } else { + nativeSetPointerIconType(mPtr, mIconType); + } + } } // Binder call @Override public void setCustomPointerIcon(PointerIcon icon) { Objects.requireNonNull(icon); - nativeSetCustomPointerIcon(mPtr, icon); + synchronized (mAdditionalDisplayInputPropertiesLock) { + mIconType = PointerIcon.TYPE_CUSTOM; + mIcon = icon; + if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { + final AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId); + if (properties == null || properties.pointerIconVisible) { + // Only set the icon if it is not currently hidden; otherwise, it will be set + // once it's no longer hidden. + nativeSetCustomPointerIcon(mPtr, mIcon); + } + } else { + nativeSetCustomPointerIcon(mPtr, mIcon); + } + } } /** @@ -2383,7 +2448,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 +2480,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 +2494,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 +2562,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 +2625,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 +2644,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"); @@ -2620,6 +2685,7 @@ public class InputManagerService extends IInputManager.Stub pw.println("Input Manager Service (Java) State:"); dumpAssociations(pw, " " /*prefix*/); dumpSpyWindowGestureMonitors(pw, " " /*prefix*/); + dumpDisplayInputPropertiesValues(pw, " " /* prefix */); } private void dumpAssociations(PrintWriter pw, String prefix) { @@ -2660,6 +2726,25 @@ public class InputManagerService extends IInputManager.Stub } } + private void dumpDisplayInputPropertiesValues(PrintWriter pw, String prefix) { + synchronized (mAdditionalDisplayInputPropertiesLock) { + if (mAdditionalDisplayInputProperties.size() != 0) { + pw.println(prefix + "mAdditionalDisplayInputProperties:"); + for (int i = 0; i < mAdditionalDisplayInputProperties.size(); i++) { + pw.println(prefix + " displayId: " + + mAdditionalDisplayInputProperties.keyAt(i)); + final AdditionalDisplayInputProperties properties = + mAdditionalDisplayInputProperties.valueAt(i); + pw.println(prefix + " pointerAcceleration: " + properties.pointerAcceleration); + pw.println(prefix + " pointerIconVisible: " + properties.pointerIconVisible); + } + } + if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { + pw.println(prefix + "mOverriddenPointerDisplayId: " + mOverriddenPointerDisplayId); + } + } + } + private boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == Process.myPid()) { @@ -2683,17 +2768,19 @@ public class InputManagerService extends IInputManager.Stub synchronized (mInputFilterLock) { } synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ } - synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ } synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ } + synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ } nativeMonitor(mPtr); } // 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 +2794,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 +2827,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 +2840,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private void notifyInputChannelBroken(IBinder token) { synchronized (mInputMonitors) { if (mInputMonitors.containsKey(token)) { @@ -2762,16 +2851,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 +2879,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 +2902,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 +2925,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 +2955,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 +2973,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 +2990,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 +3004,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 +3092,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private String[] getInputPortAssociations() { final Map<String, Integer> associations = new HashMap<>(mStaticAssociations); @@ -2996,6 +3105,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback + @SuppressWarnings("unused") private String[] getInputUniqueIdAssociations() { final Map<String, String> associations; synchronized (mAssociationsLock) { @@ -3016,46 +3126,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,8 +3220,9 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private int getPointerDisplayId() { - synchronized (mPointerDisplayIdLock) { + synchronized (mAdditionalDisplayInputPropertiesLock) { // Prefer the override to all other displays. if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) { return mOverriddenPointerDisplayId; @@ -3112,6 +3232,7 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. + @SuppressWarnings("unused") private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) { if (!mSystemReady) { return null; @@ -3123,19 +3244,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 +3262,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 +3412,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 +3454,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 +3466,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 +3505,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); } } @@ -3592,13 +3718,18 @@ public class InputManagerService extends IInputManager.Stub } @Override + public int getVirtualMousePointerDisplayId() { + return InputManagerService.this.getVirtualMousePointerDisplayId(); + } + + @Override public PointF getCursorPosition() { return mWindowManagerCallbacks.getCursorPosition(); } @Override - public void setPointerAcceleration(float acceleration) { - InputManagerService.this.setPointerAcceleration(acceleration); + public void setPointerAcceleration(float acceleration, int displayId) { + InputManagerService.this.setPointerAcceleration(acceleration, displayId); } @Override @@ -3607,6 +3738,11 @@ public class InputManagerService extends IInputManager.Stub } @Override + public void setPointerIconVisible(boolean visible, int displayId) { + InputManagerService.this.setPointerIconVisible(visible, displayId); + } + + @Override public void registerLidSwitchCallback(LidSwitchCallback callbacks) { registerLidSwitchCallbackInternal(callbacks); } @@ -3633,4 +3769,21 @@ public class InputManagerService extends IInputManager.Stub new InputShellCommand().exec(this, in, out, err, args, callback, resultReceiver); } + private static class AdditionalDisplayInputProperties { + + static final boolean DEFAULT_POINTER_ICON_VISIBLE = true; + static final float DEFAULT_POINTER_ACCELERATION = + (float) IInputConstants.DEFAULT_POINTER_ACCELERATION; + + // The pointer acceleration for this display. + public float pointerAcceleration = DEFAULT_POINTER_ACCELERATION; + + // Whether the pointer icon should be visible or hidden on this display. + public boolean pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE; + + public boolean allDefaults() { + return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0 + && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE; + } + } } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index 4c2616667a02..9846a2ba48a4 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -19,7 +19,6 @@ package com.android.server.inputmethod; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import android.annotation.NonNull; -import android.graphics.Rect; import android.os.Process; import android.view.InputApplicationHandle; import android.view.InputChannel; @@ -33,8 +32,11 @@ final class HandwritingEventReceiverSurface { public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName(); static final boolean DEBUG = HandwritingModeController.DEBUG; - private final int mClientPid; - private final int mClientUid; + // Place the layer below the highest layer to place it under gesture monitors. If the surface + // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface + // is intercepting. + // TODO(b/217538817): Specify the ordering in WM by usage. + private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1; private final InputApplicationHandle mApplicationHandle; private final InputWindowHandle mWindowHandle; @@ -44,9 +46,6 @@ final class HandwritingEventReceiverSurface { HandwritingEventReceiverSurface(String name, int displayId, @NonNull SurfaceControl sc, @NonNull InputChannel inputChannel) { - // Initialized the window as being owned by the system. - mClientPid = Process.myPid(); - mClientUid = Process.myUid(); mApplicationHandle = new InputApplicationHandle(null, name, DEFAULT_DISPATCHING_TIMEOUT_MILLIS); @@ -57,29 +56,26 @@ final class HandwritingEventReceiverSurface { mWindowHandle.name = name; mWindowHandle.token = mClientChannel.getToken(); mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; - mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; mWindowHandle.visible = true; mWindowHandle.focusable = false; mWindowHandle.hasWallpaper = false; mWindowHandle.paused = false; - mWindowHandle.ownerPid = mClientPid; - mWindowHandle.ownerUid = mClientUid; + mWindowHandle.ownerPid = Process.myPid(); + mWindowHandle.ownerUid = Process.myUid(); mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY | WindowManager.LayoutParams.INPUT_FEATURE_INTERCEPTS_STYLUS; mWindowHandle.scaleFactor = 1.0f; mWindowHandle.trustedOverlay = true; - mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface as crop */); + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.setInputWindowInfo(mInputSurface, mWindowHandle); - t.setLayer(mInputSurface, Integer.MAX_VALUE); + t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER); t.setPosition(mInputSurface, 0, 0); - // Use an arbitrarily large crop that is positioned at the origin. The crop determines the - // bounds and the coordinate space of the input events, so it must start at the origin to - // receive input in display space. - // TODO(b/210039666): fix this in SurfaceFlinger and avoid the hack. - t.setCrop(mInputSurface, new Rect(0, 0, 10000, 10000)); + t.setCrop(mInputSurface, null /* crop to parent surface */); t.show(mInputSurface); t.apply(); 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/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java new file mode 100644 index 000000000000..6b442a6a395e --- /dev/null +++ b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.logcat; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.ServiceManager; +import android.os.logcat.ILogcatManagerService; +import android.util.Slog; +import android.view.View; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + + +/** + * This dialog is shown to the user before an activity in a harmful app is launched. + * + * See {@code PackageManager.setLogcatAppInfo} for more info. + */ +public class LogAccessConfirmationActivity extends AlertActivity implements + DialogInterface.OnClickListener { + private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName(); + + private String mPackageName; + private IntentSender mTarget; + private final ILogcatManagerService mLogcatManagerService = + ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat")); + + private int mUid; + private int mGid; + private int mPid; + private int mFd; + + private static final String EXTRA_UID = "uid"; + private static final String EXTRA_GID = "gid"; + private static final String EXTRA_PID = "pid"; + private static final String EXTRA_FD = "fd"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Intent intent = getIntent(); + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mUid = intent.getIntExtra("uid", 0); + mGid = intent.getIntExtra("gid", 0); + mPid = intent.getIntExtra("pid", 0); + mFd = intent.getIntExtra("fd", 0); + + final AlertController.AlertParams p = mAlertParams; + p.mTitle = getString(R.string.log_access_confirmation_title); + p.mView = createView(); + + p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow); + p.mPositiveButtonListener = this; + p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny); + p.mNegativeButtonListener = this; + + mAlert.installContent(mAlertParams); + } + + private View createView() { + final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog, + null /*root*/); + ((TextView) view.findViewById(R.id.app_name_text)) + .setText(mPackageName); + ((TextView) view.findViewById(R.id.message)) + .setText(getIntent().getExtras().getString("body")); + return view; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + try { + mLogcatManagerService.approve(mUid, mGid, mPid, mFd); + } catch (Throwable t) { + Slog.e(TAG, "Could not start the LogcatManagerService.", t); + } + finish(); + break; + case DialogInterface.BUTTON_NEGATIVE: + try { + mLogcatManagerService.decline(mUid, mGid, mPid, mFd); + } catch (Throwable t) { + Slog.e(TAG, "Could not start the LogcatManagerService.", t); + } + finish(); + break; + } + } + + /** + * Create the Intent for a LogAccessConfirmationActivity. + */ + public static Intent createIntent(Context context, String targetPackageName, + IntentSender target, int uid, int gid, int pid, int fd) { + final Intent intent = new Intent(); + intent.setClass(context, LogAccessConfirmationActivity.class); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName); + intent.putExtra(EXTRA_UID, uid); + intent.putExtra(EXTRA_GID, gid); + intent.putExtra(EXTRA_PID, pid); + intent.putExtra(EXTRA_FD, fd); + + return intent; + } + +} diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index ff6372aec3bd..140c6d48b57b 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -16,20 +16,36 @@ package com.android.server.logcat; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.ActivityManagerInternal; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.ILogd; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.logcat.ILogcatManagerService; import android.util.Slog; +import com.android.internal.R; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import com.android.server.SystemService; +import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * Service responsible for manage the access to Logcat. + * Service responsible for managing the access to Logcat. */ public final class LogcatManagerService extends SystemService { @@ -38,6 +54,43 @@ public final class LogcatManagerService extends SystemService { private final BinderService mBinderService; private final ExecutorService mThreadExecutor; private ILogd mLogdService; + private NotificationManager mNotificationManager; + private @NonNull ActivityManager mActivityManager; + private ActivityManagerInternal mActivityManagerInternal; + private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2; + private static int sUidImportanceListenerCount = 0; + private static final int AID_SHELL_UID = 2000; + + // TODO This allowlist is just a temporary workaround for the tests: + // FrameworksServicesTests + // PlatformRuleTests + // After adapting the test suites, the allowlist will be removed in + // the upcoming bug fix patches. + private static final String[] ALLOWABLE_TESTING_PACKAGES = { + "android.platform.test.rule.tests", + "com.android.frameworks.servicestests" + }; + + // TODO Same as the above ALLOWABLE_TESTING_PACKAGES. + private boolean isAllowableTestingPackage(int uid) { + PackageManager pm = mContext.getPackageManager(); + + String[] packageNames = pm.getPackagesForUid(uid); + + if (ArrayUtils.isEmpty(packageNames)) { + return false; + } + + for (String name : packageNames) { + Slog.e(TAG, "isAllowableTestingPackage: " + name); + + if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) { + return true; + } + } + + return false; + }; private final class BinderService extends ILogcatManagerService.Stub { @Override @@ -51,6 +104,197 @@ public final class LogcatManagerService extends SystemService { // the logd data access is finished. mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false)); } + + @Override + public void approve(int uid, int gid, int pid, int fd) { + try { + getLogdService().approve(uid, gid, pid, fd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + public void decline(int uid, int gid, int pid, int fd) { + try { + getLogdService().decline(uid, gid, pid, fd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private ILogd getLogdService() { + synchronized (LogcatManagerService.this) { + if (mLogdService == null) { + LogcatManagerService.this.addLogdService(); + } + return mLogdService; + } + } + + private String getBodyString(Context context, String callingPackage, int uid) { + PackageManager pm = context.getPackageManager(); + try { + return context.getString( + com.android.internal.R.string.log_access_confirmation_body, + pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO, + UserHandle.getUserId(uid)).loadLabel(pm)); + } catch (NameNotFoundException e) { + // App name is unknown. + return null; + } + } + + private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid, + int fd) { + + final ActivityManagerInternal activityManagerInternal = + LocalServices.getService(ActivityManagerInternal.class); + + PackageManager pm = mContext.getPackageManager(); + String packageName = activityManagerInternal.getPackageNameByPid(pid); + if (packageName != null) { + String notificationBody = getBodyString(mContext, packageName, uid); + + final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext, + packageName, null, uid, gid, pid, fd); + + if (notificationBody == null) { + // Decline the logd access if the nofitication body is unknown + Slog.e(TAG, "Unknown notification body, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + // TODO Next version will replace notification with dialogue + // per UX guidance. + generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody, + mIntent); + return; + + } + + String[] packageNames = pm.getPackagesForUid(uid); + + if (ArrayUtils.isEmpty(packageNames)) { + // Decline the logd access if the app name is unknown + Slog.e(TAG, "Unknown calling package name, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + String firstPackageName = packageNames[0]; + + if (firstPackageName == null || firstPackageName.length() == 0) { + // Decline the logd access if the package name from uid is unknown + Slog.e(TAG, "Unknown calling package name, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + String notificationBody = getBodyString(mContext, firstPackageName, uid); + + final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext, + firstPackageName, null, uid, gid, pid, fd); + + if (notificationBody == null) { + Slog.e(TAG, "Unknown notification body, declining the logd access"); + declineLogdAccess(uid, gid, pid, fd); + return; + } + + // TODO Next version will replace notification with dialogue + // per UX guidance. + generateNotificationWithBodyContent(notificationId, clientInfo, + notificationBody, mIntent); + } + + private void declineLogdAccess(int uid, int gid, int pid, int fd) { + try { + getLogdService().decline(uid, gid, pid, fd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions ", ex); + } + } + + private void generateNotificationWithBodyContent(int notificationId, String clientInfo, + String notificationBody, Intent intent) { + final Notification.Builder notificationBuilder = new Notification.Builder( + mContext, + SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.setIdentifier(String.valueOf(notificationId) + clientInfo); + intent.putExtra("body", notificationBody); + + notificationBuilder + .setSmallIcon(R.drawable.ic_info) + .setContentTitle( + mContext.getString(R.string.log_access_confirmation_title)) + .setContentText(notificationBody) + .setContentIntent( + PendingIntent.getActivity(mContext, 0, intent, + PendingIntent.FLAG_IMMUTABLE)) + .setTicker(mContext.getString(R.string.log_access_confirmation_title)) + .setOnlyAlertOnce(true) + .setAutoCancel(true); + mNotificationManager.notify(notificationId, notificationBuilder.build()); + } + + /** + * A class which watches an uid for background access and notifies the logdMonitor when + * the package status becomes foreground (importance change) + */ + private class UidImportanceListener implements ActivityManager.OnUidImportanceListener { + private final int mExpectedUid; + private final int mExpectedGid; + private final int mExpectedPid; + private final int mExpectedFd; + private int mExpectedImportance; + private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE; + + UidImportanceListener(int uid, int gid, int pid, int fd, int importance) { + mExpectedUid = uid; + mExpectedGid = gid; + mExpectedPid = pid; + mExpectedFd = fd; + mExpectedImportance = importance; + } + + @Override + public void onUidImportance(int uid, int importance) { + if (uid == mExpectedUid) { + mCurrentImportance = importance; + + /** + * 1) If the process status changes to foreground, send a notification + * for user consent. + * 2) If the process status remains background, we decline logd access request. + **/ + if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) { + String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd); + sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid, + mExpectedFd); + mActivityManager.removeOnUidImportanceListener(this); + + synchronized (LogcatManagerService.this) { + sUidImportanceListenerCount--; + } + } else { + try { + getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions ", ex); + } + } + } + } + } + + private static String getClientInfo(int uid, int gid, int pid, int fd) { + return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID=" + + Integer.toString(pid) + " FD=" + Integer.toString(fd); } private class LogdMonitor implements Runnable { @@ -74,9 +318,7 @@ public final class LogcatManagerService extends SystemService { } /** - * The current version grant the permission by default. - * And track the logd access. - * The next version will generate a prompt for users. + * LogdMonitor generates a prompt for users. * The users decide whether the logd access is allowed. */ @Override @@ -86,10 +328,61 @@ public final class LogcatManagerService extends SystemService { } if (mStart) { - try { - mLogdService.approve(mUid, mGid, mPid, mFd); - } catch (RemoteException ex) { - Slog.e(TAG, "Fails to call remote functions ", ex); + + // TODO See the comments of ALLOWABLE_TESTING_PACKAGES. + if (isAllowableTestingPackage(mUid)) { + try { + getLogdService().approve(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + return; + } + + // If the access request is coming from adb shell, approve the logd access + if (mUid == AID_SHELL_UID) { + try { + getLogdService().approve(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + return; + } + + final int procState = LocalServices.getService(ActivityManagerInternal.class) + .getUidProcessState(mUid); + // If the process is foreground, send a notification for user consent + if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + String clientInfo = getClientInfo(mUid, mGid, mPid, mFd); + sendNotification(0, clientInfo, mUid, mGid, mPid, mFd); + } else { + /** + * If the process is background, add a background process change listener and + * monitor if the process status changes. + * To avoid clients registering multiple listeners, we limit the number of + * maximum listeners to MAX_UID_IMPORTANCE_COUNT_LISTENER. + **/ + if (mActivityManager == null) { + return; + } + + synchronized (LogcatManagerService.this) { + if (sUidImportanceListenerCount < MAX_UID_IMPORTANCE_COUNT_LISTENER) { + // Trigger addOnUidImportanceListener when there is an update from + // the importance of the process + mActivityManager.addOnUidImportanceListener(new UidImportanceListener( + mUid, mGid, mPid, mFd, + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE), + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); + sUidImportanceListenerCount++; + } else { + try { + getLogdService().decline(mUid, mGid, mPid, mFd); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } } } } @@ -100,6 +393,8 @@ public final class LogcatManagerService extends SystemService { mContext = context; mBinderService = new BinderService(); mThreadExecutor = Executors.newCachedThreadPool(); + mActivityManager = context.getSystemService(ActivityManager.class); + mNotificationManager = mContext.getSystemService(NotificationManager.class); } @Override @@ -114,5 +409,4 @@ public final class LogcatManagerService extends SystemService { private void addLogdService() { mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd")); } - } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 91de9e559e13..728782ccee0b 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -45,7 +45,6 @@ import android.util.SparseIntArray; import com.android.internal.R; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -448,15 +447,16 @@ class BluetoothRouteProvider { case BluetoothProfile.A2DP: mA2dpProfile = (BluetoothA2dp) proxy; // It may contain null. - activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice()); + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP); break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = (BluetoothHearingAid) proxy; - activeDevices = mHearingAidProfile.getActiveDevices(); + activeDevices = mBluetoothAdapter.getActiveDevices( + BluetoothProfile.HEARING_AID); break; case BluetoothProfile.LE_AUDIO: mLeAudioProfile = (BluetoothLeAudio) proxy; - activeDevices = mLeAudioProfile.getActiveDevices(); + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO); break; default: return; diff --git a/services/core/java/com/android/server/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/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index 9f086e6e4a61..d745a2325b22 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -48,8 +48,8 @@ import com.android.server.SystemServerInitThreadPool; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.pkg.SELinuxUtil; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SELinuxUtil; import dalvik.system.VMRuntime; @@ -549,6 +549,10 @@ final class AppDataHelper { } public void migrateKeyStoreData(int previousAppId, int appId) { + // If previous UID is system UID, declaring inheritKeyStoreKeys is not supported. + // Silently ignore the request to migrate keys. + if (previousAppId == Process.SYSTEM_UID) return; + for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) { int srcUid = UserHandle.getUid(userId, previousAppId); int destUid = UserHandle.getUid(userId, appId); diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index ba89916e6dfa..53eb9cf7d9fe 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -293,7 +293,8 @@ final class DexOptHelper { public ArraySet<String> getOptimizablePackages() { ArraySet<String> pkgs = new ArraySet<>(); mPm.forEachPackageState(packageState -> { - if (mPm.mPackageDexOptimizer.canOptimizePackage(packageState.getPkg())) { + final AndroidPackage pkg = packageState.getPkg(); + if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { pkgs.add(packageState.getPackageName()); } }); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index a5b42f03b6df..69d498794e64 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -182,7 +182,7 @@ public class PackageDexOptimizer { mInjector = injector; } - boolean canOptimizePackage(AndroidPackage pkg) { + boolean canOptimizePackage(@NonNull AndroidPackage pkg) { // We do not dexopt a package with no code. // Note that the system package is marked as having no code, however we can // still optimize it via dexoptSystemServerPath. diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index d2abc69ad6f2..bd32d03f8b2c 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -29,10 +29,8 @@ import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; import android.content.pm.SigningInfo; -import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.content.pm.overlay.OverlayPaths; -import android.os.PersistableBundle; import android.os.UserHandle; import android.service.pm.PackageProto; import android.util.ArrayMap; @@ -50,7 +48,6 @@ import com.android.server.pm.pkg.AndroidPackageApi; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUnserialized; -import com.android.server.pm.pkg.PackageUserState; import com.android.server.pm.pkg.PackageUserStateImpl; import com.android.server.pm.pkg.PackageUserStateInternal; import com.android.server.pm.pkg.SuspendParams; @@ -662,10 +659,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal usesStaticLibrariesVersions = other.usesStaticLibrariesVersions != null ? Arrays.copyOf(other.usesStaticLibrariesVersions, other.usesStaticLibrariesVersions.length) : null; - mUserStates.clear(); for (int i = 0; i < other.mUserStates.size(); i++) { - mUserStates.put(other.mUserStates.keyAt(i), other.mUserStates.valueAt(i)); + mUserStates.put(other.mUserStates.keyAt(i), + new PackageUserStateImpl(this, other.mUserStates.valueAt(i))); } if (mOldCodePaths != null) { @@ -686,7 +683,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal PackageUserStateImpl modifyUserState(int userId) { PackageUserStateImpl state = mUserStates.get(userId); if (state == null) { - state = new PackageUserStateImpl(); + state = new PackageUserStateImpl(this); mUserStates.put(userId, state); onChanged(); } @@ -696,7 +693,7 @@ public class PackageSetting extends SettingBase implements PackageStateInternal public PackageUserStateImpl getOrCreateUserState(@UserIdInt int userId) { PackageUserStateImpl state = mUserStates.get(userId); if (state == null) { - state = new PackageUserStateImpl(); + state = new PackageUserStateImpl(this); mUserStates.put(userId, state); } return state; @@ -1491,10 +1488,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal } @DataClass.Generated( - time = 1640923794772L, + time = 1643648635766L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java", - inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)") + inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override int getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)") @Deprecated private void __metadata() {} 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 edc0e3d64c42..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 @@ -1318,7 +1310,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (op < 0) { // Bg location is one-off runtime modifier permission and has no app op - if (sPlatformPermissions.contains(permission) + if (sPlatformPermissions.containsKey(permission) && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission) && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) { Slog.wtf(LOG_TAG, "Platform runtime permission " + permission 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/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java index 25abcb3bec35..efb6144af9a8 100644 --- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java +++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java @@ -30,13 +30,13 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; +import com.android.server.utils.Watchable; import java.util.Objects; @DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true) @DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock", - "mSharedLibraryOverlayPaths", "setOverlayPaths", "mCachedOverlayPathsLock", - "mCachedOverlayPaths", "setCachedOverlayPaths"}) + "mSharedLibraryOverlayPaths", "setOverlayPaths", "setCachedOverlayPaths"}) public class PackageUserStateImpl implements PackageUserStateInternal { @Nullable @@ -72,31 +72,37 @@ public class PackageUserStateImpl implements PackageUserStateInternal { @Nullable private String mSplashScreenTheme; - /** Suspending package to suspend params */ + /** + * Suspending package to suspend params + */ @Nullable private ArrayMap<String, SuspendParams> mSuspendParams; @Nullable - private OverlayPaths mCachedOverlayPaths; - - @Nullable private ArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap; private long mFirstInstallTime; + @Nullable + private final Watchable mWatchable; + public PackageUserStateImpl() { super(); + mWatchable = null; } - public PackageUserStateImpl(PackageUserStateImpl other) { + public PackageUserStateImpl(@NonNull Watchable watchable) { + mWatchable = watchable; + } + + public PackageUserStateImpl(@NonNull Watchable watchable, PackageUserStateImpl other) { + mWatchable = watchable; mDisabledComponents = ArrayUtils.cloneOrNull(other.mDisabledComponents); mEnabledComponents = ArrayUtils.cloneOrNull(other.mEnabledComponents); mOverlayPaths = other.mOverlayPaths; if (other.mSharedLibraryOverlayPaths != null) { mSharedLibraryOverlayPaths = new ArrayMap<>(other.mSharedLibraryOverlayPaths); } - mDisabledComponents = other.mDisabledComponents; - mEnabledComponents = other.mEnabledComponents; mCeDataInode = other.mCeDataInode; mInstalled = other.mInstalled; mStopped = other.mStopped; @@ -110,16 +116,22 @@ public class PackageUserStateImpl implements PackageUserStateInternal { mUninstallReason = other.mUninstallReason; mHarmfulAppWarning = other.mHarmfulAppWarning; mLastDisableAppCaller = other.mLastDisableAppCaller; - mOverlayPaths = other.mOverlayPaths; - mSharedLibraryOverlayPaths = other.mSharedLibraryOverlayPaths; mSplashScreenTheme = other.mSplashScreenTheme; mSuspendParams = other.mSuspendParams == null ? null : new ArrayMap<>(other.mSuspendParams); mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null ? null : new ArrayMap<>(other.mComponentLabelIconOverrideMap); + mFirstInstallTime = other.mFirstInstallTime; + } + + private void onChanged() { + if (mWatchable != null) { + mWatchable.dispatchChange(mWatchable); + } } /** * Sets the path of overlays currently enabled for this package and user combination. + * * @return true if the path contents differ than what they were previously */ @Nullable @@ -132,7 +144,7 @@ public class PackageUserStateImpl implements PackageUserStateInternal { return false; } mOverlayPaths = paths; - mCachedOverlayPaths = null; + onChanged(); return true; } @@ -150,11 +162,13 @@ public class PackageUserStateImpl implements PackageUserStateInternal { if (Objects.equals(paths, currentPaths)) { return false; } - mCachedOverlayPaths = null; if (paths == null || paths.isEmpty()) { - return mSharedLibraryOverlayPaths.remove(library) != null; + boolean returnValue = mSharedLibraryOverlayPaths.remove(library) != null; + onChanged(); + return returnValue; } else { mSharedLibraryOverlayPaths.put(library, paths); + onChanged(); return true; } } @@ -233,16 +247,18 @@ public class PackageUserStateImpl implements PackageUserStateInternal { mComponentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon)); } + onChanged(); } return changed; } /** - * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName, - * String, Integer)}. - * - * This is done when the package is updated as the components and resource IDs may have changed. + * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName, String, + * Integer)}. + * <p> + * This is done when the package is updated as the components and resource IDs may have + * changed. */ public void resetOverrideComponentLabelIcon() { mComponentLabelIconOverrideMap = null; @@ -263,21 +279,157 @@ public class PackageUserStateImpl implements PackageUserStateInternal { } public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage, - @NonNull SuspendParams suspendParams) { + @Nullable SuspendParams suspendParams) { if (mSuspendParams == null) { mSuspendParams = new ArrayMap<>(); } - mSuspendParams.put(suspendingPackage, suspendParams); + if (!mSuspendParams.containsKey(suspendingPackage) + || !Objects.equals(mSuspendParams.get(suspendingPackage), suspendParams)) { + mSuspendParams.put(suspendingPackage, suspendParams); + onChanged(); + } + return this; } public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) { if (mSuspendParams != null) { mSuspendParams.remove(suspendingPackage); + onChanged(); } return this; } + public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) { + mDisabledComponents = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) { + mEnabledComponents = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setCeDataInode(long value) { + mCeDataInode = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setInstalled(boolean value) { + mInstalled = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setStopped(boolean value) { + mStopped = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setNotLaunched(boolean value) { + mNotLaunched = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setHidden(boolean value) { + mHidden = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setDistractionFlags(int value) { + mDistractionFlags = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setInstantApp(boolean value) { + mInstantApp = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setVirtualPreload(boolean value) { + mVirtualPreload = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setEnabledState(int value) { + mEnabledState = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) { + mInstallReason = value; + com.android.internal.util.AnnotationValidations.validate( + PackageManager.InstallReason.class, null, mInstallReason); + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setUninstallReason( + @PackageManager.UninstallReason int value) { + mUninstallReason = value; + com.android.internal.util.AnnotationValidations.validate( + PackageManager.UninstallReason.class, null, mUninstallReason); + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) { + mHarmfulAppWarning = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) { + mLastDisableAppCaller = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths( + @NonNull ArrayMap<String, OverlayPaths> value) { + mSharedLibraryOverlayPaths = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) { + mSplashScreenTheme = value; + onChanged(); + return this; + } + + /** + * Suspending package to suspend params + */ + public @NonNull PackageUserStateImpl setSuspendParams( + @NonNull ArrayMap<String, SuspendParams> value) { + mSuspendParams = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setComponentLabelIconOverrideMap( + @NonNull ArrayMap<ComponentName, Pair<String, Integer>> value) { + mComponentLabelIconOverrideMap = value; + onChanged(); + return this; + } + + public @NonNull PackageUserStateImpl setFirstInstallTime(long value) { + mFirstInstallTime = value; + onChanged(); + return this; + } @@ -393,11 +545,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal { } @DataClass.Generated.Member - public @Nullable OverlayPaths getCachedOverlayPaths() { - return mCachedOverlayPaths; - } - - @DataClass.Generated.Member public @Nullable ArrayMap<ComponentName,Pair<String,Integer>> getComponentLabelIconOverrideMap() { return mComponentLabelIconOverrideMap; } @@ -408,130 +555,8 @@ public class PackageUserStateImpl implements PackageUserStateInternal { } @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) { - mDisabledComponents = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) { - mEnabledComponents = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setCeDataInode( long value) { - mCeDataInode = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setInstalled( boolean value) { - mInstalled = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setStopped( boolean value) { - mStopped = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setNotLaunched( boolean value) { - mNotLaunched = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setHidden( boolean value) { - mHidden = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setDistractionFlags( int value) { - mDistractionFlags = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setInstantApp( boolean value) { - mInstantApp = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setVirtualPreload( boolean value) { - mVirtualPreload = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setEnabledState( int value) { - mEnabledState = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) { - mInstallReason = value; - com.android.internal.util.AnnotationValidations.validate( - PackageManager.InstallReason.class, null, mInstallReason); - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setUninstallReason(@PackageManager.UninstallReason int value) { - mUninstallReason = value; - com.android.internal.util.AnnotationValidations.validate( - PackageManager.UninstallReason.class, null, mUninstallReason); - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) { - mHarmfulAppWarning = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) { - mLastDisableAppCaller = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths(@NonNull ArrayMap<String,OverlayPaths> value) { - mSharedLibraryOverlayPaths = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) { - mSplashScreenTheme = value; - return this; - } - - /** - * Suspending package to suspend params - */ - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setSuspendParams(@NonNull ArrayMap<String,SuspendParams> value) { - mSuspendParams = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setComponentLabelIconOverrideMap(@NonNull ArrayMap<ComponentName,Pair<String,Integer>> value) { - mComponentLabelIconOverrideMap = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull PackageUserStateImpl setFirstInstallTime( long value) { - mFirstInstallTime = value; - return this; + public @Nullable Watchable getWatchable() { + return mWatchable; } @Override @@ -566,9 +591,9 @@ public class PackageUserStateImpl implements PackageUserStateInternal { && Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths) && Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme) && Objects.equals(mSuspendParams, that.mSuspendParams) - && Objects.equals(mCachedOverlayPaths, that.mCachedOverlayPaths) && Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap) - && mFirstInstallTime == that.mFirstInstallTime; + && mFirstInstallTime == that.mFirstInstallTime + && Objects.equals(mWatchable, that.mWatchable); } @Override @@ -597,17 +622,17 @@ public class PackageUserStateImpl implements PackageUserStateInternal { _hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths); _hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme); _hash = 31 * _hash + Objects.hashCode(mSuspendParams); - _hash = 31 * _hash + Objects.hashCode(mCachedOverlayPaths); _hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap); _hash = 31 * _hash + Long.hashCode(mFirstInstallTime); + _hash = 31 * _hash + Objects.hashCode(mWatchable); return _hash; } @DataClass.Generated( - time = 1640923839971L, + time = 1643854846064L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java", - inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") + inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate void onChanged()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)") @Deprecated private void __metadata() {} 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/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java index f519ceda1e50..243efb5e58ce 100644 --- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java +++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java @@ -16,6 +16,8 @@ package com.android.server.security; +import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE; import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN; import android.content.Context; @@ -76,10 +78,24 @@ public class AttestationVerificationManagerService extends SystemService { private void verifyAttestationForAllVerifiers( AttestationProfile profile, int localBindingType, Bundle requirements, byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) { - // TODO(b/201696614): Implement IVerificationResult result = new IVerificationResult(); - result.resultCode = RESULT_UNKNOWN; + // TODO(b/201696614): Implement result.token = null; + switch (profile.getAttestationProfileId()) { + case PROFILE_SELF_TRUSTED: + Slog.d(TAG, "Verifying Self trusted profile."); + try { + result.resultCode = + AttestationVerificationSelfTrustedVerifierForTesting.getInstance() + .verifyAttestation(localBindingType, requirements, attestation); + } catch (Throwable t) { + result.resultCode = RESULT_FAILURE; + } + break; + default: + Slog.d(TAG, "No profile found, defaulting."); + result.resultCode = RESULT_UNKNOWN; + } resultCallback.complete(result); } diff --git a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java new file mode 100644 index 000000000000..58df2bd982dc --- /dev/null +++ b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security; + +import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE; +import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS; +import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; + +import android.annotation.NonNull; +import android.os.Build; +import android.os.Bundle; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.internal.org.bouncycastle.asn1.ASN1OctetString; +import com.android.internal.org.bouncycastle.asn1.ASN1Sequence; +import com.android.internal.org.bouncycastle.asn1.x509.Certificate; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Verifies {@code PROFILE_SELF_TRUSTED} attestations. + * + * Verifies that the attesting environment can create an attestation with the same root certificate + * as the verifying device with a matching attestation challenge. Skips CRL revocations checking + * so this verifier can work in a hermetic test environment. + * + * This verifier profile is intended to be used only for testing. + */ +class AttestationVerificationSelfTrustedVerifierForTesting { + private static final String TAG = "AVF"; + private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE); + + // The OID for the extension Android Keymint puts into device-generated certificates. + private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID = + "1.3.6.1.4.1.11129.2.1.17"; + + // ASN.1 sequence index values for the Android Keymint extension. + private static final int ATTESTATION_CHALLENGE_INDEX = 4; + + private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; + private static final String GOLDEN_ALIAS = + AttestationVerificationSelfTrustedVerifierForTesting.class.getCanonicalName() + + ".Golden"; + + private static volatile AttestationVerificationSelfTrustedVerifierForTesting + sAttestationVerificationSelfTrustedVerifier = null; + + private final CertificateFactory mCertificateFactory; + private final CertPathValidator mCertPathValidator; + private final KeyStore mAndroidKeyStore; + private X509Certificate mGoldenRootCert; + + static AttestationVerificationSelfTrustedVerifierForTesting getInstance() + throws Exception { + if (sAttestationVerificationSelfTrustedVerifier == null) { + synchronized (AttestationVerificationSelfTrustedVerifierForTesting.class) { + if (sAttestationVerificationSelfTrustedVerifier == null) { + sAttestationVerificationSelfTrustedVerifier = + new AttestationVerificationSelfTrustedVerifierForTesting(); + } + } + } + return sAttestationVerificationSelfTrustedVerifier; + } + + private static void debugVerboseLog(String str, Throwable t) { + if (DEBUG) { + Slog.v(TAG, str, t); + } + } + + private static void debugVerboseLog(String str) { + if (DEBUG) { + Slog.v(TAG, str); + } + } + + private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception { + mCertificateFactory = CertificateFactory.getInstance("X.509"); + mCertPathValidator = CertPathValidator.getInstance("PKIX"); + mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE); + mAndroidKeyStore.load(null); + if (!mAndroidKeyStore.containsAlias(GOLDEN_ALIAS)) { + KeyPairGenerator kpg = + KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE); + KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder( + GOLDEN_ALIAS, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) + .setAttestationChallenge(GOLDEN_ALIAS.getBytes()) + .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).build(); + kpg.initialize(parameterSpec); + kpg.generateKeyPair(); + } + + X509Certificate[] goldenCerts = (X509Certificate[]) + ((KeyStore.PrivateKeyEntry) mAndroidKeyStore.getEntry(GOLDEN_ALIAS, null)) + .getCertificateChain(); + mGoldenRootCert = goldenCerts[goldenCerts.length - 1]; + } + + int verifyAttestation( + int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) { + List<X509Certificate> certificates = new ArrayList<>(); + ByteArrayInputStream bis = new ByteArrayInputStream(attestation); + try { + while (bis.available() > 0) { + certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis)); + } + } catch (CertificateException e) { + debugVerboseLog("Unable to parse certificates from attestation", e); + return RESULT_FAILURE; + } + + if (localBindingType == TYPE_CHALLENGE + && validateRequirements(requirements) + && checkLeafChallenge(requirements, certificates) + && verifyCertificateChain(certificates)) { + return RESULT_SUCCESS; + } + + return RESULT_FAILURE; + } + + private boolean verifyCertificateChain(List<X509Certificate> certificates) { + if (certificates.size() < 2) { + debugVerboseLog("Certificate chain less than 2 in size."); + return false; + } + + try { + CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); + PKIXParameters validationParams = new PKIXParameters(getTrustAnchors()); + // Skipping revocation checking because we want this to work in a hermetic test + // environment. + validationParams.setRevocationEnabled(false); + mCertPathValidator.validate(certificatePath, validationParams); + } catch (Throwable t) { + debugVerboseLog("Invalid certificate chain", t); + return false; + } + + return true; + } + + private Set<TrustAnchor> getTrustAnchors() { + return Collections.singleton(new TrustAnchor(mGoldenRootCert, null)); + } + + private boolean validateRequirements(Bundle requirements) { + if (requirements.size() != 1) { + debugVerboseLog("Requirements does not contain exactly 1 key."); + return false; + } + if (!requirements.containsKey(PARAM_CHALLENGE)) { + debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE); + return false; + } + return true; + } + + private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) { + // Verify challenge + byte[] challenge; + try { + challenge = getChallengeFromCert(certificates.get(0)); + } catch (Throwable t) { + debugVerboseLog("Unable to parse challenge from certificate.", t); + return false; + } + + if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) { + return true; + } else { + debugVerboseLog("Self-Trusted validation failed; challenge mismatch."); + return false; + } + } + + private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate) + throws CertificateEncodingException, IOException { + Certificate certificate = Certificate.getInstance( + new ASN1InputStream(x509Certificate.getEncoded()).readObject()); + ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions() + .getExtensionParsedValue( + new ASN1ObjectIdentifier(ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID)); + return ((ASN1OctetString) keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX)) + .getOctets(); + } +} diff --git a/services/core/java/com/android/server/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/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 8a87c96fcaaa..94f483c65eb5 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -53,6 +53,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.fingerprint.IUdfpsHbmListener; +import android.media.MediaRoute2Info; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -92,6 +93,7 @@ import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.internal.statusbar.StatusBarIcon; @@ -1265,6 +1267,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D "StatusBarManagerService"); } + private void enforceMediaContentControl() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, + "StatusBarManagerService"); + } + /** * For targetSdk S+ we require STATUS_BAR. For targetSdk < S, we only require EXPAND_STATUS_BAR * but also require that it falls into one of the allowed use-cases to lock down abuse vector. @@ -1987,6 +1995,53 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return false; } + /** + * Notifies the system of a new media tap-to-transfer state for the *sender* device. See + * {@link StatusBarManager.updateMediaTapToTransferSenderDisplay} for more information. + * + * @param undoCallback a callback that will be triggered if the user elects to undo a media + * transfer. + * + * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL} + * permission. + */ + @Override + public void updateMediaTapToTransferSenderDisplay( + @StatusBarManager.MediaTransferSenderState int displayState, + @NonNull MediaRoute2Info routeInfo, + @Nullable IUndoMediaTransferCallback undoCallback + ) { + enforceMediaContentControl(); + if (mBar != null) { + try { + mBar.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, undoCallback); + } catch (RemoteException e) { + Slog.e(TAG, "updateMediaTapToTransferSenderDisplay", e); + } + } + } + + /** + * Notifies the system of a new media tap-to-transfer state for the *receiver* device. See + * {@link StatusBarManager.updateMediaTapToTransferReceiverDisplay} for more information. + * + * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL} + * permission. + */ + @Override + public void updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState int displayState, + MediaRoute2Info routeInfo) { + enforceMediaContentControl(); + if (mBar != null) { + try { + mBar.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo); + } catch (RemoteException e) { + Slog.e(TAG, "updateMediaTapToTransferReceiverDisplay", e); + } + } + } + /** @hide */ public void passThroughShellCommand(String[] args, FileDescriptor fd) { enforceStatusBarOrShell(); 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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5f04b7e2621a..6836e315437c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7655,13 +7655,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true. * <li>The activity is eligible for fixed orientation letterbox. * <li>The activity is in fullscreen. + * <li>The activity is portrait-only. * </ul> */ // TODO(b/215316431): Add tests boolean isEligibleForLetterboxEducation() { return mWmService.mLetterboxConfiguration.getIsEducationEnabled() && mIsEligibleForFixedOrientationLetterbox - && getWindowingMode() == WINDOWING_MODE_FULLSCREEN; + && getWindowingMode() == WINDOWING_MODE_FULLSCREEN + && getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT; } /** diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8d4ae908e79f..ca4d7178fbcc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -24,6 +24,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; +import static android.Manifest.permission.MANAGE_GAME_ACTIVITY; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; @@ -1771,6 +1772,43 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public int startActivityFromGameSession(IApplicationThread caller, String callingPackage, + String callingFeatureId, int callingPid, int callingUid, Intent intent, int taskId, + int userId) { + if (checkCallingPermission(MANAGE_GAME_ACTIVITY) != PERMISSION_GRANTED) { + final String msg = "Permission Denial: startActivityFromGameSession() from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + + " requires " + MANAGE_GAME_ACTIVITY; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + assertPackageMatchesCallingUid(callingPackage); + + final ActivityOptions activityOptions = ActivityOptions.makeBasic(); + activityOptions.setLaunchTaskId(taskId); + + userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityFromGameSession"); + + final long origId = Binder.clearCallingIdentity(); + try { + return getActivityStartController() + .obtainStarter(intent, "startActivityFromGameSession") + .setCaller(caller) + .setCallingUid(callingUid) + .setCallingPid(callingPid) + .setCallingPackage(intent.getPackage()) + .setCallingFeatureId(callingFeatureId) + .setUserId(userId) + .setActivityOptions(activityOptions.toBundle()) + .setRealCallingUid(Binder.getCallingUid()) + .execute(); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public BackNavigationInfo startBackNavigation() { mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "startBackNavigation()"); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index a8779fa79675..45a6cb9d3920 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -29,6 +29,7 @@ import android.os.SystemProperties; import android.util.Slog; import android.view.SurfaceControl; import android.window.BackNavigationInfo; +import android.window.IOnBackInvokedCallback; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; @@ -91,29 +92,41 @@ class BackNavigationController { HardwareBuffer screenshotBuffer = null; int prevTaskId; int prevUserId; + IOnBackInvokedCallback callback; synchronized (task.mWmService.mGlobalLock) { activityRecord = task.topRunningActivity(); removedWindowContainer = activityRecord; taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s", - task, activityRecord); - - // IME is visible, back gesture will dismiss it, nothing to preview. - if (task.getDisplayContent().getImeContainer().isVisible()) { - return null; - } - - // Current Activity is home, there is no previous activity to display - if (activityRecord.isActivityTypeHome()) { - return null; + WindowState topChild = activityRecord.getTopChild(); + callback = topChild.getOnBackInvokedCallback(); + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " + + "topRunningActivity=%s, topWindow=%s backCallback=%s", + task, activityRecord, topChild, + callback != null ? callback.getClass().getSimpleName() : null); + + // For IME and Home, either a callback is registered, or we do nothing. In both cases, + // we don't need to pass the leashes below. + if (task.getDisplayContent().getImeContainer().isVisible() + || activityRecord.isActivityTypeHome()) { + if (callback != null) { + return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, + null /* topWindowLeash */, null /* screenshotSurface */, + null /* screenshotBuffer */, null /* taskWindowConfiguration */, + null /* onBackNavigationDone */, callback /* onBackInvokedCallback */); + } else { + return null; + } } prev = task.getActivity( (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity()); - if (prev != null) { + if (callback != null) { + backType = BackNavigationInfo.TYPE_CALLBACK; + } else if (prev != null) { backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else if (task.returnsToHomeRootTask()) { prevTask = null; @@ -148,7 +161,7 @@ class BackNavigationController { // Prepare a leash to animate the current top window animLeash = removedWindowContainer.makeAnimationLeash() - .setName("BackPreview Leash") + .setName("BackPreview Leash for " + removedWindowContainer) .setHidden(false) .setBLASTLayer() .build(); @@ -158,7 +171,7 @@ class BackNavigationController { } SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot") + .setName("BackPreview Screenshot for " + prev) .setParent(animationLeashParent) .setHidden(false) .setBLASTLayer(); @@ -171,6 +184,10 @@ class BackNavigationController { screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId); } } + + // The Animation leash needs to be above the screenshot surface, but the animation leash + // needs to be added before to be in the synchronized block. + tx.setLayer(animLeash, 1); tx.apply(); WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; @@ -183,13 +200,16 @@ class BackNavigationController { return null; } + RemoteCallback onBackNavigationDone = new RemoteCallback( + result -> resetSurfaces(finalRemovedWindowContainer + )); return new BackNavigationInfo(backType, animLeash, screenshotSurface, screenshotBuffer, taskWindowConfiguration, - new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer - ))); + onBackNavigationDone, + callback); } diff --git a/services/core/java/com/android/server/wm/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 1167cb531c76..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() { @@ -3855,14 +3867,20 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned - * bitmap will be full size and will not include any secure content. + * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. * - * @param taskId The task ID of the task for which a snapshot is requested. + * @param taskId The task ID of the task for which a Bitmap is requested. + * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with + * arguments for how to capture the Bitmap. The caller can + * specify any arguments, but this method will ensure that the + * specified task's SurfaceControl is used and the crop is set to + * the bounds of that task. * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could * not be generated. */ - @Nullable public Bitmap captureTaskBitmap(int taskId) { + @Nullable + public Bitmap captureTaskBitmap(int taskId, + @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) { if (mTaskSnapshotController.shouldDisableSnapshots()) { return null; } @@ -3876,9 +3894,7 @@ public class WindowManagerService extends IWindowManager.Stub task.getBounds(mTmpRect); final SurfaceControl sc = task.getSurfaceControl(); final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers( - new SurfaceControl.LayerCaptureArgs.Builder(sc) - .setSourceCrop(mTmpRect) - .build()); + layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build()); if (buffer == null) { Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId); return null; @@ -5877,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)); @@ -5947,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. @@ -7983,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 6f41d42b3d54..921e2e181d91 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; @@ -214,6 +215,7 @@ import android.app.admin.SystemUpdatePolicy; import android.app.admin.UnsafeStateException; import android.app.backup.IBackupManager; import android.app.compat.CompatChanges; +import android.app.role.RoleManager; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; @@ -222,6 +224,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; @@ -6847,7 +6850,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String.format(NOT_SYSTEM_CALLER_MSG, "call isAlwaysOnVpnLockdownEnabledForUser")); synchronized (getLockObject()) { ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle); - return admin != null ? admin.mAlwaysOnVpnLockdown : null; + return admin != null && admin.mAlwaysOnVpnLockdown; } } @@ -10855,6 +10858,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userHandle = user.getIdentifier(); final long id = mInjector.binderClearCallingIdentity(); try { + maybeInstallDeviceManagerRoleHolderInUser(userHandle); + manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, /* showDisclaimer= */ true); @@ -10951,7 +10956,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mPendingUserCreatedCallbackTokens.contains(token)) { // Ignore because it was triggered by createAndManageUser() Slogf.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId - + " due to token" + token); + + " due to token " + token); mPendingUserCreatedCallbackTokens.remove(token); return; } @@ -11083,6 +11088,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { switched = mInjector.getIActivityManager().switchUser(userId); if (!switched) { Slogf.w(LOG_TAG, "Failed to switch to user %d", userId); + } else { + Slogf.d(LOG_TAG, "Switched"); } return switched; } catch (RemoteException e) { @@ -11106,18 +11113,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private @UserIdInt int getLogoutUserIdUnchecked() { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) { - // mLogoutUserId is USER_SYSTEM as well, but there's no need to acquire the lock - return UserHandle.USER_SYSTEM; - } synchronized (getLockObject()) { return mLogoutUserId; } } private void clearLogoutUser() { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore - synchronized (getLockObject()) { setLogoutUserIdLocked(UserHandle.USER_NULL); } @@ -11125,8 +11126,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @GuardedBy("getLockObject()") private void setLogoutUserIdLocked(@UserIdInt int userId) { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore - if (userId == UserHandle.USER_CURRENT) { userId = getCurrentForegroundUserId(); } @@ -11209,8 +11208,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } - // TODO(b/204585343): remove the headless system user check? - if (mInjector.userManagerIsHeadlessSystemUserMode() && callingUserId != mInjector + if (callingUserId != mInjector .binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId())) { Slogf.d(LOG_TAG, "logoutUser(): user %d is in background, just stopping, not switching", callingUserId); @@ -11226,8 +11224,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(canManageUsers(caller) || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS)); - int result = logoutUserUnchecked(getCurrentForegroundUserId()); - Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result); + int currentUserId = getCurrentForegroundUserId(); + if (VERBOSE_LOG) { + Slogf.v(LOG_TAG, "logout() called by uid %d; current user is %d", caller.getUid(), + currentUserId); + } + int result = logoutUserUnchecked(currentUserId); + if (VERBOSE_LOG) { + Slogf.v(LOG_TAG, "Result of logout(): %d", result); + } return result; } @@ -11613,6 +11618,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller) || isProfileOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); @@ -13988,7 +13994,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (isFinancedDeviceOwner(caller)) { - enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission); + enforcePermissionGrantStateOnFinancedDevice(packageName, permission); } long ident = mInjector.binderClearCallingIdentity(); try { @@ -14049,14 +14055,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 +14071,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()) @@ -17689,6 +17699,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final long startTime = SystemClock.elapsedRealtime(); + + onCreateAndProvisionManagedProfileStarted(provisioningParams); + final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled() ? Collections.emptySet() : mOverlayPackagesProvider.getNonRequiredApps( @@ -17700,6 +17713,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]"); } } + userInfo = mUserManager.createProfileForUserEvenWhenDisallowed( provisioningParams.getProfileName(), UserManager.USER_TYPE_PROFILE_MANAGED, @@ -17718,7 +17732,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { startTime, callerPackage); - onCreateAndProvisionManagedProfileStarted(provisioningParams); + maybeInstallDeviceManagerRoleHolderInUser(userInfo.id); installExistingAdminPackage(userInfo.id, admin.getPackageName()); if (!enableAdminAndSetProfileOwner( @@ -17786,6 +17800,43 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void onCreateAndProvisionManagedProfileCompleted( ManagedProfileProvisioningParams provisioningParams) {} + private void maybeInstallDeviceManagerRoleHolderInUser(int targetUserId) { + String deviceManagerRoleHolderPackageName = getDeviceManagerRoleHolderPackageName(mContext); + if (deviceManagerRoleHolderPackageName == null) { + Slogf.d(LOG_TAG, "No device manager role holder specified."); + return; + } + try { + if (mIPackageManager.isPackageAvailable( + deviceManagerRoleHolderPackageName, targetUserId)) { + Slogf.d(LOG_TAG, "The device manager role holder " + + deviceManagerRoleHolderPackageName + " is already installed in " + + "user " + targetUserId); + return; + } + Slogf.d(LOG_TAG, "Installing the device manager role holder " + + deviceManagerRoleHolderPackageName + " in user " + targetUserId); + mIPackageManager.installExistingPackageAsUser( + deviceManagerRoleHolderPackageName, + targetUserId, + PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, + PackageManager.INSTALL_REASON_POLICY, + /* whiteListedPermissions= */ null); + } catch (RemoteException e) { + // Does not happen, same process + } + } + + private String getDeviceManagerRoleHolderPackageName(Context context) { + RoleManager roleManager = context.getSystemService(RoleManager.class); + List<String> roleHolders = + roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER); + if (roleHolders.isEmpty()) { + return null; + } + return roleHolders.get(0); + } + private void resetInteractAcrossProfilesAppOps() { mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps(); pregrantDefaultInteractAcrossProfilesAppOps(); @@ -18669,4 +18720,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mContext.sendBroadcastAsUser(intent, user); } } + + public boolean isDpcDownloaded() { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + ContentResolver cr = mContext.getContentResolver(); + + return mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.getIntForUser( + cr, MANAGED_PROVISIONING_DPC_DOWNLOADED, + /* def= */ 0, /* userHandle= */ cr.getUserId()) + == 1); + } + + public void setDpcDownloaded(boolean downloaded) { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + int setTo = downloaded ? 1 : 0; + + mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.putInt( + mContext.getContentResolver(), MANAGED_PROVISIONING_DPC_DOWNLOADED, setTo)); + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f4fae2f81718..a7539b507e62 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; @@ -85,6 +84,7 @@ import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; +import android.util.Dumpable; import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Pair; @@ -143,7 +143,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; @@ -397,6 +396,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 = @@ -663,7 +664,12 @@ public final class SystemServer implements Dumpable { } @Override - public void dump(IndentingPrintWriter pw, String[] args) { + public String getDumpableName() { + return SystemServer.class.getSimpleName(); + } + + @Override + public void dump(PrintWriter pw, String[] args) { pw.printf("Runtime restart: %b\n", mRuntimeRestart); pw.printf("Start count: %d\n", mStartCount); pw.print("Runtime start-up time: "); @@ -1393,10 +1399,8 @@ public final class SystemServer implements Dumpable { DynamicSystemService dynamicSystem = null; IStorageManager storageManager = null; NetworkManagementService networkManagement = null; - IpSecService ipSecService = null; VpnManagerService vpnManager = null; VcnManagementService vcnManagement = null; - NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; WindowManagerService wm = null; SerialService serial = null; @@ -1506,6 +1510,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. @@ -1898,15 +1906,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartIpSecService"); - try { - ipSecService = IpSecService.create(context); - ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); - } catch (Throwable e) { - reportWtf("starting IpSec Service", e); - } - t.traceEnd(); - t.traceBegin("StartFontManagerService"); mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode)); t.traceEnd(); @@ -1927,13 +1926,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"); @@ -2785,7 +2781,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; @@ -2793,7 +2788,6 @@ public final class SystemServer implements Dumpable { final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; - final IpSecService ipSecServiceF = ipSecService; final VpnManagerService vpnManagerF = vpnManager; final VcnManagementService vcnManagementF = vcnManagement; final WindowManagerService windowManagerF = wm; @@ -2885,24 +2879,6 @@ public final class SystemServer implements Dumpable { .networkScoreAndNetworkManagementServiceReady(); } t.traceEnd(); - t.traceBegin("MakeIpSecServiceReady"); - try { - if (ipSecServiceF != null) { - ipSecServiceF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making IpSec Service ready", e); - } - t.traceEnd(); - t.traceBegin("MakeNetworkStatsServiceReady"); - try { - if (networkStatsF != null) { - networkStatsF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making Network Stats Service ready", e); - } - t.traceEnd(); t.traceBegin("MakeConnectivityServiceReady"); try { if (connectivityF != null) { @@ -3191,11 +3167,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/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 9e83f8e7bda8..ca9ff6f15f3f 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -2727,7 +2727,7 @@ public class KeyValueBackupTaskTest { // The second line will throw NPE because it will call lambda 1 with null, since argThat() // returns null. So we guard against that by checking for null. return packageInfo -> - packageInfo != null && packageInfo.packageName.equals(packageInfo.packageName); + packageInfo != null && packageInfo.packageName.equals(packageData.packageName); } /** Matches {@link ApplicationInfo} whose package name is {@code packageData.packageName}. */ diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 28c91aaefd8b..76709536b40b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -101,6 +101,7 @@ import android.os.SystemClock; import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.telephony.TelephonyManager; import android.util.Log; import android.util.Pair; @@ -211,6 +212,7 @@ public final class BackgroundRestrictionTest { @Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal; @Mock private MediaSessionManager mMediaSessionManager; @Mock private RoleManager mRoleManager; + @Mock private TelephonyManager mTelephonyManager; private long mCurrentTimeMillis; @@ -2309,6 +2311,11 @@ public final class BackgroundRestrictionTest { } @Override + TelephonyManager getTelephonyManager() { + return mTelephonyManager; + } + + @Override AppFGSTracker getAppFGSTracker() { return mAppFGSTracker; } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 08de62bc6537..ed232e5458b0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -60,6 +60,7 @@ import android.service.games.IGameServiceController; import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; +import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import androidx.test.filters.SmallTest; @@ -405,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 @@ -555,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 @@ -746,6 +748,11 @@ public final class GameServiceProviderInstanceImplTest { mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); + IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); @@ -754,18 +761,28 @@ public final class GameServiceProviderInstanceImplTest { GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, result.getStatus()); - verify(mMockWindowManagerService).captureTaskBitmap(10); + + verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any()); } @Test public void takeScreenshot_success() throws Exception { - when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP); + SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class); + SurfaceControl[] excludeLayers = new SurfaceControl[1]; + excludeLayers[0] = mockOverlaySurfaceControl; + when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); mFakeGameService.requestCreateGameSession(10); + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); + when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); + IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index bdeb2b4fd839..f9bdad6c62ba 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -204,6 +204,49 @@ public class JobSchedulerServiceTest { jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag); } + @Test + public void testGetMinJobExecutionGuaranteeMs() { + JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(1).setExpedited(true)); + JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(3).setExpedited(true)); + JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH)); + JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs", + createJobInfo(6)); + + spyOn(ejMax); + spyOn(ejHigh); + spyOn(ejMaxDowngraded); + spyOn(ejHighDowngraded); + spyOn(jobHigh); + spyOn(jobDef); + + when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true); + when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true); + when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); + when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false); + when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false); + when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false); + + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejMax)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejHigh)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded)); + assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobHigh)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobDef)); + } + /** * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job * with the correct delay and deadline constraints if the periodic job is scheduled with the diff --git a/services/tests/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/am/ActivityManagerUtilsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java index 96103e36ae92..d95c9ac1bd6c 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java @@ -17,7 +17,7 @@ package com.android.server.am; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; @@ -89,18 +89,20 @@ public class ActivityManagerUtilsTest { } @Test - public void testSheckShouldSamplePackage() { + public void testCheckShouldSamplePackage() { // Just make sure checkShouldSamplePackage is actually working... + assertFailure(() -> checkShouldSamplePackage(0.3f, 0.6f, false, true)); + assertFailure(() -> checkShouldSamplePackage(0.6f, 0.3f, true, false)); + } + + private static void assertFailure(Runnable r) { + boolean failed = false; try { - checkShouldSamplePackage(0.3f, 0.6f, false, true); - fail(); - } catch (AssertionError expected) { - } - try { - checkShouldSamplePackage(0.6f, 0.3f, true, false); - fail(); - } catch (AssertionError expected) { + r.run(); + } catch (AssertionError e) { + failed = true; } + assertTrue(failed); } private void checkShouldSamplePackage(float inputSampleRate, float expectedRate, 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 9e1445cf589d..dad9fe8648b2 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -28,9 +28,10 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; +import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; -import android.media.BtProfileConnectionInfo; +import android.media.BluetoothProfileConnectionInfo; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -100,7 +101,7 @@ public class AudioDeviceBrokerTest { mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( any(AudioDeviceBroker.BtDeviceInfo.class) @@ -186,8 +187,9 @@ public class AudioDeviceBrokerTest { doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup( any(Intent.class)); - mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioService.CONNECTION_STATE_CONNECTED, address, name, caller); + mSpyDevInventory.setWiredDeviceConnectionState(new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_WIRED_HEADSET, address, name), + AudioService.CONNECTION_STATE_CONNECTED, caller); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // Verify that the sticky intent is broadcasted @@ -208,13 +210,13 @@ public class AudioDeviceBrokerTest { // first connection: ensure the device is connected as a starting condition for the test mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource")); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice, - BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(false, -1), "testSource")); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } @@ -222,7 +224,7 @@ public class AudioDeviceBrokerTest { // reconnection mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, - BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource")); + BluetoothProfileConnectionInfo.createA2dpInfo(true, 2), "testSource")); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts, @@ -246,11 +248,11 @@ public class AudioDeviceBrokerTest { */ private void checkSingleSystemConnection(BluetoothDevice btDevice) throws Exception { final String expectedName = btDevice.getName() == null ? "" : btDevice.getName(); + AudioDeviceAttributes expected = new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress(), expectedName); verify(mSpyAudioSystem, times(1)).setDeviceConnectionState( - ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), + ArgumentMatchers.argThat(x -> x.equalTypeAddress(expected)), ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE), - ArgumentMatchers.eq(btDevice.getAddress()), - ArgumentMatchers.eq(expectedName), anyInt() /*codec*/); } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 8d706cb960e9..1f355b096335 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -48,11 +48,10 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { //----------------------------------------------------------------- // Overrides of AudioSystemAdapter @Override - public int setDeviceConnectionState(int device, int state, String deviceAddress, - String deviceName, int codecFormat) { - Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, %s, %s, 0x%s", - Integer.toHexString(device), state, deviceAddress, deviceName, - Integer.toHexString(codecFormat))); + public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, + int codecFormat) { + Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s", + attributes.toString(), state, Integer.toHexString(codecFormat))); return AudioSystem.AUDIO_STATUS_OK; } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 83fa7ac02503..b4bb04d2b1b4 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerInternal; @@ -30,7 +29,6 @@ import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; import android.os.Binder; import android.os.IBinder; -import android.os.IInputConstants; import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.DisplayInfo; @@ -81,17 +79,15 @@ public class InputControllerTest { } @Test - public void unregisterInputDevice_allMiceUnregistered_unsetValues() { + public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() { final IBinder deviceToken = new Binder(); mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); - verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f)); + doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mInputController.unregisterInputDevice(deviceToken); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId( eq(Display.INVALID_DISPLAY)); - verify(mInputManagerInternalMock).setPointerAcceleration( - eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION)); } @Test @@ -100,14 +96,11 @@ public class InputControllerTest { mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); - verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f)); final IBinder deviceToken2 = new Binder(); mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken2, /* displayId= */ 2); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2)); mInputController.unregisterInputDevice(deviceToken); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); - verify(mInputManagerInternalMock, times(0)).setPointerAcceleration( - eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION)); } } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 33540c874c0a..3b4aece5997e 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -19,6 +19,8 @@ package com.android.server.companion.virtual; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; @@ -117,6 +120,8 @@ public class VirtualDeviceManagerServiceTest { LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); + doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); + doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt()); LocalServices.removeServiceForTest(InputManagerInternal.class); LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); @@ -353,7 +358,7 @@ public class VirtualDeviceManagerServiceTest { mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, /* displayId= */ 1, PHYS)); - mInputController.mActivePointerDisplayId = 1; + doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() .setButtonCode(buttonCode) .setAction(action).build()); @@ -394,7 +399,7 @@ public class VirtualDeviceManagerServiceTest { mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, /* displayId= */ 1, PHYS)); - mInputController.mActivePointerDisplayId = 1; + doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() .setRelativeX(x).setRelativeY(y).build()); verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y); @@ -435,7 +440,7 @@ public class VirtualDeviceManagerServiceTest { mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, /* displayId= */ 1, PHYS)); - mInputController.mActivePointerDisplayId = 1; + doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() .setXAxisMovement(x) .setYAxisMovement(y).build()); @@ -508,4 +513,19 @@ public class VirtualDeviceManagerServiceTest { verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure, majorAxisSize); } + + @Test + public void setShowPointerIcon_setsValueForAllDisplays() { + mDeviceImpl.mVirtualDisplayIds.add(1); + mDeviceImpl.mVirtualDisplayIds.add(2); + mDeviceImpl.mVirtualDisplayIds.add(3); + mDeviceImpl.createVirtualMouse(1, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); + mDeviceImpl.createVirtualMouse(2, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); + mDeviceImpl.createVirtualMouse(3, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); + mDeviceImpl.setShowPointerIcon(false); + verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt()); + verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt()); + mDeviceImpl.setShowPointerIcon(true); + verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(true), anyInt()); + } } 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/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index ac836426b536..ed4dee1792e5 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -43,7 +43,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; -import android.content.pm.parsing.FrameworkParsingPackageUtils; import android.os.BaseBundle; import android.os.PersistableBundle; import android.os.Process; @@ -87,7 +86,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.security.PublicKey; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; @@ -671,6 +669,23 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/, UUID.randomUUID()); + origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false, + false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}), + new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning", + "splashScreenTheme", 1000L); + final PersistableBundle appExtras1 = createPersistableBundle( + PACKAGE_NAME_1, 1L, 0.01, true, "appString1"); + final PersistableBundle launcherExtras1 = createPersistableBundle( + PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1"); + final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder() + .setIcon(0x11220001) + .setTitle("String Title") + .setMessage("1st message") + .setNeutralButtonText(0x11220003) + .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS) + .build(); + origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1", + SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1)); final PackageSetting testPkgSetting01 = new PackageSetting( PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, @@ -691,6 +706,8 @@ public class PackageManagerSettingsTests { UUID.randomUUID()); testPkgSetting01.copyPackageSetting(origPkgSetting01); verifySettingCopy(origPkgSetting01, testPkgSetting01); + verifyUserStatesCopy(origPkgSetting01.readUserState(0), + testPkgSetting01.readUserState(0)); } /** Update package */ @@ -724,8 +741,7 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.getFlags(), is(0)); assertThat(testPkgSetting01.getPrivateFlags(), is(0)); final PackageUserState userState = testPkgSetting01.readUserState(0); - final PackageUserState oldUserState = oldPkgSetting01.readUserState(0); - verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/, + verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, false /*installed*/); } @@ -760,11 +776,7 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM)); assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)); final PackageUserState userState = testPkgSetting01.readUserState(0); - final PackageUserState oldUserState = oldPkgSetting01.readUserState(0); - // WARNING: When creating a shallow copy of the PackageSetting we do NOT create - // new contained objects. For example, this means that changes to the user state - // in testPkgSetting01 will also change the user state in its copy. - verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/, + verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/); } @@ -839,8 +851,7 @@ public class PackageManagerSettingsTests { assertNotSame(testPkgSetting01.getSignatures(), originalSignatures); assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); - verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/, - false /*notLaunched*/, false /*stopped*/, true /*installed*/); + verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/); } /** Create a new non-system PackageSetting */ @@ -880,8 +891,7 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE)); // by default, the package is considered stopped final PackageUserState userState = testPkgSetting01.readUserState(0); - verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/, - true /*notLaunched*/, true /*stopped*/, true /*installed*/); + verifyUserState(userState, true /*notLaunched*/, true /*stopped*/, true /*installed*/); } /** Create PackageSetting for a shared user */ @@ -923,8 +933,7 @@ public class PackageManagerSettingsTests { assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86")); assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); - verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/, - false /*notLaunched*/, false /*stopped*/, true /*installed*/); + verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/); } /** Create a new PackageSetting based on a disabled package setting */ @@ -968,8 +977,7 @@ public class PackageManagerSettingsTests { assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures); assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); - verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/, - false /*notLaunched*/, false /*stopped*/, true /*installed*/); + verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/); } @Test @@ -1080,29 +1088,8 @@ public class PackageManagerSettingsTests { assertThat(countDownLatch.getCount(), is(0L)); } - private <T> void assertArrayEquals(T[] a, T[] b) { - assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b), - Arrays.equals(a, b)); - } - - private void assertArrayEquals(int[] a, int[] b) { - assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b), - Arrays.equals(a, b)); - } - - private void assertArrayEquals(long[] a, long[] b) { - assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b), - Arrays.equals(a, b)); - } - - private void verifyUserState(PackageUserState userState, PackageUserState oldUserState, - boolean userStateChanged) { - verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/, - false /*stopped*/, true /*installed*/); - } - - private void verifyUserState(PackageUserState userState, PackageUserState oldUserState, - boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) { + private void verifyUserState(PackageUserState userState, + boolean notLaunched, boolean stopped, boolean installed) { assertThat(userState.getEnabledState(), is(0)); assertThat(userState.isHidden(), is(false)); assertThat(userState.isInstalled(), is(installed)); @@ -1110,9 +1097,6 @@ public class PackageManagerSettingsTests { assertThat(userState.isStopped(), is(stopped)); assertThat(userState.isSuspended(), is(false)); assertThat(userState.getDistractionFlags(), is(0)); - if (oldUserState != null) { - assertThat(userState.equals(oldUserState), is(not(userStateChanged))); - } } private void verifyKeySetData(PackageKeySetData originalData, PackageKeySetData testData) { @@ -1177,6 +1161,57 @@ public class PackageManagerSettingsTests { assertThat(origPkgSetting.getVolumeUuid(), is(testPkgSetting.getVolumeUuid())); } + private void verifyUserStatesCopy(PackageUserStateInternal origPus, + PackageUserStateInternal testPus) { + assertThat(userStateEquals(origPus, testPus), is(true)); + // Verify suspendParams are copied over + assertThat(origPus.getSuspendParams(), is(notNullValue())); + assertThat(testPus.getSuspendParams(), is(notNullValue())); + SuspendParams origSuspendParams = origPus.getSuspendParams().valueAt(0); + SuspendParams testSuspendParams = testPus.getSuspendParams().valueAt(0); + assertThat(origSuspendParams.getDialogInfo().equals(testSuspendParams.getDialogInfo()), + is(true)); + assertThat(BaseBundle.kindofEquals( + origSuspendParams.getAppExtras(), testSuspendParams.getAppExtras()), is(true)); + assertThat(BaseBundle.kindofEquals(origSuspendParams.getLauncherExtras(), + testSuspendParams.getLauncherExtras()), is(true)); + // Verify that disabledComponents and enabledComponents are copied + assertThat(origPus.getDisabledComponents(), is(notNullValue())); + assertThat(origPus.getDisabledComponents().equals(testPus.getDisabledComponents()), + is(true)); + assertThat(origPus.getEnabledComponents(), is(notNullValue())); + assertThat(origPus.getEnabledComponents().equals(testPus.getEnabledComponents()), + is(true)); + } + + private boolean userStateEquals(PackageUserState userState, PackageUserState oldUserState) { + return userState.isHidden() == oldUserState.isHidden() + && userState.isStopped() == oldUserState.isStopped() + && userState.isInstalled() == oldUserState.isInstalled() + && userState.isSuspended() == oldUserState.isSuspended() + && userState.isNotLaunched() == oldUserState.isNotLaunched() + && userState.isInstantApp() == oldUserState.isInstantApp() + && userState.isVirtualPreload() == oldUserState.isVirtualPreload() + && (userState.getAllOverlayPaths() != null + ? userState.getAllOverlayPaths().equals(oldUserState.getAllOverlayPaths()) + : oldUserState.getOverlayPaths() == null) + && userState.getCeDataInode() == oldUserState.getCeDataInode() + && userState.getDistractionFlags() == oldUserState.getDistractionFlags() + && userState.getFirstInstallTime() == oldUserState.getFirstInstallTime() + && userState.getEnabledState() == oldUserState.getEnabledState() + && userState.getHarmfulAppWarning().equals(oldUserState.getHarmfulAppWarning()) + && userState.getInstallReason() == oldUserState.getInstallReason() + && userState.getLastDisableAppCaller().equals( + oldUserState.getLastDisableAppCaller()) + && (userState.getSharedLibraryOverlayPaths() != null + ? userState.getSharedLibraryOverlayPaths().equals( + oldUserState.getSharedLibraryOverlayPaths()) + : oldUserState.getSharedLibraryOverlayPaths() == null) + && userState.getSplashScreenTheme().equals( + oldUserState.getSplashScreenTheme()) + && userState.getUninstallReason() == oldUserState.getUninstallReason(); + } + private SharedUserSetting createSharedUserSetting(Settings settings, String userName, int sharedUserId, int pkgFlags, int pkgPrivateFlags) { return settings.addSharedUserLPw( diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 6c7236993df0..a0edb85e64e1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -236,7 +236,7 @@ public class PackageUserStateTest { testUserState1.setSuspendParams(paramsMap1); PackageUserStateImpl testUserState2 = - new PackageUserStateImpl(testUserState1); + new PackageUserStateImpl(null, testUserState1); assertThat(testUserState1.equals(testUserState2), is(true)); testUserState2.setSuspendParams(paramsMap2); // Should not be equal since suspendParams maps are different @@ -250,12 +250,12 @@ public class PackageUserStateTest { userState1.setDistractionFlags(PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS); final PackageUserStateImpl copyOfUserState1 = - new PackageUserStateImpl(userState1); + new PackageUserStateImpl(null, userState1); assertThat(userState1.getDistractionFlags(), is(copyOfUserState1.getDistractionFlags())); assertThat(userState1.equals(copyOfUserState1), is(true)); final PackageUserStateImpl userState2 = - new PackageUserStateImpl(userState1); + new PackageUserStateImpl(null, userState1); userState2.setDistractionFlags(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS); assertThat(userState1.equals(userState2), is(false)); } 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..d8c653ce3c51 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -49,7 +49,6 @@ import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.ArraySet; import android.view.SurfaceControl; -import android.view.TransactionCommittedListener; import android.window.IDisplayAreaOrganizer; import android.window.ITaskOrganizer; import android.window.ITransitionPlayer; @@ -533,8 +532,8 @@ public class TransitionTests extends WindowTestsBase { assertTrue(asyncRotationController.isTargetToken(statusBar.mToken)); final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); - final ArgumentCaptor<TransactionCommittedListener> listenerCaptor = - ArgumentCaptor.forClass(TransactionCommittedListener.class); + final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor = + ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class); player.onTransactionReady(startTransaction); verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture()); @@ -739,6 +738,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/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 98a41bcf5adf..4a761a7a47be 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1814,6 +1814,7 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { mResponseStatsTracker.dump(idpw); } + return; } else if (arg != null && !arg.startsWith("-")) { // Anything else that doesn't start with '-' is a pkg to filter pkgs.add(arg); diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java index 85b1de5478e1..337e1f92050c 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.annotation.NonNull; +import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.media.IAudioService; import android.os.RemoteException; @@ -213,24 +214,25 @@ public final class UsbAlsaDevice { int outputState = (enable && connected) ? 1 : 0; if (outputState != mOutputState) { mOutputState = outputState; - mAudioService.setWiredDeviceConnectionState(device, outputState, - alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG); } } // Input Device if (mHasInput) { - int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET + int device = mIsInputHeadset + ? AudioSystem.DEVICE_IN_USB_HEADSET : AudioSystem.DEVICE_IN_USB_DEVICE; boolean connected = isInputJackConnected(); Slog.i(TAG, "INPUT JACK connected: " + connected); int inputState = (enable && connected) ? 1 : 0; if (inputState != mInputState) { mInputState = inputState; - mAudioService.setWiredDeviceConnectionState( - device, inputState, alsaCardDeviceString, - mDeviceName, TAG); + AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, + alsaCardDeviceString, mDeviceName); + mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG); } } } catch (RemoteException e) { diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e560f34cfcd8..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/Annotation.java b/telephony/java/android/telephony/Annotation.java index e88106cb95fe..86b98f1cbe79 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -127,7 +127,9 @@ public class Annotation { ApnSetting.TYPE_EMERGENCY, ApnSetting.TYPE_MCX, ApnSetting.TYPE_XCAP, - // ApnSetting.TYPE_ENTERPRISE + ApnSetting.TYPE_BIP, + ApnSetting.TYPE_VSIM, + ApnSetting.TYPE_ENTERPRISE }) @Retention(RetentionPolicy.SOURCE) public @interface ApnType { @@ -707,6 +709,9 @@ public class Annotation { NetworkCapabilities.NET_CAPABILITY_VSIM, NetworkCapabilities.NET_CAPABILITY_BIP, NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT, + NetworkCapabilities.NET_CAPABILITY_MMTEL, + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY, + NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH }) public @interface NetCapability { } @@ -721,4 +726,16 @@ public class Annotation { NetworkAgent.VALIDATION_STATUS_NOT_VALID }) public @interface ValidationStatus {} + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = { + NetworkCapabilities.NET_ENTERPRISE_ID_1, + NetworkCapabilities.NET_ENTERPRISE_ID_2, + NetworkCapabilities.NET_ENTERPRISE_ID_3, + NetworkCapabilities.NET_ENTERPRISE_ID_4, + NetworkCapabilities.NET_ENTERPRISE_ID_5 + }) + + public @interface EnterpriseId {} } diff --git a/telephony/java/android/telephony/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/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index a166a5d6404c..fa1bae41f433 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -26,10 +26,10 @@ import android.annotation.SystemApi; import android.net.NetworkCapabilities; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetCapability; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.NetworkTypeBitMask; +import android.telephony.data.ApnSetting.ApnType; import android.telephony.data.ApnSetting.AuthType; import android.text.TextUtils; @@ -245,8 +245,7 @@ public final class DataProfile implements Parcelable { * @return The supported APN types bitmask. * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnTypeBitmask()} instead. */ - @Deprecated - public @ApnType int getSupportedApnTypesBitmask() { + @Deprecated public @ApnType int getSupportedApnTypesBitmask() { if (mApnSetting != null) { return mApnSetting.getApnTypeBitmask(); } @@ -425,6 +424,12 @@ public final class DataProfile implements Parcelable { return ApnSetting.TYPE_MCX; case NetworkCapabilities.NET_CAPABILITY_IA: return ApnSetting.TYPE_IA; + case NetworkCapabilities.NET_CAPABILITY_BIP: + return ApnSetting.TYPE_BIP; + case NetworkCapabilities.NET_CAPABILITY_VSIM: + return ApnSetting.TYPE_VSIM; + case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE: + return ApnSetting.TYPE_ENTERPRISE; default: return ApnSetting.TYPE_NONE; } diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index 1ff6ec1779cd..ec734716f6e4 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -261,9 +261,10 @@ public class DataServiceCallback { } /** - * Unthrottles the APN on the current transport. There is no matching "APN throttle" method. - * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within - * the time specified by {@link DataCallResponse#getRetryDurationMillis}. + * Unthrottles the APN on the current transport. + * The APN is throttled when {@link IDataService#setupDataCall} fails within + * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain + * throttled until this method is called. * <p/> * see: {@link DataCallResponse#getRetryDurationMillis} * @@ -284,9 +285,9 @@ public class DataServiceCallback { /** * Unthrottles the DataProfile on the current transport. - * There is no matching "DataProfile throttle" method. - * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within - * the time specified by {@link DataCallResponse#getRetryDurationMillis}. + * The DataProfile is throttled when {@link IDataService#setupDataCall} fails within + * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain + * throttled until this method is called. * <p/> * see: {@link DataCallResponse#getRetryDurationMillis} * diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java index 2178fc1717b9..66dcf8f49e9c 100644 --- a/telephony/java/android/telephony/data/TrafficDescriptor.java +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -21,8 +21,13 @@ import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; +import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection @@ -31,24 +36,215 @@ import java.util.Objects; * not specify the end point to be used for the data call. */ public final class TrafficDescriptor implements Parcelable { + /** + * The OS/App id + * + * @hide + */ + public static final class OsAppId { + /** + * OSId for "Android", using UUID version 5 with namespace ISO OSI. + * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching. + */ + public static final UUID ANDROID_OS_ID = + UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47"); + + /** + * Allowed app ids. + */ + // The following app ids are the only apps id Android supports. OEMs or vendors are + // prohibited to modify/extend the allowed list, especially passing the real package name to + // the network. + private static final Set<String> ALLOWED_APP_IDS = Set.of( + "ENTERPRISE", "PRIORITIZE_LATENCY", "PRIORITIZE_BANDWIDTH", "CBS" + ); + + /** OS id in UUID format. */ + private final @NonNull UUID mOsId; + + /** + * App id in string format. Note that Android will not allow use specific app id. This must + * be a category/capability identifier. + */ + private final @NonNull String mAppId; + + /** + * The differentiator when multiple traffic descriptor has the same OS and app id. Must be + * greater than 1. + */ + private final int mDifferentiator; + + /** + * Constructor + * + * @param osId OS id in UUID format. + * @param appId App id in string format. Note that Android will not allow use specific app + * id. This must be a category/capability identifier. + */ + public OsAppId(@NonNull UUID osId, @NonNull String appId) { + this(osId, appId, 1); + } + + /** + * Constructor + * + * @param osId OS id in UUID format. + * @param appId App id in string format. Note that Android will not allow use specific app + * id. This must be a category/capability identifier. + * @param differentiator The differentiator when multiple traffic descriptor has the same + * OS and app id. Must be greater than 0. + */ + public OsAppId(@NonNull UUID osId, @NonNull String appId, int differentiator) { + Objects.requireNonNull(osId); + Objects.requireNonNull(appId); + if (differentiator < 1) { + throw new IllegalArgumentException("Invalid differentiator " + differentiator); + } + + mOsId = osId; + mAppId = appId; + mDifferentiator = differentiator; + } + + /** + * Constructor from raw byte array. + * + * @param rawOsAppId The raw OS/App id. + */ + public OsAppId(@NonNull byte[] rawOsAppId) { + try { + ByteBuffer bb = ByteBuffer.wrap(rawOsAppId); + // OS id is the first 16 bytes. + mOsId = new UUID(bb.getLong(), bb.getLong()); + // App id length is 1 byte. + int appIdLen = bb.get(); + // The remaining is the app id + differentiator. + byte[] appIdAndDifferentiator = new byte[appIdLen]; + bb.get(appIdAndDifferentiator, 0, appIdLen); + // Extract trailing numbers, for example, "ENTERPRISE", "ENTERPRISE3". + String appIdAndDifferentiatorStr = new String(appIdAndDifferentiator); + Pattern pattern = Pattern.compile("[^0-9]+([0-9]+)$"); + Matcher matcher = pattern.matcher(new String(appIdAndDifferentiator)); + if (matcher.find()) { + mDifferentiator = Integer.parseInt(matcher.group(1)); + mAppId = appIdAndDifferentiatorStr.replace(matcher.group(1), ""); + } else { + mDifferentiator = 1; + mAppId = appIdAndDifferentiatorStr; + } + } catch (Exception e) { + throw new IllegalArgumentException("Failed to decode " + (rawOsAppId != null + ? new BigInteger(1, rawOsAppId).toString(16) : null)); + } + } + + /** + * @return The OS id in UUID format. + */ + public @NonNull UUID getOsId() { + return mOsId; + } + + /** + * @return App id in string format. Note that Android will not allow use specific app id. + * This must be a category/capability identifier. + */ + public @NonNull String getAppId() { + return mAppId; + } + + /** + * @return The differentiator when multiple traffic descriptor has the same OS and app id. + * Must be greater than 1. + */ + public int getDifferentiator() { + return mDifferentiator; + } + + /** + * @return OS/App id in raw byte format. + */ + public @NonNull byte[] getBytes() { + byte[] osAppId = (mAppId + (mDifferentiator > 1 ? mDifferentiator : "")).getBytes(); + // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId + ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length); + bb.putLong(mOsId.getMostSignificantBits()); + bb.putLong(mOsId.getLeastSignificantBits()); + bb.put((byte) osAppId.length); + bb.put(osAppId); + return bb.array(); + } + + @Override + public String toString() { + return "[OsAppId: OS=" + mOsId + ", App=" + mAppId + ", differentiator=" + + mDifferentiator + ", raw=" + + new BigInteger(1, getBytes()).toString(16) + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OsAppId osAppId = (OsAppId) o; + return mDifferentiator == osAppId.mDifferentiator && mOsId.equals(osAppId.mOsId) + && mAppId.equals(osAppId.mAppId); + } + + @Override + public int hashCode() { + return Objects.hash(mOsId, mAppId, mDifferentiator); + } + } + private final String mDnn; - private final byte[] mOsAppId; + private final OsAppId mOsAppId; private TrafficDescriptor(@NonNull Parcel in) { mDnn = in.readString(); - mOsAppId = in.createByteArray(); + byte[] osAppIdBytes = in.createByteArray(); + OsAppId osAppId = null; + if (osAppIdBytes != null) { + osAppId = new OsAppId(osAppIdBytes); + } + mOsAppId = osAppId; + + enforceAllowedIds(); } /** * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 * @param dnn optional DNN, which must be used for traffic matching, if present - * @param osAppId OsId + osAppId of the traffic descriptor + * @param osAppIdRawBytes Raw bytes of OsId + osAppId of the traffic descriptor * * @hide */ - public TrafficDescriptor(String dnn, byte[] osAppId) { + public TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes) { mDnn = dnn; + OsAppId osAppId = null; + if (osAppIdRawBytes != null) { + osAppId = new OsAppId(osAppIdRawBytes); + } mOsAppId = osAppId; + + enforceAllowedIds(); + } + + /** + * Enforce the OS id and app id are in the allowed list. + * + * @throws IllegalArgumentException if ids are not allowed. + */ + private void enforceAllowedIds() { + if (mOsAppId != null && !mOsAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) { + throw new IllegalArgumentException("OS id " + mOsAppId.getOsId() + " does not match " + + OsAppId.ANDROID_OS_ID); + } + + if (mOsAppId != null && !OsAppId.ALLOWED_APP_IDS.contains(mOsAppId.getAppId())) { + throw new IllegalArgumentException("Illegal app id " + mOsAppId.getAppId() + + ". Only allowing one of the following " + OsAppId.ALLOWED_APP_IDS); + } } /** @@ -61,13 +257,13 @@ public final class TrafficDescriptor implements Parcelable { } /** - * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic - * category. It includes the OS Id component of the field as defined in the specs. - * @return the OS App ID of this traffic descriptor if one is included by the network, null - * otherwise. + * OsAppId identifies a broader traffic category. Although it names Os/App id, it only includes + * OS version with a general/broader category id used as app id. + * + * @return The id in byte format. {@code null} if not available. */ public @Nullable byte[] getOsAppId() { - return mOsAppId; + return mOsAppId != null ? mOsAppId.getBytes() : null; } @Override @@ -77,13 +273,13 @@ public final class TrafficDescriptor implements Parcelable { @NonNull @Override public String toString() { - return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}"; + return "TrafficDescriptor={mDnn=" + mDnn + ", " + mOsAppId + "}"; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mDnn); - dest.writeByteArray(mOsAppId); + dest.writeByteArray(mOsAppId != null ? mOsAppId.getBytes() : null); } public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR = @@ -104,7 +300,7 @@ public final class TrafficDescriptor implements Parcelable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TrafficDescriptor that = (TrafficDescriptor) o; - return Objects.equals(mDnn, that.mDnn) && Arrays.equals(mOsAppId, that.mOsAppId); + return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId); } @Override @@ -148,7 +344,7 @@ public final class TrafficDescriptor implements Parcelable { } /** - * Set the OS App ID (including OS Id as defind in the specs). + * Set the OS App ID (including OS Id as defined in the specs). * * @return The same instance of the builder. */ diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java index c25ace0c6a62..f367e404a35b 100644 --- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java +++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java @@ -34,7 +34,7 @@ public final class RcsClientConfiguration implements Parcelable { /**@hide*/ @StringDef(prefix = "RCS_PROFILE_", - value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3}) + value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4}) public @interface StringRcsProfile {} /** @@ -45,6 +45,10 @@ public final class RcsClientConfiguration implements Parcelable { * RCS profile UP 2.3 */ public static final String RCS_PROFILE_2_3 = "UP_2.3"; + /** + * RCS profile UP 2.4 + */ + public static final String RCS_PROFILE_2_4 = "UP_2.4"; private String mRcsVersion; private String mRcsProfile; @@ -58,8 +62,8 @@ public final class RcsClientConfiguration implements Parcelable { * @param rcsVersion The parameter identifies the RCS version supported * by the client. Refer to GSMA RCC.07 "rcs_version" parameter. * @param rcsProfile Identifies a fixed set of RCS services that are - * supported by the client. See {@link #RCS_PROFILE_1_0 } or - * {@link #RCS_PROFILE_2_3 } + * supported by the client. See {@link #RCS_PROFILE_1_0 }, + * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 } * @param clientVendor Identifies the vendor providing the RCS client. * @param clientVersion Identifies the RCS client version. Refer to GSMA * RCC.07 "client_version" parameter. @@ -80,8 +84,8 @@ public final class RcsClientConfiguration implements Parcelable { * @param rcsVersion The parameter identifies the RCS version supported * by the client. Refer to GSMA RCC.07 "rcs_version" parameter. * @param rcsProfile Identifies a fixed set of RCS services that are - * supported by the client. See {@link #RCS_PROFILE_1_0 } or - * {@link #RCS_PROFILE_2_3 } + * supported by the client. See {@link #RCS_PROFILE_1_0 }, + * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 } * @param clientVendor Identifies the vendor providing the RCS client. * @param clientVersion Identifies the RCS client version. Refer to GSMA * RCC.07 "client_version" parameter. diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt index 48bfd6f5d33c..62902929fcd5 100644 --- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt +++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt @@ -11,10 +11,20 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import com.google.common.truth.Truth.assertThat +import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE +import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED -import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY +import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE +import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN +import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY +import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties import java.lang.IllegalArgumentException +import java.io.ByteArrayOutputStream +import java.security.KeyPairGenerator +import java.security.KeyStore import java.time.Duration import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -23,25 +33,26 @@ import java.util.concurrent.TimeUnit @SmallTest @RunWith(AndroidJUnit4::class) class SystemAttestationVerificationTest { - @get:Rule val rule = ActivityScenarioRule(TestActivity::class.java) private lateinit var activity: Activity private lateinit var avm: AttestationVerificationManager + private lateinit var androidKeystore: KeyStore @Before fun setup() { rule.getScenario().onActivity { avm = it.getSystemService(AttestationVerificationManager::class.java) activity = it + androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } } } @Test fun verifyAttestation_returnsUnknown() { val future = CompletableFuture<Int>() - val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { result, _ -> future.complete(result) @@ -51,9 +62,82 @@ class SystemAttestationVerificationTest { } @Test - fun verifyToken_returnsUnknown() { + fun verifyAttestation_returnsFailureWithEmptyAttestation() { val future = CompletableFuture<Int>() val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0), + activity.mainExecutor) { result, _ -> + future.complete(result) + } + + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithEmptyRequirements() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongBindingType() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY, + selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongRequirements() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + val wrongKeyRequirements = Bundle() + wrongKeyRequirements.putByteArray( + "wrongBindingKey", "challengeStr".encodeToByteArray()) + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + @Test + fun verifyAttestation_returnsFailureWithWrongChallenge() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + val wrongChallengeRequirements = Bundle() + wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray()) + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) { + result, _ -> future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE) + } + + // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED. + @Test + fun verifyAttestation_returnsSuccess() { + val future = CompletableFuture<Int>() + val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr") + avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType, + selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ -> + future.complete(result) + } + assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS) + } + + @Test + fun verifyToken_returnsUnknown() { + val future = CompletableFuture<Int>() + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { _, token -> val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null) @@ -66,7 +150,7 @@ class SystemAttestationVerificationTest { @Test fun verifyToken_tooBigMaxAgeThrows() { val future = CompletableFuture<VerificationToken>() - val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val profile = AttestationProfile(PROFILE_PEER_DEVICE) avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0), activity.mainExecutor) { _, token -> future.complete(token) @@ -87,4 +171,52 @@ class SystemAttestationVerificationTest { super.onCreate(savedInstanceState) } } + + inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) { + val profile = AttestationProfile(PROFILE_SELF_TRUSTED) + val localBindingType = TYPE_CHALLENGE + val requirements: Bundle + val attestation: ByteArray + + init { + val challengeByteArray = challenge.encodeToByteArray() + generateAndStoreKey(alias, challengeByteArray) + attestation = generateCertificatesByteArray(alias) + requirements = Bundle() + requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray) + } + + private fun generateAndStoreKey(alias: String, challenge: ByteArray) { + val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_EC, + ANDROID_KEYSTORE + ) + val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( + alias, + KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY + ).run { + // a challenge results in a generated attestation + setAttestationChallenge(challenge) + setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + build() + } + kpg.initialize(parameterSpec) + kpg.generateKeyPair() + } + + private fun generateCertificatesByteArray(alias: String): ByteArray { + val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry + val certs = pkEntry.certificateChain + val bos = ByteArrayOutputStream() + certs.forEach { + bos.write(it.encoded) + } + return bos.toByteArray() + } + } + + companion object { + private const val TAG = "AVFTEST" + private const val ANDROID_KEYSTORE = "AndroidKeyStore" + } } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index 566c725a3414..98d13e81551d 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -26,8 +26,16 @@ <option name="shell-timeout" value="6600s" /> <option name="test-timeout" value="6600s" /> <option name="hidden-api-checks" value="false" /> + <option name="device-listeners" + value="com.android.server.wm.flicker.TraceFileReadyListener" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="(\w)+\.winscope" /> + <option name="pull-pattern-keys" value="(\w)+\.mp4" /> + <option name="collect-on-run-ended-only" value="false" /> + <option name="clean-up" value="true" /> + </metrics_collector> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index 8fe00297139a..b66c45c7c9f0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -19,11 +19,13 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.support.test.launcherhelper.ILauncherStrategy import android.support.test.launcherhelper.LauncherStrategyFactory +import android.view.Display import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper @@ -36,6 +38,10 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( .getInstance(instr) .launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { + + private val secondActivityComponent = + ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent() + fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) { val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY) val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT) @@ -47,8 +53,11 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( button.click() device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT) - wmHelper.waitForAppTransitionIdle() - wmHelper.waitForFullScreenApp(component) + wmHelper.waitForFullScreenApp(secondActivityComponent) + wmHelper.waitFor( + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), + WindowManagerConditionsFactory.hasLayersAnimating().negate() + ) } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index 648353e34f92..195af589d77c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -70,7 +70,7 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) { fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { - eachRun { + test { testApp.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(testApp.component) } diff --git a/tests/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/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml index 16dea484b644..bd8942d46383 100644 --- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml +++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:color="@android:color/system_neutral2_200"> - <item android:drawable="@drawable/letterbox_education_ic_expand_more"/> -</ripple>
\ No newline at end of file +<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(); + } +} diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 41f73cd9c706..228520e8545b 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -18,6 +18,7 @@ android_test { "java/**/*.kt", ], platform_apis: true, + defaults: ["framework-connectivity-test-defaults"], test_suites: ["device-tests"], certificate: "platform", static_libs: [ @@ -28,6 +29,7 @@ android_test { "net-tests-utils", "platform-test-annotations", "services.core", + "service-connectivity-tiramisu-pre-jarjar", ], libs: [ "android.test.runner", diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml index 2ad9aac67029..a8f657c89f76 100644 --- a/tests/vcn/AndroidManifest.xml +++ b/tests/vcn/AndroidManifest.xml @@ -16,7 +16,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.tests.vcn"> - + <uses-sdk android:minSdkVersion="33" + android:targetSdkVersion="33"/> <application> <uses-library android:name="android.test.runner" /> </application> diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index 99f77fee4698..2c2c91825162 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -635,23 +635,11 @@ def compute_expected_emoji(): sequence = tuple(ch for ch in sequence if ch != EMOJI_VS) all_sequences.add(sequence) sequence_pieces.update(sequence) - if _emoji_sequences.get(sequence, None) == 'Emoji_Tag_Sequence': - # Add reverse of all emoji ZWJ sequences, which are added to the - # fonts as a workaround to get the sequences work in RTL text. - # TODO: test if these are actually needed by Minikin/HarfBuzz. - reversed_seq = reverse_emoji(sequence) - all_sequences.add(reversed_seq) - equivalent_emoji[reversed_seq] = sequence for sequence in adjusted_emoji_zwj_sequences.keys(): sequence = tuple(ch for ch in sequence if ch != EMOJI_VS) all_sequences.add(sequence) sequence_pieces.update(sequence) - # Add reverse of all emoji ZWJ sequences, which are added to the fonts - # as a workaround to get the sequences work in RTL text. - reversed_seq = reverse_emoji(sequence) - all_sequences.add(reversed_seq) - equivalent_emoji[reversed_seq] = sequence for first, second in SAME_FLAG_MAPPINGS: equivalent_emoji[first] = second |