Merge "Prevent apps from publishing system routes" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2457e70..76377e7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -29,6 +29,7 @@
":android.service.notification.flags-aconfig-java{.generated_srcjars}",
":android.view.flags-aconfig-java{.generated_srcjars}",
":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
+ ":audio-framework-aconfig",
":camera_platform_flags_core_java_lib{.generated_srcjars}",
":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
@@ -39,7 +40,6 @@
":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
":android.widget.flags-aconfig-java{.generated_srcjars}",
- ":com.android.media.audio.flags-aconfig-java{.generated_srcjars}",
":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
":sdk_sandbox_flags_lib{.generated_srcjars}",
":android.permission.flags-aconfig-java{.generated_srcjars}",
@@ -53,7 +53,7 @@
":android.credentials.flags-aconfig-java{.generated_srcjars}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
- ":aconfig_midi_flags_java_lib{.generated_srcjars}",
+ ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
":device_policy_aconfig_flags_lib{.generated_srcjars}",
@@ -379,10 +379,16 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
-// Media Audio
+// Media TV
+aconfig_declarations {
+ name: "android.media.tv.flags-aconfig",
+ package: "android.media.tv.flags",
+ srcs: ["media/java/android/media/tv/flags/media_tv.aconfig"],
+}
+
java_aconfig_library {
- name: "com.android.media.audio.flags-aconfig-java",
- aconfig_declarations: "aconfig_audio_flags",
+ name: "android.media.tv.flags-aconfig-java",
+ aconfig_declarations: "android.media.tv.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -570,6 +576,24 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Media
+aconfig_declarations {
+ name: "android.media.playback.flags-aconfig",
+ package: "com.android.media.playback.flags",
+ srcs: ["media/jni/playback_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "android.media.playback.flags-aconfig-cc",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+}
+
+java_aconfig_library {
+ name: "android.media.playback.flags-aconfig-java",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// VCN
aconfig_declarations {
name: "android.net.vcn.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 8c4d769..49386d4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -614,19 +614,6 @@
],
}
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
filegroup {
name: "android-non-updatable-stub-sources",
srcs: [
@@ -641,142 +628,6 @@
visibility: ["//frameworks/base/api"],
}
-// Defaults for all stubs that include the non-updatable framework. These defaults do not include
-// module symbols, so will not compile correctly on their own. Users must add module APIs to the
-// classpath (or sources) somehow.
-stubs_defaults {
- name: "android-non-updatable-stubs-defaults",
- srcs: [":android-non-updatable-stub-sources"],
- sdk_version: "none",
- system_modules: "none",
- java_version: "1.8",
- arg_files: [":frameworks-base-core-AndroidManifest.xml"],
- aidl: {
- include_dirs: [
- "frameworks/av/aidl",
- "frameworks/base/media/aidl",
- "frameworks/base/telephony/java",
- "frameworks/native/libs/permission/aidl",
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- "hardware/interfaces/biometrics/common/aidl",
- "hardware/interfaces/biometrics/fingerprint/aidl",
- "hardware/interfaces/graphics/common/aidl",
- "hardware/interfaces/keymaster/aidl",
- "system/hardware/interfaces/media/aidl",
- ],
- },
- // These are libs from framework-internal-utils that are required (i.e. being referenced)
- // from framework-non-updatable-sources. Add more here when there's a need.
- // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
- // dependencies gets bigger.
- libs: [
- "android.hardware.cas-V1.2-java",
- "android.hardware.health-V1.0-java-constants",
- "android.hardware.thermal-V1.0-java-constants",
- "android.hardware.thermal-V2.0-java",
- "android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.usb-V1.0-java-constants",
- "android.hardware.usb-V1.1-java-constants",
- "android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
- "framework-protos",
- ],
- flags: [
- "--api-lint-ignore-prefix android.icu.",
- "--api-lint-ignore-prefix java.",
- "--api-lint-ignore-prefix junit.",
- "--api-lint-ignore-prefix org.",
- "--error NoSettingsProvider",
- "--error UnhiddenSystemApi",
- "--error UnflaggedApi",
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
- "--hide BroadcastBehavior",
- "--hide CallbackInterface",
- "--hide DeprecationMismatch",
- "--hide HiddenSuperclass",
- "--hide MissingPermission",
- "--hide RequiresPermission",
- "--hide SdkConstant",
- "--hide Todo",
- "--hide-package android.audio.policy.configuration.V7_0",
- "--hide-package com.android.server",
- "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
- ],
- filter_packages: packages_to_document,
- high_mem: true, // Lots of sources => high memory use, see b/170701554
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: ["metalava-manual"],
- defaults_visibility: ["//frameworks/base/api"],
- visibility: ["//frameworks/base/api"],
-}
-
-// Defaults with module APIs in the classpath (mostly from prebuilts).
-// Suitable for compiling android-non-updatable.
-stubs_defaults {
- name: "module-classpath-stubs-defaults",
- aidl: {
- include_dirs: [
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- ],
- },
- libs: [
- "art.module.public.api",
- "sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-t",
- "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
- // full sdk to ensure we can resolve them. If a new class gets added,
- // the prebuilts/sdk/current needs to be updated.
- "sdk_system_current_android",
- // NOTE: The below can be removed once the prebuilt stub contains IKE.
- "sdk_system_current_android.net.ipsec.ike",
- ],
-}
-
-// Defaults for the java_sdk_libraries of unbundled jars from framework.
-// java_sdk_libraries using these defaults should also add themselves to the
-// non_updatable_modules list in frameworks/base/api/api.go
-java_defaults {
- name: "framework-non-updatable-unbundled-defaults",
- defaults: [
- "framework-non-updatable-lint-defaults",
- "non-updatable-framework-module-defaults",
- ],
- public: {
- libs: ["android_module_lib_stubs_current"],
- },
- system: {
- libs: ["android_module_lib_stubs_current"],
- },
- module_lib: {
- libs: ["android_module_lib_stubs_current"],
- },
- test: {
- libs: ["android_test_frameworks_core_stubs_current"],
- },
- sdk_version: "core_platform",
- stub_only_libs: ["framework-protos"],
- impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
- impl_library_visibility: ["//frameworks/base"],
- defaults_visibility: ["//frameworks/base/location"],
- plugins: ["error_prone_android_framework"],
- errorprone: {
- javacflags: [
- "-Xep:AndroidFrameworkCompatChange:ERROR",
- "-Xep:AndroidFrameworkUid:ERROR",
- ],
- },
- // Include manual annotations in API txt files
- merge_annotations_dirs: ["metalava-manual"],
-}
-
build = [
"AconfigFlags.bp",
"ProtoLibraries.bp",
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index a92a01f..646e05c 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -432,7 +432,6 @@
@UnsupportedAppUsage
private final ComponentName service;
private final int constraintFlags;
- private final int mPreferredConstraintFlags;
private final TriggerContentUri[] triggerContentUris;
private final long triggerContentUpdateDelay;
private final long triggerContentMaxDelay;
@@ -523,30 +522,6 @@
}
/**
- * @hide
- * @see JobInfo.Builder#setPrefersBatteryNotLow(boolean)
- */
- public boolean isPreferBatteryNotLow() {
- return (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
- }
-
- /**
- * @hide
- * @see JobInfo.Builder#setPrefersCharging(boolean)
- */
- public boolean isPreferCharging() {
- return (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
- }
-
- /**
- * @hide
- * @see JobInfo.Builder#setPrefersDeviceIdle(boolean)
- */
- public boolean isPreferDeviceIdle() {
- return (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
- }
-
- /**
* @see JobInfo.Builder#setRequiresCharging(boolean)
*/
public boolean isRequireCharging() {
@@ -582,13 +557,6 @@
}
/**
- * @hide
- */
- public int getPreferredConstraintFlags() {
- return mPreferredConstraintFlags;
- }
-
- /**
* Which content: URIs must change for the job to be scheduled. Returns null
* if there are none required.
* @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
@@ -832,9 +800,6 @@
if (constraintFlags != j.constraintFlags) {
return false;
}
- if (mPreferredConstraintFlags != j.mPreferredConstraintFlags) {
- return false;
- }
if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
return false;
}
@@ -915,7 +880,6 @@
hashCode = 31 * hashCode + service.hashCode();
}
hashCode = 31 * hashCode + constraintFlags;
- hashCode = 31 * hashCode + mPreferredConstraintFlags;
if (triggerContentUris != null) {
hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
}
@@ -958,7 +922,6 @@
}
service = in.readParcelable(null);
constraintFlags = in.readInt();
- mPreferredConstraintFlags = in.readInt();
triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
triggerContentUpdateDelay = in.readLong();
triggerContentMaxDelay = in.readLong();
@@ -993,7 +956,6 @@
clipGrantFlags = b.mClipGrantFlags;
service = b.mJobService;
constraintFlags = b.mConstraintFlags;
- mPreferredConstraintFlags = b.mPreferredConstraintFlags;
triggerContentUris = b.mTriggerContentUris != null
? b.mTriggerContentUris.toArray(new TriggerContentUri[b.mTriggerContentUris.size()])
: null;
@@ -1037,7 +999,6 @@
}
out.writeParcelable(service, flags);
out.writeInt(constraintFlags);
- out.writeInt(mPreferredConstraintFlags);
out.writeTypedArray(triggerContentUris, flags);
out.writeLong(triggerContentUpdateDelay);
out.writeLong(triggerContentMaxDelay);
@@ -1185,7 +1146,6 @@
private int mFlags;
// Requirements.
private int mConstraintFlags;
- private int mPreferredConstraintFlags;
private NetworkRequest mNetworkRequest;
private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
@@ -1239,7 +1199,6 @@
mBias = job.getBias();
mFlags = job.getFlags();
mConstraintFlags = job.getConstraintFlags();
- mPreferredConstraintFlags = job.getPreferredConstraintFlags();
mNetworkRequest = job.getRequiredNetwork();
mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
@@ -1389,6 +1348,10 @@
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permission to
* schedule a job that requires a network.
*
+ * <p> Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link JobScheduler} may try to shift the execution of jobs requiring
+ * {@link #NETWORK_TYPE_ANY} to when there is access to an un-metered network.
+ *
* <p class="note">
* When your job executes in
* {@link JobService#onStartJob(JobParameters)}, be sure to use the
@@ -1555,100 +1518,6 @@
}
/**
- * Specify that this job would prefer to be run when the device's battery is not low.
- * This defaults to {@code false}.
- *
- * <p>The system may attempt to delay this job until the device's battery is not low,
- * but may choose to run it even if the device's battery is low. JobScheduler will not stop
- * this job if this constraint is no longer satisfied after the job has started running.
- * If this job must only run when the device's battery is not low,
- * use {@link #setRequiresBatteryNotLow(boolean)} instead.
- *
- * <p>
- * Because it doesn't make sense for a constraint to be both preferred and required,
- * calling both this and {@link #setRequiresBatteryNotLow(boolean)} with {@code true}
- * will result in an {@link java.lang.IllegalArgumentException} when
- * {@link android.app.job.JobInfo.Builder#build()} is called.
- *
- * @param prefersBatteryNotLow Pass {@code true} to prefer that the device's battery level
- * not be low in order to run the job.
- * @return This object for method chaining
- * @see JobInfo#isPreferBatteryNotLow()
- * @hide
- */
- @NonNull
- public Builder setPrefersBatteryNotLow(boolean prefersBatteryNotLow) {
- mPreferredConstraintFlags =
- (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
- | (prefersBatteryNotLow ? CONSTRAINT_FLAG_BATTERY_NOT_LOW : 0);
- return this;
- }
-
- /**
- * Specify that this job would prefer to be run when the device is charging (or be a
- * non-battery-powered device connected to permanent power, such as Android TV
- * devices). This defaults to {@code false}.
- *
- * <p>
- * The system may attempt to delay this job until the device is charging, but may
- * choose to run it even if the device is not charging. JobScheduler will not stop
- * this job if this constraint is no longer satisfied after the job has started running.
- * If this job must only run when the device is charging,
- * use {@link #setRequiresCharging(boolean)} instead.
- *
- * <p>
- * Because it doesn't make sense for a constraint to be both preferred and required,
- * calling both this and {@link #setRequiresCharging(boolean)} with {@code true}
- * will result in an {@link java.lang.IllegalArgumentException} when
- * {@link android.app.job.JobInfo.Builder#build()} is called.
- *
- * @param prefersCharging Pass {@code true} to prefer that the device be
- * charging in order to run the job.
- * @return This object for method chaining
- * @see JobInfo#isPreferCharging()
- * @hide
- */
- @NonNull
- public Builder setPrefersCharging(boolean prefersCharging) {
- mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_CHARGING)
- | (prefersCharging ? CONSTRAINT_FLAG_CHARGING : 0);
- return this;
- }
-
- /**
- * Specify that this job would prefer to be run when the device is not in active use.
- * This defaults to {@code false}.
- *
- * <p>The system may attempt to delay this job until the device is not in active use,
- * but may choose to run it even if the device is not idle. JobScheduler will not stop
- * this job if this constraint is no longer satisfied after the job has started running.
- * If this job must only run when the device is not in active use,
- * use {@link #setRequiresDeviceIdle(boolean)} instead.
- *
- * <p>
- * Because it doesn't make sense for a constraint to be both preferred and required,
- * calling both this and {@link #setRequiresDeviceIdle(boolean)} with {@code true}
- * will result in an {@link java.lang.IllegalArgumentException} when
- * {@link android.app.job.JobInfo.Builder#build()} is called.
- *
- * <p class="note">Despite the similar naming, this job constraint is <em>not</em>
- * related to the system's "device idle" or "doze" states. This constraint only
- * determines whether a job is allowed to run while the device is directly in use.
- *
- * @param prefersDeviceIdle Pass {@code true} to prefer that the device not be in active
- * use when running this job.
- * @return This object for method chaining
- * @see JobInfo#isRequireDeviceIdle()
- * @hide
- */
- @NonNull
- public Builder setPrefersDeviceIdle(boolean prefersDeviceIdle) {
- mPreferredConstraintFlags = (mPreferredConstraintFlags & ~CONSTRAINT_FLAG_DEVICE_IDLE)
- | (prefersDeviceIdle ? CONSTRAINT_FLAG_DEVICE_IDLE : 0);
- return this;
- }
-
- /**
* Specify that to run this job, the device must be charging (or be a
* non-battery-powered device connected to permanent power, such as Android TV
* devices). This defaults to {@code false}. Setting this to {@code false} <b>DOES NOT</b>
@@ -2306,29 +2175,6 @@
}
}
- if ((constraintFlags & mPreferredConstraintFlags) != 0) {
- // Something is marked as both preferred and required. Try to give a clear exception
- // reason.
- if ((constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0
- && (mPreferredConstraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0) {
- throw new IllegalArgumentException(
- "battery-not-low constraint cannot be both preferred and required");
- }
- if ((constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0
- && (mPreferredConstraintFlags & CONSTRAINT_FLAG_CHARGING) != 0) {
- throw new IllegalArgumentException(
- "charging constraint cannot be both preferred and required");
- }
- if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0
- && (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
- throw new IllegalArgumentException(
- "device idle constraint cannot be both preferred and required");
- }
- // Couldn't figure out what the overlap was. Just use a generic message.
- throw new IllegalArgumentException(
- "constraints cannot be both preferred and required");
- }
-
if (isUserInitiated) {
if (hasEarlyConstraint) {
throw new IllegalArgumentException("A user-initiated job cannot have a time delay");
@@ -2346,8 +2192,7 @@
if (mPriority != PRIORITY_MAX) {
throw new IllegalArgumentException("A user-initiated job must be max priority.");
}
- if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0
- || (mPreferredConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
+ if ((constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) {
throw new IllegalArgumentException(
"A user-initiated job cannot have a device-idle constraint");
}
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 1287cb4..23b36e2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1820,7 +1820,11 @@
jobStatus.getEstimatedNetworkUploadBytes(),
jobStatus.getWorkCount(),
ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())),
- jobStatus.getNamespaceHash());
+ jobStatus.getNamespaceHash(),
+ /* system_measured_source_download_bytes */0,
+ /* system_measured_source_upload_bytes */ 0,
+ /* system_measured_calling_download_bytes */0,
+ /* system_measured_calling_upload_bytes */ 0);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -2257,7 +2261,11 @@
cancelled.getEstimatedNetworkUploadBytes(),
cancelled.getWorkCount(),
ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())),
- cancelled.getNamespaceHash());
+ cancelled.getNamespaceHash(),
+ /* system_measured_source_download_bytes */ 0,
+ /* system_measured_source_upload_bytes */ 0,
+ /* system_measured_calling_download_bytes */0,
+ /* system_measured_calling_upload_bytes */ 0);
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 79653f0..2d49cfb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -45,6 +45,7 @@
import android.content.PermissionChecker;
import android.content.ServiceConnection;
import android.net.Network;
+import android.net.TrafficStats;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -241,6 +242,14 @@
private int mDeathMarkInternalStopReason;
private String mDeathMarkDebugReason;
+ private long mInitialDownloadedBytesFromSource;
+
+ private long mInitialUploadedBytesFromSource;
+
+ private long mInitialDownloadedBytesFromCalling;
+
+ private long mInitialUploadedBytesFromCalling;
+
// Debugging: reason this job was last stopped.
public String mStoppedReason;
@@ -472,6 +481,14 @@
}
mJobPackageTracker.noteActive(job);
final int sourceUid = job.getSourceUid();
+
+ // Measure UID baseline traffic for deltas
+ mInitialDownloadedBytesFromSource = TrafficStats.getUidRxBytes(sourceUid);
+ mInitialUploadedBytesFromSource = TrafficStats.getUidTxBytes(sourceUid);
+
+ mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid());
+ mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid());
+
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
// Given that the source tag is set by the calling app, it should be connected
@@ -517,7 +534,11 @@
job.getEstimatedNetworkUploadBytes(),
job.getWorkCount(),
ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
- job.getNamespaceHash());
+ job.getNamespaceHash(),
+ /* system_measured_source_download_bytes */ 0,
+ /* system_measured_source_upload_bytes */ 0,
+ /* system_measured_calling_download_bytes */ 0,
+ /* system_measured_calling_upload_bytes */ 0);
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1586,7 +1607,15 @@
completedJob.getWorkCount(),
ActivityManager
.processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
- completedJob.getNamespaceHash());
+ completedJob.getNamespaceHash(),
+ TrafficStats.getUidRxBytes(completedJob.getSourceUid())
+ - mInitialDownloadedBytesFromSource,
+ TrafficStats.getUidTxBytes(completedJob.getSourceUid())
+ - mInitialUploadedBytesFromSource,
+ TrafficStats.getUidRxBytes(completedJob.getUid())
+ - mInitialDownloadedBytesFromCalling,
+ TrafficStats.getUidTxBytes(completedJob.getUid())
+ - mInitialUploadedBytesFromCalling);
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 1fdf906..d466f0d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -938,15 +938,6 @@
if (job.isRequireStorageNotLow()) {
out.attribute(null, "storage-not-low", Boolean.toString(true));
}
- if (job.isPreferBatteryNotLow()) {
- out.attributeBoolean(null, "prefer-battery-not-low", true);
- }
- if (job.isPreferCharging()) {
- out.attributeBoolean(null, "prefer-charging", true);
- }
- if (job.isPreferDeviceIdle()) {
- out.attributeBoolean(null, "prefer-idle", true);
- }
out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
}
@@ -1638,13 +1629,6 @@
if (val != null) {
jobBuilder.setRequiresStorageNotLow(true);
}
-
- jobBuilder.setPrefersBatteryNotLow(
- parser.getAttributeBoolean(null, "prefer-battery-not-low", false));
- jobBuilder.setPrefersCharging(
- parser.getAttributeBoolean(null, "prefer-charging", false));
- jobBuilder.setPrefersDeviceIdle(
- parser.getAttributeBoolean(null, "prefer-idle", false));
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 63eaa63..ae4e99c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -1263,9 +1263,10 @@
final boolean changed = jobStatus.setConnectivityConstraintSatisfied(nowElapsed, satisfied);
- jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
- && capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));
if (jobStatus.getPreferUnmetered()) {
+ jobStatus.setHasAccessToUnmetered(satisfied && capabilities != null
+ && capabilities.hasCapability(NET_CAPABILITY_NOT_METERED));
+
jobStatus.setFlexibilityConstraintSatisfied(nowElapsed,
mFlexibilityController.isFlexibilitySatisfiedLocked(jobStatus));
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index b9e3b76..0e03ea1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -252,15 +252,15 @@
boolean isFlexibilitySatisfiedLocked(JobStatus js) {
return !mFlexibilityEnabled
|| mService.getUidBias(js.getSourceUid()) == JobInfo.BIAS_TOP_APP
- || getNumSatisfiedFlexibleConstraintsLocked(js)
+ || getNumSatisfiedRequiredConstraintsLocked(js)
>= js.getNumRequiredFlexibleConstraints()
|| mService.isCurrentlyRunningLocked(js);
}
@VisibleForTesting
@GuardedBy("mLock")
- int getNumSatisfiedFlexibleConstraintsLocked(JobStatus js) {
- return Integer.bitCount(mSatisfiedFlexibleConstraints & js.getPreferredConstraintFlags())
+ int getNumSatisfiedRequiredConstraintsLocked(JobStatus js) {
+ return Integer.bitCount(mSatisfiedFlexibleConstraints)
// Connectivity is job-specific, so must be handled separately.
+ (js.getHasAccessToUnmetered() ? 1 : 0);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index a25af71..47d3fd5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -18,13 +18,16 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
import com.android.server.job.controllers.idle.CarIdlenessTracker;
@@ -89,6 +92,19 @@
}
}
+ @Override
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ mIdleTracker.processConstant(properties, key);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void onBatteryStateChangedLocked() {
+ mIdleTracker.onBatteryStateChanged(
+ mService.isBatteryCharging(), mService.isBatteryNotLow());
+ }
+
/**
* State-change notifications from the idleness tracker
*/
@@ -119,7 +135,16 @@
} else {
mIdleTracker = new DeviceIdlenessTracker();
}
- mIdleTracker.startTracking(ctx, this);
+ mIdleTracker.startTracking(ctx, mService, this);
+ }
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ pw.println();
+ pw.println("IdleController:");
+ pw.increaseIndent();
+ mIdleTracker.dumpConstants(pw);
+ pw.decreaseIndent();
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1fb54d5..cb6cc2b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -16,6 +16,7 @@
package com.android.server.job.controllers;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
@@ -24,6 +25,8 @@
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
+import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
@@ -124,11 +127,12 @@
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
static final int CONSTRAINT_PREFETCH = 1 << 23;
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
- static final int CONSTRAINT_FLEXIBLE = 1 << 21;
+ static final int CONSTRAINT_FLEXIBLE = 1 << 21; // Implicit constraint
private static final int IMPLICIT_CONSTRAINTS = 0
| CONSTRAINT_BACKGROUND_NOT_RESTRICTED
| CONSTRAINT_DEVICE_NOT_DOZING
+ | CONSTRAINT_FLEXIBLE
| CONSTRAINT_TARE_WEALTH
| CONSTRAINT_WITHIN_QUOTA;
@@ -323,7 +327,6 @@
// Constraints.
final int requiredConstraints;
- private final int mPreferredConstraints;
private final int mRequiredConstraintsOfInterest;
int satisfiedConstraints = 0;
private int mSatisfiedConstraintsOfInterest = 0;
@@ -668,26 +671,24 @@
}
mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
- mPreferredConstraints = job.getPreferredConstraintFlags();
+ mPreferUnmetered = job.getRequiredNetwork() != null
+ && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);
- // Exposing a preferredNetworkRequest API requires that we make sure that the preferred
- // NetworkRequest is a subset of the required NetworkRequest. We currently don't have the
- // code to ensure that, so disable this part for now.
- // TODO(236261941): look into enabling flexible network constraint requests
- mPreferUnmetered = false;
- // && job.getRequiredNetwork() != null
- // && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);
-
+ final boolean lacksSomeFlexibleConstraints =
+ ((~requiredConstraints) & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS) != 0
+ || mPreferUnmetered;
final boolean satisfiesMinWindowException =
(latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
>= MIN_WINDOW_FOR_FLEXIBILITY_MS;
// The first time a job is rescheduled it will not be subject to flexible constraints.
// Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
- if (mPreferredConstraints != 0 && !isRequestedExpeditedJob() && !job.isUserInitiated()
+ if (!isRequestedExpeditedJob() && !job.isUserInitiated()
&& satisfiesMinWindowException
- && (numFailures + numSystemStops) != 1) {
- mNumRequiredFlexibleConstraints = Integer.bitCount(mPreferredConstraints);
+ && (numFailures + numSystemStops) != 1
+ && lacksSomeFlexibleConstraints) {
+ mNumRequiredFlexibleConstraints =
+ NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
requiredConstraints |= CONSTRAINT_FLEXIBLE;
} else {
mNumRequiredFlexibleConstraints = 0;
@@ -1393,10 +1394,6 @@
mInternalFlags = mInternalFlags & ~flags;
}
- int getPreferredConstraintFlags() {
- return mPreferredConstraints;
- }
-
public int getSatisfiedConstraintFlags() {
return satisfiedConstraints;
}
@@ -2778,9 +2775,6 @@
pw.print("Required constraints:");
dumpConstraints(pw, requiredConstraints);
pw.println();
- pw.print("Preferred constraints:");
- dumpConstraints(pw, mPreferredConstraints);
- pw.println();
pw.print("Dynamic constraints:");
dumpConstraints(pw, mDynamicConstraints);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
index c458cae..ba0e633 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -16,10 +16,13 @@
package com.android.server.job.controllers.idle;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -73,7 +76,8 @@
}
@Override
- public void startTracking(Context context, IdlenessListener listener) {
+ public void startTracking(Context context, JobSchedulerService service,
+ IdlenessListener listener) {
mIdleListener = listener;
IntentFilter filter = new IntentFilter();
@@ -94,6 +98,15 @@
context.registerReceiver(this, filter, null, AppSchedulingModuleThread.getHandler());
}
+ /** Process the specified constant and update internal constants if relevant. */
+ public void processConstant(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ }
+
+ @Override
+ public void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow) {
+ }
+
@Override
public void dump(PrintWriter pw) {
pw.print(" mIdle: "); pw.println(mIdle);
@@ -119,6 +132,10 @@
}
@Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ }
+
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
logIfDebug("Received action: " + action);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
index c943e73..7dd3d13 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -17,9 +17,12 @@
package com.android.server.job.controllers.idle;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
@@ -27,10 +30,13 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PowerManager;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.AppSchedulingModuleThread;
import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
@@ -45,17 +51,38 @@
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
+ /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+ private static final String IC_DIT_CONSTANT_PREFIX = "ic_dit_";
+ @VisibleForTesting
+ static final String KEY_INACTIVITY_IDLE_THRESHOLD_MS =
+ IC_DIT_CONSTANT_PREFIX + "inactivity_idle_threshold_ms";
+ @VisibleForTesting
+ static final String KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS =
+ IC_DIT_CONSTANT_PREFIX + "inactivity_idle_stable_power_threshold_ms";
+ private static final String KEY_IDLE_WINDOW_SLOP_MS =
+ IC_DIT_CONSTANT_PREFIX + "idle_window_slop_ms";
+
private AlarmManager mAlarm;
private PowerManager mPowerManager;
// After construction, mutations of idle/screen-on/projection states will only happen
// on the JobScheduler thread, either in onReceive(), in an alarm callback, or in on.*Changed.
private long mInactivityIdleThreshold;
+ private long mInactivityStablePowerIdleThreshold;
private long mIdleWindowSlop;
+ /** Stable power is defined as "charging + battery not low." */
+ private boolean mIsStablePower;
private boolean mIdle;
private boolean mScreenOn;
private boolean mDockIdle;
private boolean mProjectionActive;
+
+ /**
+ * Time (in the elapsed realtime timebase) when the idleness check was scheduled. This should
+ * be a negative value if the device is not in state to be considered idle.
+ */
+ private long mIdlenessCheckScheduledElapsed = -1;
+
private IdlenessListener mIdleListener;
private final UiModeManager.OnProjectionStateChangedListener mOnProjectionStateChangedListener =
this::onProjectionStateChanged;
@@ -76,10 +103,14 @@
}
@Override
- public void startTracking(Context context, IdlenessListener listener) {
+ public void startTracking(Context context, JobSchedulerService service,
+ IdlenessListener listener) {
mIdleListener = listener;
mInactivityIdleThreshold = context.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+ mInactivityStablePowerIdleThreshold = context.getResources().getInteger(
+ com.android.internal.R.integer
+ .config_jobSchedulerInactivityIdleThresholdOnStablePower);
mIdleWindowSlop = context.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -107,6 +138,46 @@
context.getSystemService(UiModeManager.class).addOnProjectionStateChangedListener(
UiModeManager.PROJECTION_TYPE_ALL, AppSchedulingModuleThread.getExecutor(),
mOnProjectionStateChangedListener);
+
+ mIsStablePower = service.isBatteryCharging() && service.isBatteryNotLow();
+ }
+
+ /** Process the specified constant and update internal constants if relevant. */
+ public void processConstant(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ switch (key) {
+ case KEY_INACTIVITY_IDLE_THRESHOLD_MS:
+ // Keep the threshold in the range [1 minute, 4 hours].
+ mInactivityIdleThreshold = Math.max(MINUTE_IN_MILLIS, Math.min(4 * HOUR_IN_MILLIS,
+ properties.getLong(key, mInactivityIdleThreshold)));
+ // Don't bother updating any pending alarms. Just wait until the next time we
+ // attempt to check for idle state to use the new value.
+ break;
+ case KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS:
+ // Keep the threshold in the range [1 minute, 4 hours].
+ mInactivityStablePowerIdleThreshold = Math.max(MINUTE_IN_MILLIS,
+ Math.min(4 * HOUR_IN_MILLIS,
+ properties.getLong(key, mInactivityStablePowerIdleThreshold)));
+ // Don't bother updating any pending alarms. Just wait until the next time we
+ // attempt to check for idle state to use the new value.
+ break;
+ case KEY_IDLE_WINDOW_SLOP_MS:
+ // Keep the slop in the range [1 minute, 15 minutes].
+ mIdleWindowSlop = Math.max(MINUTE_IN_MILLIS, Math.min(15 * MINUTE_IN_MILLIS,
+ properties.getLong(key, mIdleWindowSlop)));
+ // Don't bother updating any pending alarms. Just wait until the next time we
+ // attempt to check for idle state to use the new value.
+ break;
+ }
+ }
+
+ @Override
+ public void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow) {
+ final boolean isStablePower = isCharging && isBatteryNotLow;
+ if (mIsStablePower != isStablePower) {
+ mIsStablePower = isStablePower;
+ maybeScheduleIdlenessCheck("stable power changed");
+ }
}
private void onProjectionStateChanged(@UiModeManager.ProjectionType int activeProjectionTypes,
@@ -134,8 +205,10 @@
public void dump(PrintWriter pw) {
pw.print(" mIdle: "); pw.println(mIdle);
pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ pw.print(" mIsStablePower: "); pw.println(mIsStablePower);
pw.print(" mDockIdle: "); pw.println(mDockIdle);
pw.print(" mProjectionActive: "); pw.println(mProjectionActive);
+ pw.print(" mIdlenessCheckScheduledElapsed: "); pw.println(mIdlenessCheckScheduledElapsed);
}
@Override
@@ -162,6 +235,17 @@
}
@Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ pw.println("DeviceIdlenessTracker:");
+ pw.increaseIndent();
+ pw.print(KEY_INACTIVITY_IDLE_THRESHOLD_MS, mInactivityIdleThreshold).println();
+ pw.print(KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS, mInactivityStablePowerIdleThreshold)
+ .println();
+ pw.print(KEY_IDLE_WINDOW_SLOP_MS, mIdleWindowSlop).println();
+ pw.decreaseIndent();
+ }
+
+ @Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) {
@@ -220,9 +304,24 @@
private void maybeScheduleIdlenessCheck(String reason) {
if ((!mScreenOn || mDockIdle) && !mProjectionActive) {
final long nowElapsed = sElapsedRealtimeClock.millis();
- final long when = nowElapsed + mInactivityIdleThreshold;
+ final long inactivityThresholdMs = mIsStablePower
+ ? mInactivityStablePowerIdleThreshold : mInactivityIdleThreshold;
+ if (mIdlenessCheckScheduledElapsed >= 0) {
+ if (mIdlenessCheckScheduledElapsed + inactivityThresholdMs <= nowElapsed) {
+ if (DEBUG) {
+ Slog.v(TAG, "Previous idle check @ " + mIdlenessCheckScheduledElapsed
+ + " allows device to be idle now");
+ }
+ handleIdleTrigger();
+ return;
+ }
+ } else {
+ mIdlenessCheckScheduledElapsed = nowElapsed;
+ }
+ final long when = mIdlenessCheckScheduledElapsed + inactivityThresholdMs;
if (DEBUG) {
- Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed + " when=" + when);
+ Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed
+ + " checkElapsed=" + mIdlenessCheckScheduledElapsed + " when=" + when);
}
mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
when, mIdleWindowSlop, "JS idleness",
@@ -232,6 +331,7 @@
private void cancelIdlenessCheck() {
mAlarm.cancel(mIdleAlarmListener);
+ mIdlenessCheckScheduledElapsed = -1;
}
private void handleIdleTrigger() {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
index cdab7e5..92ad4df 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java
@@ -16,9 +16,14 @@
package com.android.server.job.controllers.idle;
+import android.annotation.NonNull;
import android.content.Context;
+import android.provider.DeviceConfig;
+import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
+import com.android.server.job.JobSchedulerService;
+
import java.io.PrintWriter;
public interface IdlenessTracker {
@@ -29,7 +34,7 @@
* non-interacting state. When the idle state changes thereafter, the given
* listener must be called to report the new state.
*/
- void startTracking(Context context, IdlenessListener listener);
+ void startTracking(Context context, JobSchedulerService service, IdlenessListener listener);
/**
* Report whether the device is currently considered "idle" for purposes of
@@ -40,6 +45,12 @@
*/
boolean isIdle();
+ /** Process the specified constant and update internal constants if relevant. */
+ void processConstant(@NonNull DeviceConfig.Properties properties, @NonNull String key);
+
+ /** Called when the battery state changes. */
+ void onBatteryStateChanged(boolean isCharging, boolean isBatteryNotLow);
+
/**
* Dump useful information about tracked idleness-related state in plaintext.
*/
@@ -49,4 +60,7 @@
* Dump useful information about tracked idleness-related state to proto.
*/
void dump(ProtoOutputStream proto, long fieldId);
+
+ /** Dump any internal constants the tracker may have. */
+ void dumpConstants(IndentingPrintWriter pw);
}
diff --git a/api/Android.bp b/api/Android.bp
index d11ea7b..de4435e 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -251,6 +251,152 @@
cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)",
}
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
+// Defaults for all stubs that include the non-updatable framework. These defaults do not include
+// module symbols, so will not compile correctly on their own. Users must add module APIs to the
+// classpath (or sources) somehow.
+stubs_defaults {
+ name: "android-non-updatable-stubs-defaults",
+ srcs: [":android-non-updatable-stub-sources"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ arg_files: [":frameworks-base-core-AndroidManifest.xml"],
+ aidl: {
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/base/media/aidl",
+ "frameworks/base/telephony/java",
+ "frameworks/native/libs/permission/aidl",
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ "hardware/interfaces/biometrics/common/aidl",
+ "hardware/interfaces/biometrics/fingerprint/aidl",
+ "hardware/interfaces/graphics/common/aidl",
+ "hardware/interfaces/keymaster/aidl",
+ "system/hardware/interfaces/media/aidl",
+ ],
+ },
+ // These are libs from framework-internal-utils that are required (i.e. being referenced)
+ // from framework-non-updatable-sources. Add more here when there's a need.
+ // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+ // dependencies gets bigger.
+ libs: [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+ ],
+ flags: [
+ "--api-lint-ignore-prefix android.icu.",
+ "--api-lint-ignore-prefix java.",
+ "--api-lint-ignore-prefix junit.",
+ "--api-lint-ignore-prefix org.",
+ "--error NoSettingsProvider",
+ "--error UnhiddenSystemApi",
+ "--error UnflaggedApi",
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
+ // Disable CallbackInterface, as Java 8 default interface methods avoid the extensibility
+ // issue interfaces had previously.
+ "--hide CallbackInterface",
+ // Disable HiddenSuperclass, as Metalava handles this fine (it should be hidden by default)
+ "--hide HiddenSuperclass",
+ "--hide-package android.audio.policy.configuration.V7_0",
+ "--hide-package com.android.server",
+ "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//frameworks/base/api"],
+ visibility: ["//frameworks/base/api"],
+}
+
+// Defaults with module APIs in the classpath (mostly from prebuilts).
+// Suitable for compiling android-non-updatable.
+stubs_defaults {
+ name: "module-classpath-stubs-defaults",
+ aidl: {
+ include_dirs: [
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ ],
+ },
+ libs: [
+ "art.module.public.api",
+ "sdk_module-lib_current_framework-tethering",
+ "sdk_module-lib_current_framework-connectivity-t",
+ "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
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
+}
+
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+ name: "framework-non-updatable-unbundled-defaults",
+ defaults: [
+ "framework-non-updatable-lint-defaults",
+ "non-updatable-framework-module-defaults",
+ ],
+ public: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ system: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ module_lib: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ test: {
+ libs: ["android_test_frameworks_core_stubs_current"],
+ },
+ sdk_version: "core_platform",
+ stub_only_libs: ["framework-protos"],
+ impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
+ impl_library_visibility: ["//frameworks/base"],
+ defaults_visibility: ["//frameworks/base/location"],
+ plugins: ["error_prone_android_framework"],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+ // Include manual annotations in API txt files
+ merge_annotations_dirs: ["metalava-manual"],
+}
+
build = [
"ApiDocs.bp",
"StubLibraries.bp",
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 5744bdf..bcfb68f 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -30,6 +30,18 @@
":android-test-mock-sources",
":android-test-runner-sources",
],
+ flags: [
+ // These errors are suppressed in the doc stubs as it isn't easy to suppress them.
+ // They remain unsuppressed/active in the "main" stubs build (the jar stubs).
+ // These can be removed when either a) all the issues have been fixed or
+ // b) these reporting of these issues is gated behind api lint being enabled in metalava.
+ "--hide BroadcastBehavior",
+ "--hide DeprecationMismatch",
+ "--hide MissingPermission",
+ "--hide RequiresPermission",
+ "--hide SdkConstant",
+ "--hide Todo",
+ ],
create_doc_stubs: true,
write_sdk_values: true,
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 805dfc6..1b0311d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6727,7 +6727,7 @@
field protected android.app.Notification.Builder mBuilder;
}
- public static final class Notification.TvExtender implements android.app.Notification.Extender {
+ @FlaggedApi("android.app.api_tvextender") public static final class Notification.TvExtender implements android.app.Notification.Extender {
ctor public Notification.TvExtender();
ctor public Notification.TvExtender(@NonNull android.app.Notification);
method @NonNull public android.app.Notification.Builder extend(@NonNull android.app.Notification.Builder);
@@ -9321,6 +9321,21 @@
field public static final int USER_INTERACTION = 7; // 0x7
}
+ @FlaggedApi("android.app.usage.filter_based_event_query_api") public final class UsageEventsQuery implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getBeginTimeMillis();
+ method public long getEndTimeMillis();
+ method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.UsageEventsQuery> CREATOR;
+ }
+
+ public static final class UsageEventsQuery.Builder {
+ ctor public UsageEventsQuery.Builder(long, long);
+ method @NonNull public android.app.usage.UsageEventsQuery.Builder addEventTypes(@NonNull int...);
+ method @NonNull public android.app.usage.UsageEventsQuery build();
+ }
+
public final class UsageStats implements android.os.Parcelable {
ctor public UsageStats(android.app.usage.UsageStats);
method public void add(android.app.usage.UsageStats);
@@ -9345,6 +9360,7 @@
method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long);
method public android.app.usage.UsageEvents queryEvents(long, long);
+ method @FlaggedApi("android.app.usage.filter_based_event_query_api") @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.UsageEvents queryEvents(@NonNull android.app.usage.UsageEventsQuery);
method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
field public static final int INTERVAL_BEST = 4; // 0x4
@@ -11989,22 +12005,22 @@
method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
}
- @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivity {
- ctor public ArchivedActivity(@NonNull CharSequence, @NonNull android.content.ComponentName);
+ @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivityInfo {
+ ctor public ArchivedActivityInfo(@NonNull CharSequence, @NonNull android.content.ComponentName);
method @NonNull public android.content.ComponentName getComponentName();
method @Nullable public android.graphics.drawable.Drawable getIcon();
method @NonNull public CharSequence getLabel();
method @Nullable public android.graphics.drawable.Drawable getMonochromeIcon();
- method @NonNull public android.content.pm.ArchivedActivity setComponentName(@NonNull android.content.ComponentName);
- method @NonNull public android.content.pm.ArchivedActivity setIcon(@NonNull android.graphics.drawable.Drawable);
- method @NonNull public android.content.pm.ArchivedActivity setLabel(@NonNull CharSequence);
- method @NonNull public android.content.pm.ArchivedActivity setMonochromeIcon(@NonNull android.graphics.drawable.Drawable);
+ method @NonNull public android.content.pm.ArchivedActivityInfo setComponentName(@NonNull android.content.ComponentName);
+ method @NonNull public android.content.pm.ArchivedActivityInfo setIcon(@NonNull android.graphics.drawable.Drawable);
+ method @NonNull public android.content.pm.ArchivedActivityInfo setLabel(@NonNull CharSequence);
+ method @NonNull public android.content.pm.ArchivedActivityInfo setMonochromeIcon(@NonNull android.graphics.drawable.Drawable);
}
- @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackage {
- ctor public ArchivedPackage(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivity>);
+ @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackageInfo {
+ ctor public ArchivedPackageInfo(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivityInfo>);
method @Nullable public String getDefaultToDeviceProtectedStorage();
- method @NonNull public java.util.List<android.content.pm.ArchivedActivity> getLauncherActivities();
+ method @NonNull public java.util.List<android.content.pm.ArchivedActivityInfo> getLauncherActivities();
method @NonNull public String getPackageName();
method @Nullable public String getRequestLegacyExternalStorage();
method @NonNull public android.content.pm.SigningInfo getSigningInfo();
@@ -12012,15 +12028,15 @@
method @Nullable public String getUserDataFragile();
method public int getVersionCode();
method public int getVersionCodeMajor();
- method @NonNull public android.content.pm.ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String);
- method @NonNull public android.content.pm.ArchivedPackage setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivity>);
- method @NonNull public android.content.pm.ArchivedPackage setPackageName(@NonNull String);
- method @NonNull public android.content.pm.ArchivedPackage setRequestLegacyExternalStorage(@NonNull String);
- method @NonNull public android.content.pm.ArchivedPackage setSigningInfo(@NonNull android.content.pm.SigningInfo);
- method @NonNull public android.content.pm.ArchivedPackage setTargetSdkVersion(int);
- method @NonNull public android.content.pm.ArchivedPackage setUserDataFragile(@NonNull String);
- method @NonNull public android.content.pm.ArchivedPackage setVersionCode(int);
- method @NonNull public android.content.pm.ArchivedPackage setVersionCodeMajor(int);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivityInfo>);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setPackageName(@NonNull String);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setSigningInfo(@NonNull android.content.pm.SigningInfo);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setTargetSdkVersion(int);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setUserDataFragile(@NonNull String);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCode(int);
+ method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCodeMajor(int);
}
public final class Attribution implements android.os.Parcelable {
@@ -12355,7 +12371,7 @@
method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender);
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackage, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender);
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackageInfo, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender);
method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
@@ -12637,7 +12653,7 @@
method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackage getArchivedPackage(@NonNull String);
+ method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackageInfo getArchivedPackage(@NonNull String);
method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
@@ -18526,11 +18542,13 @@
public class BiometricManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+ method @FlaggedApi("android.hardware.biometrics.last_authentication_time") @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
method @NonNull @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public android.hardware.biometrics.BiometricManager.Strings getStrings(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
}
@@ -18576,6 +18594,7 @@
field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
}
public abstract static class BiometricPrompt.AuthenticationCallback {
@@ -23842,14 +23861,14 @@
field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_EARC = 29; // 0x1d
field public static final int TYPE_HEARING_AID = 23; // 0x17
field public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003; // 0x3eb
- field public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0
- field public static final int TYPE_REMOTE_COMPUTER = 1006; // 0x3ee
- field public static final int TYPE_REMOTE_GAME_CONSOLE = 1007; // 0x3ef
- field public static final int TYPE_REMOTE_SMARTPHONE = 1010; // 0x3f2
- field public static final int TYPE_REMOTE_SMARTWATCH = 1009; // 0x3f1
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_COMPUTER = 1006; // 0x3ee
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_GAME_CONSOLE = 1007; // 0x3ef
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_SMARTPHONE = 1010; // 0x3f2
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_SMARTWATCH = 1009; // 0x3f1
field public static final int TYPE_REMOTE_SPEAKER = 1002; // 0x3ea
- field public static final int TYPE_REMOTE_TABLET = 1004; // 0x3ec
- field public static final int TYPE_REMOTE_TABLET_DOCKED = 1005; // 0x3ed
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_TABLET = 1004; // 0x3ec
+ field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_TABLET_DOCKED = 1005; // 0x3ed
field public static final int TYPE_REMOTE_TV = 1001; // 0x3e9
field public static final int TYPE_UNKNOWN = 0; // 0x0
field public static final int TYPE_USB_ACCESSORY = 12; // 0xc
@@ -25847,15 +25866,15 @@
method public abstract void onDisconnect(android.media.midi.MidiReceiver);
}
- @FlaggedApi("com.android.media.midi.flags.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
+ @FlaggedApi("android.media.midi.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
ctor public MidiUmpDeviceService();
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onClose();
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
- method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
- field @FlaggedApi("com.android.media.midi.flags.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
+ method @FlaggedApi("android.media.midi.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
+ method @FlaggedApi("android.media.midi.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
+ method @FlaggedApi("android.media.midi.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @FlaggedApi("android.media.midi.virtual_ump") public void onClose();
+ method @FlaggedApi("android.media.midi.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
+ method @FlaggedApi("android.media.midi.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
+ field @FlaggedApi("android.media.midi.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
}
}
@@ -28503,12 +28522,12 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
- method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder enableSafeMode(boolean);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeGatewayOption(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setSafeModeEnabled(boolean);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
}
@@ -42599,6 +42618,7 @@
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+ field @FlaggedApi("com.android.server.telecom.flags.add_call_uri_for_missed_calls") public static final String EXTRA_CALL_LOG_URI = "android.telecom.extra.CALL_LOG_URI";
field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
@@ -45537,6 +45557,7 @@
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2; // 0x2
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9; // 0x9
field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.slicing_additional_error_codes") public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16; // 0x10
field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
field public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3; // 0x3
field public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4; // 0x4
@@ -49808,6 +49829,7 @@
method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
method public default void setChildBoundingInsets(@NonNull android.graphics.Rect);
method public default void setTouchableRegion(@Nullable android.graphics.Region);
+ method @FlaggedApi("com.android.window.flags.transfer_gesture_to_embedded") public default boolean transferHostTouchGestureToEmbedded(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
}
@UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index afb10f5..1e6aa49 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,4 +1,1168 @@
// Baseline format: 1.0
+BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
+ Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
+ Field 'ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED:
+ Field 'ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_MANAGED_PROFILE_PROVISIONED:
+ Field 'ACTION_MANAGED_PROFILE_PROVISIONED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_AIRPLANE_MODE_CHANGED:
+ Field 'ACTION_AIRPLANE_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED:
+ Field 'ACTION_APPLICATION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED:
+ Field 'ACTION_APPLICATION_RESTRICTIONS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_CHANGED:
+ Field 'ACTION_BATTERY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LOW:
+ Field 'ACTION_BATTERY_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_OKAY:
+ Field 'ACTION_BATTERY_OKAY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CAMERA_BUTTON:
+ Field 'ACTION_CAMERA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS:
+ Field 'ACTION_CLOSE_SYSTEM_DIALOGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CONFIGURATION_CHANGED:
+ Field 'ACTION_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DATE_CHANGED:
+ Field 'ACTION_DATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_LOW:
+ Field 'ACTION_DEVICE_STORAGE_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_OK:
+ Field 'ACTION_DEVICE_STORAGE_OK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOCK_EVENT:
+ Field 'ACTION_DOCK_EVENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STARTED:
+ Field 'ACTION_DREAMING_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STOPPED:
+ Field 'ACTION_DREAMING_STOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_CONNECTED:
+ Field 'ACTION_GTALK_SERVICE_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_DISCONNECTED:
+ Field 'ACTION_GTALK_SERVICE_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INPUT_METHOD_CHANGED:
+ Field 'ACTION_INPUT_METHOD_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCALE_CHANGED:
+ Field 'ACTION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED:
+ Field 'ACTION_LOCKED_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MANAGE_PACKAGE_STORAGE:
+ Field 'ACTION_MANAGE_PACKAGE_STORAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BAD_REMOVAL:
+ Field 'ACTION_MEDIA_BAD_REMOVAL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BUTTON:
+ Field 'ACTION_MEDIA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_CHECKING:
+ Field 'ACTION_MEDIA_CHECKING' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_EJECT:
+ Field 'ACTION_MEDIA_EJECT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_MOUNTED:
+ Field 'ACTION_MEDIA_MOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_NOFS:
+ Field 'ACTION_MEDIA_NOFS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_REMOVED:
+ Field 'ACTION_MEDIA_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_FINISHED:
+ Field 'ACTION_MEDIA_SCANNER_FINISHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_SCAN_FILE:
+ Field 'ACTION_MEDIA_SCANNER_SCAN_FILE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_STARTED:
+ Field 'ACTION_MEDIA_SCANNER_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SHARED:
+ Field 'ACTION_MEDIA_SHARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTABLE:
+ Field 'ACTION_MEDIA_UNMOUNTABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTED:
+ Field 'ACTION_MEDIA_UNMOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_REPLACED:
+ Field 'ACTION_MY_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_SUSPENDED:
+ Field 'ACTION_MY_PACKAGE_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_UNSUSPENDED:
+ Field 'ACTION_MY_PACKAGE_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_NEW_OUTGOING_CALL:
+ Field 'ACTION_NEW_OUTGOING_CALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_SUSPENDED:
+ Field 'ACTION_PACKAGES_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_UNSUSPENDED:
+ Field 'ACTION_PACKAGES_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_ADDED:
+ Field 'ACTION_PACKAGE_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_CHANGED:
+ Field 'ACTION_PACKAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_DATA_CLEARED:
+ Field 'ACTION_PACKAGE_DATA_CLEARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH:
+ Field 'ACTION_PACKAGE_FIRST_LAUNCH' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FULLY_REMOVED:
+ Field 'ACTION_PACKAGE_FULLY_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_INSTALL:
+ Field 'ACTION_PACKAGE_INSTALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REMOVED:
+ Field 'ACTION_PACKAGE_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REPLACED:
+ Field 'ACTION_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_RESTARTED:
+ Field 'ACTION_PACKAGE_RESTARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSTOPPED:
+ Field 'ACTION_PACKAGE_UNSTOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_VERIFIED:
+ Field 'ACTION_PACKAGE_VERIFIED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_CONNECTED:
+ Field 'ACTION_POWER_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_DISCONNECTED:
+ Field 'ACTION_POWER_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PROVIDER_CHANGED:
+ Field 'ACTION_PROVIDER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_REBOOT:
+ Field 'ACTION_REBOOT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_OFF:
+ Field 'ACTION_SCREEN_OFF' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_ON:
+ Field 'ACTION_SCREEN_ON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHUTDOWN:
+ Field 'ACTION_SHUTDOWN' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIMEZONE_CHANGED:
+ Field 'ACTION_TIMEZONE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_CHANGED:
+ Field 'ACTION_TIME_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_TICK:
+ Field 'ACTION_TIME_TICK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UID_REMOVED:
+ Field 'ACTION_UID_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_CONNECTED:
+ Field 'ACTION_UMS_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_DISCONNECTED:
+ Field 'ACTION_UMS_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_PRESENT:
+ Field 'ACTION_USER_PRESENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_UNLOCKED:
+ Field 'ACTION_USER_UNLOCKED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_WALLPAPER_CHANGED:
+ Field 'ACTION_WALLPAPER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_COMMITTED:
+ Field 'ACTION_SESSION_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_UPDATED:
+ Field 'ACTION_SESSION_UPDATED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_PICTURE:
+ Field 'ACTION_NEW_PICTURE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_VIDEO:
+ Field 'ACTION_NEW_VIDEO' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS:
+ Field 'ACTION_QUERY_KEYBOARD_LAYOUTS' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_DETACHED:
+ Field 'ACTION_USB_ACCESSORY_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_DEVICE_DETACHED:
+ Field 'ACTION_USB_DEVICE_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HDMI_AUDIO_PLUG:
+ Field 'ACTION_HDMI_AUDIO_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_MICROPHONE_MUTE_CHANGED:
+ Field 'ACTION_MICROPHONE_MUTE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_SPEAKERPHONE_STATE_CHANGED:
+ Field 'ACTION_SPEAKERPHONE_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_INITIALIZE_PROGRAMS:
+ Field 'ACTION_INITIALIZE_PROGRAMS' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+ Field 'ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION:
+ Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED:
+ Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED:
+ Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED:
+ Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED:
+ Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER:
+ Field 'ACTION_EVENT_REMINDER' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.ContactsContract.SimContacts#ACTION_SIM_ACCOUNTS_CHANGED:
+ Field 'ACTION_SIM_ACCOUNTS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#DATA_SMS_RECEIVED_ACTION:
+ Field 'DATA_SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION:
+ Field 'SECRET_CODE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SIM_FULL_ACTION:
+ Field 'SIM_FULL_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION:
+ Field 'SMS_CB_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION:
+ Field 'SMS_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION:
+ Field 'SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_REJECTED_ACTION:
+ Field 'SMS_REJECTED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION:
+ Field 'SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION:
+ Field 'WAP_PUSH_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_RECEIVED_ACTION:
+ Field 'WAP_PUSH_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEYCHAIN_CHANGED:
+ Field 'ACTION_KEYCHAIN_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED:
+ Field 'ACTION_KEY_ACCESS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_STORAGE_CHANGED:
+ Field 'ACTION_STORAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_TRUST_STORE_CHANGED:
+ Field 'ACTION_TRUST_STORE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech#ACTION_TTS_QUEUE_PROCESSING_COMPLETED:
+ Field 'ACTION_TTS_QUEUE_PROCESSING_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech.Engine#ACTION_TTS_DATA_INSTALLED:
+ Field 'ACTION_TTS_DATA_INSTALLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_REFRESH_SUBSCRIPTION_PLANS:
+ Field 'ACTION_REFRESH_SUBSCRIPTION_PLANS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+ Field 'ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_PCO_VALUE:
+ Field 'ACTION_CARRIER_SIGNAL_PCO_VALUE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REDIRECTED:
+ Field 'ACTION_CARRIER_SIGNAL_REDIRECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+ Field 'ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_RESET:
+ Field 'ACTION_CARRIER_SIGNAL_RESET' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SECRET_CODE:
+ Field 'ACTION_SECRET_CODE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
+ Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
+
+
+DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
+ Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
+ Method android.app.Activity.enterPictureInPictureMode(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#startAllocCounting():
+ Method android.app.Instrumentation.startAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#stopAllocCounting():
+ Method android.app.Instrumentation.stopAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#bigContentView:
+ Field Notification.bigContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#contentView:
+ Field Notification.contentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#headsUpContentView:
+ Field Notification.headsUpContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#tickerView:
+ Field Notification.tickerView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.Builder#Builder(int, CharSequence, android.app.PendingIntent):
+ Constructor android.app.Notification.Action.Builder.Builder(int, CharSequence, android.app.PendingIntent): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getCancelLabel():
+ Method android.app.Notification.Action.WearableExtender.getCancelLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getConfirmLabel():
+ Method android.app.Notification.Action.WearableExtender.getConfirmLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getInProgressLabel():
+ Method android.app.Notification.Action.WearableExtender.getInProgressLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setCancelLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setCancelLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setConfirmLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setConfirmLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setInProgressLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setInProgressLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setContent(android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setContent(android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setTicker(CharSequence, android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setTicker(CharSequence, android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIcon():
+ Method android.app.Notification.WearableExtender.getContentIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIconGravity():
+ Method android.app.Notification.WearableExtender.getContentIconGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomContentHeight():
+ Method android.app.Notification.WearableExtender.getCustomContentHeight(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomSizePreset():
+ Method android.app.Notification.WearableExtender.getCustomSizePreset(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getGravity():
+ Method android.app.Notification.WearableExtender.getGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintAvoidBackgroundClipping():
+ Method android.app.Notification.WearableExtender.getHintAvoidBackgroundClipping(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintHideIcon():
+ Method android.app.Notification.WearableExtender.getHintHideIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintScreenTimeout():
+ Method android.app.Notification.WearableExtender.getHintScreenTimeout(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintShowBackgroundOnly():
+ Method android.app.Notification.WearableExtender.getHintShowBackgroundOnly(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIcon(int):
+ Method android.app.Notification.WearableExtender.setContentIcon(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIconGravity(int):
+ Method android.app.Notification.WearableExtender.setContentIconGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomContentHeight(int):
+ Method android.app.Notification.WearableExtender.setCustomContentHeight(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomSizePreset(int):
+ Method android.app.Notification.WearableExtender.setCustomSizePreset(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setGravity(int):
+ Method android.app.Notification.WearableExtender.setGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintAvoidBackgroundClipping(boolean):
+ Method android.app.Notification.WearableExtender.setHintAvoidBackgroundClipping(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintHideIcon(boolean):
+ Method android.app.Notification.WearableExtender.setHintHideIcon(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintScreenTimeout(int):
+ Method android.app.Notification.WearableExtender.setHintScreenTimeout(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintShowBackgroundOnly(boolean):
+ Method android.app.Notification.WearableExtender.setHintShowBackgroundOnly(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.ComposeShader#ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode):
+ Constructor android.graphics.ComposeShader.ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#A_8:
+ Field PixelFormat.A_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#LA_88:
+ Field PixelFormat.LA_88: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#L_8:
+ Field PixelFormat.L_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_4444:
+ Field PixelFormat.RGBA_4444: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_5551:
+ Field PixelFormat.RGBA_5551: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGB_332:
+ Field PixelFormat.RGB_332: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.EGL14#eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int):
+ Method android.opengl.EGL14.eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLES20#GL_STENCIL_INDEX:
+ Field GLES20.GL_STENCIL_INDEX: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLSurfaceView#surfaceRedrawNeeded(android.view.SurfaceHolder):
+ Method android.opengl.GLSurfaceView.surfaceRedrawNeeded(android.view.SurfaceHolder): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle, android.os.UserHandle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle, android.os.UserHandle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.provider.Contacts.People#markAsContacted(android.content.ContentResolver, long):
+ Method android.provider.Contacts.People.markAsContacted(android.content.ContentResolver, long): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_X:
+ Field Type.CubemapFace.POSITVE_X: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Y:
+ Field Type.CubemapFace.POSITVE_Y: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Z:
+ Field Type.CubemapFace.POSITVE_Z: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.speech.tts.TextToSpeech#areDefaultsEnforced():
+ Method android.speech.tts.TextToSpeech.areDefaultsEnforced(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.PhoneStateListener#PhoneStateListener(java.util.concurrent.Executor):
+ Constructor android.telephony.PhoneStateListener.PhoneStateListener(java.util.concurrent.Executor): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_12HOUR:
+ Field DateUtils.FORMAT_12HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_24HOUR:
+ Field DateUtils.FORMAT_24HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_AMPM:
+ Field DateUtils.FORMAT_CAP_AMPM: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON:
+ Field DateUtils.FORMAT_CAP_NOON: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_NO_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_NO_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.ViewGroup.LayoutParams#FILL_PARENT:
+ Field ViewGroup.LayoutParams.FILL_PARENT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.Window#setTitleColor(int):
+ Method android.view.Window.setTitleColor(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.accessibility.AccessibilityEvent#MAX_TEXT_LENGTH:
+ Field AccessibilityEvent.MAX_TEXT_LENGTH: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebSettings#getSaveFormData():
+ Method android.webkit.WebSettings.getSaveFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebView#shouldDelayChildPressedState():
+ Method android.webkit.WebView.shouldDelayChildPressedState(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#clearFormData():
+ Method android.webkit.WebViewDatabase.clearFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#hasFormData():
+ Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
+ Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
+RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+ Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+ Method 'hasFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getHistoricalProcessExitReasons(String, int, int):
+ Method 'getHistoricalProcessExitReasons' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getProcessesInErrorState():
+ Method 'getProcessesInErrorState' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent):
+ Method 'setAlarmClock' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExact(int, long, android.app.PendingIntent):
+ Method 'setExact' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent):
+ Method 'setExactAndAllowWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setTime(long):
+ Method 'setTime' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#isOpActive(String, int, String):
+ Method 'isOpActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingActive(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpActiveChangedListener):
+ Method 'startWatchingActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationInExternalPublicDir(String, String):
+ Method 'setDestinationInExternalPublicDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationUri(android.net.Uri):
+ Method 'setDestinationUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setNotificationVisibility(int):
+ Method 'setNotificationVisibility' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setShowRunningNotification(boolean):
+ Method 'setShowRunningNotification' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.LocaleManager#getApplicationLocales(String):
+ Method 'getApplicationLocales' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.Notification.Builder#setFullScreenIntent(android.app.PendingIntent, boolean):
+ Method 'setFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.NotificationManager#canUseFullScreenIntent():
+ Method 'canUseFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.Service#startForeground(int, android.app.Notification):
+ Method 'startForeground' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.StatusBarManager#canLaunchCaptureContentActivityForNote(android.app.Activity):
+ Method 'canLaunchCaptureContentActivityForNote' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperInfo#getSettingsSliceUri():
+ Method 'getSettingsSliceUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clear():
+ Method 'clear' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable():
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable(int):
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable():
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable(int):
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperFile(int):
+ Method 'getWallpaperFile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperInfo(int):
+ Method 'getWallpaperInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable():
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable(int):
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable():
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable(int):
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setDisplayPadding(android.graphics.Rect):
+ Method 'setDisplayPadding' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setResource(int):
+ Method 'setResource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream, android.graphics.Rect, boolean):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#suggestDesiredDimensions(int, int):
+ Method 'suggestDesiredDimensions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'addCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName):
+ Method 'addPersistentPreferredActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindDeviceAdminServiceAsUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearPackagePersistentPreferredActivities(android.content.ComponentName, String):
+ Method 'clearPackagePersistentPreferredActivities' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearResetPasswordToken(android.content.ComponentName):
+ Method 'clearResetPasswordToken' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#generateKeyPair(android.content.ComponentName, String, android.security.keystore.KeyGenParameterSpec, int):
+ Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
+ Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
+ Method 'getLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskPackages(android.content.ComponentName):
+ Method 'getLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy():
+ Method 'getNearbyAppStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy():
+ Method 'getNearbyNotificationStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getOrganizationName(android.content.ComponentName):
+ Method 'getOrganizationName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getPasswordComplexity():
+ Method 'getPasswordComplexity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getShortSupportMessage(android.content.ComponentName):
+ Method 'getShortSupportMessage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getUserControlDisabledPackages(android.content.ComponentName):
+ Method 'getUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#hasKeyPair(String):
+ Method 'hasKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, String):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, boolean):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, int):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isPackageSuspended(android.content.ComponentName, String):
+ Method 'isPackageSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isResetPasswordTokenActive(android.content.ComponentName):
+ Method 'isResetPasswordTokenActive' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#lockNow(int):
+ Method 'lockNow' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#removeCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'removeCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
+ Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
+ Method 'setLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, String[]):
+ Method 'setLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setPermittedInputMethods' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUninstallBlocked(android.content.ComponentName, String, boolean):
+ Method 'setUninstallBlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUserControlDisabledPackages(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int, CharSequence):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeDevice(int):
+ Method 'wipeDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicyChanged(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicyChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicySetResult(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicySetResult' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#dataChanged(String):
+ Method 'dataChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetwork(android.net.NetworkRequest):
+ Method 'setRequiredNetwork' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetworkType(int):
+ Method 'setRequiredNetworkType' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setUserInitiated(boolean):
+ Method 'setUserInitiated' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.job.JobParameters#isUserInitiatedJob():
+ Method 'isUserInitiatedJob' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobScheduler#canRunUserInitiatedJobs():
+ Method 'canRunUserInitiatedJobs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryExternalStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryExternalStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForPackage(java.util.UUID, String, android.os.UserHandle):
+ Method 'queryStatsForPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUid(java.util.UUID, int):
+ Method 'queryStatsForUid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#isAppInactive(String):
+ Method 'isAppInactive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryAndAggregateUsageStats(long, long):
+ Method 'queryAndAggregateUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryConfigurations(int, long, long):
+ Method 'queryConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEventStats(int, long, long):
+ Method 'queryEventStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEvents(long, long):
+ Method 'queryEvents' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryUsageStats(int, long, long):
+ Method 'queryUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle):
+ Method 'bindAppWidgetIdIfAllowed' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#startObservingDevicePresence(String):
+ Method 'startObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#stopObservingDevicePresence(String):
+ Method 'stopObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.ContentResolver#addPeriodicSync(android.accounts.Account, String, android.os.Bundle, long):
+ Method 'addPeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#cancelSync(android.content.SyncRequest):
+ Method 'cancelSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSync():
+ Method 'getCurrentSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSyncs():
+ Method 'getCurrentSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getIsSyncable(android.accounts.Account, String):
+ Method 'getIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getMasterSyncAutomatically():
+ Method 'getMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getPeriodicSyncs(android.accounts.Account, String):
+ Method 'getPeriodicSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getSyncAutomatically(android.accounts.Account, String):
+ Method 'getSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncActive(android.accounts.Account, String):
+ Method 'isSyncActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncPending(android.accounts.Account, String):
+ Method 'isSyncPending' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#removePeriodicSync(android.accounts.Account, String, android.os.Bundle):
+ Method 'removePeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setIsSyncable(android.accounts.Account, String, int):
+ Method 'setIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setMasterSyncAutomatically(boolean):
+ Method 'setMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setSyncAutomatically(android.accounts.Account, String, boolean):
+ Method 'setSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindServiceAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#clearWallpaper():
+ Method 'clearWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDir():
+ Method 'getExternalCacheDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDirs():
+ Method 'getExternalCacheDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDir(String):
+ Method 'getExternalFilesDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDirs(String):
+ Method 'getExternalFilesDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalMediaDirs():
+ Method 'getExternalMediaDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDir():
+ Method 'getObbDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDirs():
+ Method 'getObbDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+ Method 'removeStickyBroadcastAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(android.graphics.Bitmap):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(java.io.InputStream):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#canRequestInteractAcrossProfiles():
+ Method 'canRequestInteractAcrossProfiles' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#getAllPackageInstallerSessions():
+ Method 'getAllPackageInstallerSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#registerPackageInstallerSessionCallback(java.util.concurrent.Executor, android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerPackageInstallerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps.Callback#onPackagesSuspended(String[], android.os.UserHandle, android.os.Bundle):
+ Method 'onPackagesSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getAllSessions():
+ Method 'getAllSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getSessionInfo(int):
+ Method 'getSessionInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getStagedSessions():
+ Method 'getStagedSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.Session#requestUserPreapproval(android.content.pm.PackageInstaller.PreapprovalDetails, android.content.IntentSender):
+ Method 'requestUserPreapproval' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setInstallerPackageName(String):
+ Method 'setInstallerPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setPermissionState(String, int):
+ Method 'setPermissionState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean):
+ Method 'setRequestUpdateOwnership' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int):
+ Method 'setRequireUserAction' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#canRequestPackageInstalls():
+ Method 'canRequestPackageInstalls' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getSuspendedPackageAppExtras():
+ Method 'getSuspendedPackageAppExtras' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isAutoRevokeWhitelisted(String):
+ Method 'isAutoRevokeWhitelisted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isPackageSuspended():
+ Method 'isPackageSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getHighestDirectReportRateLevel():
+ Method 'getHighestDirectReportRateLevel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getMinDelay():
+ Method 'getMinDelay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraCharacteristics#getKeysNeedingPermission():
+ Method 'getKeysNeedingPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#hasPermission(android.hardware.usb.UsbDevice):
+ Method 'hasPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent):
+ Method 'requestPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioAttributes.Builder#setHapticChannelsMuted(boolean):
+ Method 'setHapticChannelsMuted' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaPlayer#setWakeMode(android.content.Context, int):
+ Method 'setWakeMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getCursor():
+ Method 'getCursor' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getValidRingtoneUri(android.content.Context):
+ Method 'getValidRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.audiofx.HapticGenerator#setEnabled(boolean):
+ Method 'setEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjection#createVirtualDisplay(String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler):
+ Method 'createVirtualDisplay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjectionManager#getMediaProjection(int, android.content.Intent):
+ Method 'getMediaProjection' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnMediaKeyEventSessionChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener):
+ Method 'addOnMediaKeyEventSessionChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessions(android.content.ComponentName):
+ Method 'getActiveSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSession():
+ Method 'getMediaKeyEventSession' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSessionPackageName():
+ Method 'getMediaKeyEventSessionPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo):
+ Method 'isTrustedForMediaControl' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean):
+ Method 'setSpeakerMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#startAudio():
+ Method 'startAudio' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity):
+ Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]):
+ Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String):
+ Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String):
+ Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String):
+ Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]):
+ Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]):
+ Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int):
+ Method 'decrement' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int):
+ Method 'increment' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int):
+ Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#restore(int):
+ Method 'restore' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transfer(int):
+ Method 'transfer' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]):
+ Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int):
+ Method 'readPages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]):
+ Method 'writePage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#getNdefMessage():
+ Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#isWritable():
+ Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#makeReadOnly():
+ Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage):
+ Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage):
+ Method 'format' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage):
+ Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#close():
+ Method 'close' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#connect():
+ Method 'connect' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#cancelBugreport():
+ Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Build#getSerial():
+ Method 'getSerial' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Debug#dumpService(String, java.io.FileDescriptor, String[]):
+ Method 'dumpService' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.DropBoxManager#getNextEntry(String, long):
+ Method 'getNextEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Environment#getExternalStorageDirectory():
+ Method 'getExternalStorageDirectory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager():
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager(java.io.File):
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#newWakeLock(int, String):
+ Method 'newWakeLock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#reboot(String):
+ Method 'reboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.RecoverySystem#rebootWipeUserData(android.content.Context):
+ Method 'rebootWipeUserData' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.StrictMode.VmPolicy.Builder#detectFileUriExposure():
+ Method 'detectFileUriExposure' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserRestrictions(android.os.UserHandle):
+ Method 'getUserRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlocked(android.os.UserHandle):
+ Method 'isUserUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle):
+ Method 'requestQuietModeEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#setUserRestriction(String, boolean):
+ Method 'setUserRestriction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshot(int):
+ Method 'takeUidSnapshot' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshots(int[]):
+ Method 'takeUidSnapshots' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getManageSpaceActivityIntent(String, int):
+ Method 'getManageSpaceActivityIntent' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.storage.StorageVolume#createAccessIntent(String):
+ Method 'createAccessIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.provider.Settings#canDrawOverlays(android.content.Context):
+ Method 'canDrawOverlays' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.provider.Settings.System#canWrite(android.content.Context):
+ Method 'canWrite' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.security.KeyChain#removeCredentialManagementApp(android.content.Context):
+ Method 'removeCredentialManagementApp' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginCreateCredentialResponse.Builder#setRemoteCreateEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCreateEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginGetCredentialResponse.Builder#setRemoteCredentialEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCredentialEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.CallingAppInfo#getOrigin():
+ Method 'getOrigin' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactDisplayName():
+ Method 'getContactDisplayName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactPhotoUri():
+ Method 'getContactPhotoUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle):
+ Method 'acceptHandover' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingCall' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingConference(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingConference' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getLine1Number(android.telecom.PhoneAccountHandle):
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getOwnSelfManagedPhoneAccounts():
+ Method 'getOwnSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getPhoneAccount(android.telecom.PhoneAccountHandle):
+ Method 'getPhoneAccount' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getSelfManagedPhoneAccounts():
+ Method 'getSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#hasManageOngoingCallsPermission():
+ Method 'hasManageOngoingCallsPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#placeCall(android.net.Uri, android.os.Bundle):
+ Method 'placeCall' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#showInCallScreen(boolean):
+ Method 'showInCallScreen' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#silenceRinger():
+ Method 'silenceRinger' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig():
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig(java.lang.String...):
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigByComponentForSubId(String, int):
+ Method 'getConfigByComponentForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int, java.lang.String...):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#notifyConfigChangedForSubId(int):
+ Method 'notifyConfigChangedForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CellLocation#requestLocationUpdate():
+ Method 'requestLocationUpdate' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.NetworkRegistrationInfo#getCellIdentity():
+ Method 'getCellIdentity' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallStateChanged(int, String):
+ Method 'onCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onDisplayInfoChanged(android.telephony.TelephonyDisplayInfo):
+ Method 'onDisplayInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaNetworkId():
+ Method 'getCdmaNetworkId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaSystemId():
+ Method 'getCdmaSystemId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaLong():
+ Method 'getOperatorAlphaLong' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaShort():
+ Method 'getOperatorAlphaShort' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorNumeric():
+ Method 'getOperatorNumeric' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#getSmscAddress():
+ Method 'getSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#injectSmsPdu(byte[], String, android.app.PendingIntent):
+ Method 'injectSmsPdu' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendDataMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#setSmscAddress(String):
+ Method 'setSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#addSubscriptionsIntoGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'addSubscriptionsIntoGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#createSubscriptionGroup(java.util.List<java.lang.Integer>):
+ Method 'createSubscriptionGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfo(int):
+ Method 'getActiveSubscriptionInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoForSimSlotIndex(int):
+ Method 'getActiveSubscriptionInfoForSimSlotIndex' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoList():
+ Method 'getActiveSubscriptionInfoList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getCompleteActiveSubscriptionInfoList():
+ Method 'getCompleteActiveSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getOpportunisticSubscriptions():
+ Method 'getOpportunisticSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getSubscriptionsInGroup(android.os.ParcelUuid):
+ Method 'getSubscriptionsInGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#removeSubscriptionsFromGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'removeSubscriptionsFromGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setOpportunistic(boolean, int):
+ Method 'setOpportunistic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.BarringInfoListener#onBarringInfoChanged(android.telephony.BarringInfo):
+ Method 'onBarringInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallForwardingIndicatorListener#onCallForwardingIndicatorChanged(boolean):
+ Method 'onCallForwardingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.EmergencyNumberListListener#onEmergencyNumberListChanged(java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>):
+ Method 'onEmergencyNumberListChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(android.telephony.ims.ImsReasonInfo):
+ Method 'onImsCallDisconnectCauseChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged(boolean):
+ Method 'onMessageWaitingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PhysicalChannelConfigListener#onPhysicalChannelConfigChanged(java.util.List<android.telephony.PhysicalChannelConfig>):
+ Method 'onPhysicalChannelConfigChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.RegistrationFailedListener#onRegistrationFailed(android.telephony.CellIdentity, String, int, int, int):
+ Method 'onRegistrationFailed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ServiceStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'clearSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#doesSwitchMultiSimConfigTriggerReboot():
+ Method 'doesSwitchMultiSimConfigTriggerReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesForReason(int):
+ Method 'getAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallStateForSubscription():
+ Method 'getCallStateForSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierConfig():
+ Method 'getCarrierConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierRestrictionStatus(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getCarrierRestrictionStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataNetworkType():
+ Method 'getDataNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId():
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId(int):
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList():
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList(int):
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEquivalentHomePlmns():
+ Method 'getEquivalentHomePlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getForbiddenPlmns():
+ Method 'getForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getGroupIdLevel1():
+ Method 'getGroupIdLevel1' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getImei(int):
+ Method 'getImei' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getLine1Number():
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getManualNetworkSelectionPlmn():
+ Method 'getManualNetworkSelectionPlmn' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid():
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid(int):
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNai():
+ Method 'getNai' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSelectionMode():
+ Method 'getNetworkSelectionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSlicingConfiguration(java.util.concurrent.Executor, android.os.OutcomeReceiver<android.telephony.data.NetworkSlicingConfig,android.telephony.TelephonyManager.NetworkSlicingException>):
+ Method 'getNetworkSlicingConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPhoneAccountHandle():
+ Method 'getPhoneAccountHandle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredOpportunisticDataSubscription():
+ Method 'getPreferredOpportunisticDataSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState():
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState(int):
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimSerialNumber():
+ Method 'getSimSerialNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSubscriberId():
+ Method 'getSubscriberId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSupportedRadioAccessFamily():
+ Method 'getSupportedRadioAccessFamily' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVisualVoicemailPackageName():
+ Method 'getVisualVoicemailPackageName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailAlphaTag():
+ Method 'getVoiceMailAlphaTag' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailNumber():
+ Method 'getVoiceMailNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceNetworkType():
+ Method 'getVoiceNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannel(int):
+ Method 'iccCloseLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccExchangeSimIO(int, int, int, int, int, String):
+ Method 'iccExchangeSimIO' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String, int):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannel(int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabled():
+ Method 'isDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabledForReason(int):
+ Method 'isDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataRoamingEnabled():
+ Method 'isDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isManualNetworkSelectionAllowed():
+ Method 'isManualNetworkSelectionAllowed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isModemEnabledForSlot(int):
+ Method 'isModemEnabledForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isMultiSimSupported():
+ Method 'isMultiSimSupported' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPremiumCapabilityAvailableForPurchase(int):
+ Method 'isPremiumCapabilityAvailableForPurchase' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#purchasePremiumCapability(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'purchasePremiumCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootModem():
+ Method 'rebootModem' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(int, android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendEnvelopeWithStatus(String):
+ Method 'sendEnvelopeWithStatus' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler):
+ Method 'sendUssdRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendVisualVoicemailSms(String, int, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedNetworkTypesForReason(int, long):
+ Method 'setAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabled(boolean):
+ Method 'setDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabledForReason(int, boolean):
+ Method 'setDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setForbiddenPlmns(java.util.List<java.lang.String>):
+ Method 'setForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeAutomatic():
+ Method 'setNetworkSelectionModeAutomatic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean, int):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredOpportunisticDataSubscription' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'setSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri):
+ Method 'setVoicemailRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean):
+ Method 'setVoicemailVibrationEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#switchMultiSimConfig(int):
+ Method 'switchMultiSimConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'updateAvailableNetworks' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#deleteSubscription(int, android.app.PendingIntent):
+ Method 'deleteSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent):
+ Method 'downloadSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#updateSubscriptionNickname(int, String, android.app.PendingIntent):
+ Method 'updateSubscriptionNickname' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getVoWiFiModeSetting():
+ Method 'getVoWiFiModeSetting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isAdvancedCallingSettingEnabled():
+ Method 'isAdvancedCallingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isCrossSimCallingEnabled():
+ Method 'isCrossSimCallingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isTtyOverVolteEnabled():
+ Method 'isTtyOverVolteEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiRoamingSettingEnabled():
+ Method 'isVoWiFiRoamingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiSettingEnabled():
+ Method 'isVoWiFiSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVtSettingEnabled():
+ Method 'isVtSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'registerMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'unregisterMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationState(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getProvisioningStatusForCapability(int, int):
+ Method 'getProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getRcsProvisioningStatusForCapability(int, int):
+ Method 'getRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isProvisioningRequiredForCapability(int, int):
+ Method 'isProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsProvisioningRequiredForCapability(int, int):
+ Method 'isRcsProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerFeatureProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback):
+ Method 'registerFeatureProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setProvisioningStatusForCapability(int, int, boolean):
+ Method 'setProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype):
+ Method 'setCurrentInputMethodSubtype' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setBlockNetworkLoads(boolean):
+ Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
+ Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
+
+
+Todo: android.hardware.camera2.params.StreamConfigurationMap:
+ Documentation mentions 'TODO'
+Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
+ Documentation mentions 'TODO'
+
+
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
New API must be flagged with @FlaggedApi: field android.accessibilityservice.AccessibilityService.OVERLAY_RESULT_INTERNAL_ERROR
UnflaggedApi: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index 1633835..e49d6e3 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -1,4 +1,1714 @@
// Baseline format: 1.0
+BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
+ Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
+ Field 'ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.NotificationManager#ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL:
+ Field 'ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED:
+ Field 'ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_MANAGED_PROFILE_PROVISIONED:
+ Field 'ACTION_MANAGED_PROFILE_PROVISIONED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_AIRPLANE_MODE_CHANGED:
+ Field 'ACTION_AIRPLANE_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED:
+ Field 'ACTION_APPLICATION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED:
+ Field 'ACTION_APPLICATION_RESTRICTIONS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_CHANGED:
+ Field 'ACTION_BATTERY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LOW:
+ Field 'ACTION_BATTERY_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_OKAY:
+ Field 'ACTION_BATTERY_OKAY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CAMERA_BUTTON:
+ Field 'ACTION_CAMERA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS:
+ Field 'ACTION_CLOSE_SYSTEM_DIALOGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CONFIGURATION_CHANGED:
+ Field 'ACTION_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DATE_CHANGED:
+ Field 'ACTION_DATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_LOW:
+ Field 'ACTION_DEVICE_STORAGE_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_OK:
+ Field 'ACTION_DEVICE_STORAGE_OK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOCK_EVENT:
+ Field 'ACTION_DOCK_EVENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOMAINS_NEED_VERIFICATION:
+ Field 'ACTION_DOMAINS_NEED_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STARTED:
+ Field 'ACTION_DREAMING_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STOPPED:
+ Field 'ACTION_DREAMING_STOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_CONNECTED:
+ Field 'ACTION_GTALK_SERVICE_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_DISCONNECTED:
+ Field 'ACTION_GTALK_SERVICE_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INPUT_METHOD_CHANGED:
+ Field 'ACTION_INPUT_METHOD_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE:
+ Field 'ACTION_INSTALL_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS:
+ Field 'ACTION_INSTANT_APP_RESOLVER_SETTINGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION:
+ Field 'ACTION_INTENT_FILTER_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOAD_DATA:
+ Field 'ACTION_LOAD_DATA' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCALE_CHANGED:
+ Field 'ACTION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED:
+ Field 'ACTION_LOCKED_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MANAGE_PACKAGE_STORAGE:
+ Field 'ACTION_MANAGE_PACKAGE_STORAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BAD_REMOVAL:
+ Field 'ACTION_MEDIA_BAD_REMOVAL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BUTTON:
+ Field 'ACTION_MEDIA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_CHECKING:
+ Field 'ACTION_MEDIA_CHECKING' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_EJECT:
+ Field 'ACTION_MEDIA_EJECT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_MOUNTED:
+ Field 'ACTION_MEDIA_MOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_NOFS:
+ Field 'ACTION_MEDIA_NOFS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_REMOVED:
+ Field 'ACTION_MEDIA_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_FINISHED:
+ Field 'ACTION_MEDIA_SCANNER_FINISHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_SCAN_FILE:
+ Field 'ACTION_MEDIA_SCANNER_SCAN_FILE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_STARTED:
+ Field 'ACTION_MEDIA_SCANNER_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SHARED:
+ Field 'ACTION_MEDIA_SHARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTABLE:
+ Field 'ACTION_MEDIA_UNMOUNTABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTED:
+ Field 'ACTION_MEDIA_UNMOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_REPLACED:
+ Field 'ACTION_MY_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_SUSPENDED:
+ Field 'ACTION_MY_PACKAGE_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_UNSUSPENDED:
+ Field 'ACTION_MY_PACKAGE_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_NEW_OUTGOING_CALL:
+ Field 'ACTION_NEW_OUTGOING_CALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_SUSPENDED:
+ Field 'ACTION_PACKAGES_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_UNSUSPENDED:
+ Field 'ACTION_PACKAGES_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_ADDED:
+ Field 'ACTION_PACKAGE_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_CHANGED:
+ Field 'ACTION_PACKAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_DATA_CLEARED:
+ Field 'ACTION_PACKAGE_DATA_CLEARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH:
+ Field 'ACTION_PACKAGE_FIRST_LAUNCH' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FULLY_REMOVED:
+ Field 'ACTION_PACKAGE_FULLY_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_INSTALL:
+ Field 'ACTION_PACKAGE_INSTALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REMOVED:
+ Field 'ACTION_PACKAGE_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REPLACED:
+ Field 'ACTION_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_RESTARTED:
+ Field 'ACTION_PACKAGE_RESTARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSTOPPED:
+ Field 'ACTION_PACKAGE_UNSTOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY:
+ Field 'ACTION_PACKAGE_UNSUSPENDED_MANUALLY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_VERIFIED:
+ Field 'ACTION_PACKAGE_VERIFIED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_CONNECTED:
+ Field 'ACTION_POWER_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_DISCONNECTED:
+ Field 'ACTION_POWER_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PROVIDER_CHANGED:
+ Field 'ACTION_PROVIDER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_QUERY_PACKAGE_RESTART:
+ Field 'ACTION_QUERY_PACKAGE_RESTART' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_REBOOT:
+ Field 'ACTION_REBOOT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_RESOLVE_INSTANT_APP_PACKAGE:
+ Field 'ACTION_RESOLVE_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_ROLLBACK_COMMITTED:
+ Field 'ACTION_ROLLBACK_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_OFF:
+ Field 'ACTION_SCREEN_OFF' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_ON:
+ Field 'ACTION_SCREEN_ON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS:
+ Field 'ACTION_SHOW_SUSPENDED_APP_DETAILS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHUTDOWN:
+ Field 'ACTION_SHUTDOWN' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SIM_STATE_CHANGED:
+ Field 'ACTION_SIM_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SPLIT_CONFIGURATION_CHANGED:
+ Field 'ACTION_SPLIT_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIMEZONE_CHANGED:
+ Field 'ACTION_TIMEZONE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_CHANGED:
+ Field 'ACTION_TIME_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_TICK:
+ Field 'ACTION_TIME_TICK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UID_REMOVED:
+ Field 'ACTION_UID_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_CONNECTED:
+ Field 'ACTION_UMS_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_DISCONNECTED:
+ Field 'ACTION_UMS_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_PRESENT:
+ Field 'ACTION_USER_PRESENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_UNLOCKED:
+ Field 'ACTION_USER_UNLOCKED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_WALLPAPER_CHANGED:
+ Field 'ACTION_WALLPAPER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_COMMITTED:
+ Field 'ACTION_SESSION_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_UPDATED:
+ Field 'ACTION_SESSION_UPDATED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_PICTURE:
+ Field 'ACTION_NEW_PICTURE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_VIDEO:
+ Field 'ACTION_NEW_VIDEO' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.hdmi.HdmiControlManager#ACTION_OSD_MESSAGE:
+ Field 'ACTION_OSD_MESSAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS:
+ Field 'ACTION_QUERY_KEYBOARD_LAYOUTS' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_DETACHED:
+ Field 'ACTION_USB_ACCESSORY_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_HANDSHAKE:
+ Field 'ACTION_USB_ACCESSORY_HANDSHAKE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_DEVICE_DETACHED:
+ Field 'ACTION_USB_DEVICE_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HDMI_AUDIO_PLUG:
+ Field 'ACTION_HDMI_AUDIO_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_MICROPHONE_MUTE_CHANGED:
+ Field 'ACTION_MICROPHONE_MUTE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_SPEAKERPHONE_STATE_CHANGED:
+ Field 'ACTION_SPEAKERPHONE_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_CHANNEL_BROWSABLE_REQUESTED:
+ Field 'ACTION_CHANNEL_BROWSABLE_REQUESTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_INITIALIZE_PROGRAMS:
+ Field 'ACTION_INITIALIZE_PROGRAMS' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+ Field 'ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORER_CHANGED:
+ Field 'ACTION_SCORER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORE_NETWORKS:
+ Field 'ACTION_SCORE_NETWORKS' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION:
+ Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED:
+ Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED:
+ Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED:
+ Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED:
+ Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER:
+ Field 'ACTION_EVENT_REMINDER' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.ContactsContract.SimContacts#ACTION_SIM_ACCOUNTS_CHANGED:
+ Field 'ACTION_SIM_ACCOUNTS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
+ Field 'ACTION_SMS_EMERGENCY_CB_RECEIVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#DATA_SMS_RECEIVED_ACTION:
+ Field 'DATA_SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION:
+ Field 'SECRET_CODE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SIM_FULL_ACTION:
+ Field 'SIM_FULL_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION:
+ Field 'SMS_CB_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION:
+ Field 'SMS_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION:
+ Field 'SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_REJECTED_ACTION:
+ Field 'SMS_REJECTED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION:
+ Field 'SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION:
+ Field 'WAP_PUSH_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_RECEIVED_ACTION:
+ Field 'WAP_PUSH_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEYCHAIN_CHANGED:
+ Field 'ACTION_KEYCHAIN_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED:
+ Field 'ACTION_KEY_ACCESS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_STORAGE_CHANGED:
+ Field 'ACTION_STORAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_TRUST_STORE_CHANGED:
+ Field 'ACTION_TRUST_STORE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_START_EUICC_ACTIVATION:
+ Field 'ACTION_START_EUICC_ACTIVATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech#ACTION_TTS_QUEUE_PROCESSING_COMPLETED:
+ Field 'ACTION_TTS_QUEUE_PROCESSING_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech.Engine#ACTION_TTS_DATA_INSTALLED:
+ Field 'ACTION_TTS_DATA_INSTALLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_REFRESH_SUBSCRIPTION_PLANS:
+ Field 'ACTION_REFRESH_SUBSCRIPTION_PLANS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_SUBSCRIPTION_PLANS_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_PLANS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+ Field 'ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_PCO_VALUE:
+ Field 'ACTION_CARRIER_SIGNAL_PCO_VALUE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REDIRECTED:
+ Field 'ACTION_CARRIER_SIGNAL_REDIRECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+ Field 'ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_RESET:
+ Field 'ACTION_CARRIER_SIGNAL_RESET' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALLBACK_MODE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALLBACK_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALL_STATE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALL_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SECRET_CODE:
+ Field 'ACTION_SECRET_CODE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_APPLICATION_STATE_CHANGED:
+ Field 'ACTION_SIM_APPLICATION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_CARD_STATE_CHANGED:
+ Field 'ACTION_SIM_CARD_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_SLOT_STATUS_CHANGED:
+ Field 'ACTION_SIM_SLOT_STATUS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
+ Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_OTA_STATUS_CHANGED:
+ Field 'ACTION_OTA_STATUS_CHANGED' is missing @BroadcastBehavior
+
+
+DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
+ Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
+ Method android.app.Activity.enterPictureInPictureMode(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#startAllocCounting():
+ Method android.app.Instrumentation.startAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#stopAllocCounting():
+ Method android.app.Instrumentation.stopAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#bigContentView:
+ Field Notification.bigContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#contentView:
+ Field Notification.contentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#headsUpContentView:
+ Field Notification.headsUpContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#tickerView:
+ Field Notification.tickerView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.Builder#Builder(int, CharSequence, android.app.PendingIntent):
+ Constructor android.app.Notification.Action.Builder.Builder(int, CharSequence, android.app.PendingIntent): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getCancelLabel():
+ Method android.app.Notification.Action.WearableExtender.getCancelLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getConfirmLabel():
+ Method android.app.Notification.Action.WearableExtender.getConfirmLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getInProgressLabel():
+ Method android.app.Notification.Action.WearableExtender.getInProgressLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setCancelLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setCancelLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setConfirmLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setConfirmLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setInProgressLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setInProgressLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setContent(android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setContent(android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setTicker(CharSequence, android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setTicker(CharSequence, android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIcon():
+ Method android.app.Notification.WearableExtender.getContentIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIconGravity():
+ Method android.app.Notification.WearableExtender.getContentIconGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomContentHeight():
+ Method android.app.Notification.WearableExtender.getCustomContentHeight(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomSizePreset():
+ Method android.app.Notification.WearableExtender.getCustomSizePreset(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getGravity():
+ Method android.app.Notification.WearableExtender.getGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintAvoidBackgroundClipping():
+ Method android.app.Notification.WearableExtender.getHintAvoidBackgroundClipping(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintHideIcon():
+ Method android.app.Notification.WearableExtender.getHintHideIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintScreenTimeout():
+ Method android.app.Notification.WearableExtender.getHintScreenTimeout(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintShowBackgroundOnly():
+ Method android.app.Notification.WearableExtender.getHintShowBackgroundOnly(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIcon(int):
+ Method android.app.Notification.WearableExtender.setContentIcon(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIconGravity(int):
+ Method android.app.Notification.WearableExtender.setContentIconGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomContentHeight(int):
+ Method android.app.Notification.WearableExtender.setCustomContentHeight(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomSizePreset(int):
+ Method android.app.Notification.WearableExtender.setCustomSizePreset(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setGravity(int):
+ Method android.app.Notification.WearableExtender.setGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintAvoidBackgroundClipping(boolean):
+ Method android.app.Notification.WearableExtender.setHintAvoidBackgroundClipping(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintHideIcon(boolean):
+ Method android.app.Notification.WearableExtender.setHintHideIcon(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintScreenTimeout(int):
+ Method android.app.Notification.WearableExtender.setHintScreenTimeout(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintShowBackgroundOnly(boolean):
+ Method android.app.Notification.WearableExtender.setHintShowBackgroundOnly(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.backup.BackupManager#selectBackupTransport(String):
+ Method android.app.backup.BackupManager.selectBackupTransport(String): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND:
+ Field Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#WIFI_RTT_SERVICE:
+ Field Context.WIFI_RTT_SERVICE: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.ComposeShader#ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode):
+ Constructor android.graphics.ComposeShader.ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#A_8:
+ Field PixelFormat.A_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#LA_88:
+ Field PixelFormat.LA_88: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#L_8:
+ Field PixelFormat.L_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_4444:
+ Field PixelFormat.RGBA_4444: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_5551:
+ Field PixelFormat.RGBA_5551: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGB_332:
+ Field PixelFormat.RGB_332: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.hardware.hdmi.HdmiControlManager#RESULT_ALREADY_IN_PROGRESS:
+ Field HdmiControlManager.RESULT_ALREADY_IN_PROGRESS: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setCodeRate(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setCodeRate(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setModulation(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setModulation(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.EGL14#eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int):
+ Method android.opengl.EGL14.eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLES20#GL_STENCIL_INDEX:
+ Field GLES20.GL_STENCIL_INDEX: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLSurfaceView#surfaceRedrawNeeded(android.view.SurfaceHolder):
+ Method android.opengl.GLSurfaceView.surfaceRedrawNeeded(android.view.SurfaceHolder): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle, android.os.UserHandle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle, android.os.UserHandle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.provider.Contacts.People#markAsContacted(android.content.ContentResolver, long):
+ Method android.provider.Contacts.People.markAsContacted(android.content.ContentResolver, long): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_X:
+ Field Type.CubemapFace.POSITVE_X: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Y:
+ Field Type.CubemapFace.POSITVE_Y: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Z:
+ Field Type.CubemapFace.POSITVE_Z: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.speech.tts.TextToSpeech#areDefaultsEnforced():
+ Method android.speech.tts.TextToSpeech.areDefaultsEnforced(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle):
+ Constructor android.telecom.StatusHints.StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIcon(android.content.Context):
+ Method android.telecom.StatusHints.getIcon(android.content.Context): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIconResId():
+ Method android.telecom.StatusHints.getIconResId(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getPackageName():
+ Method android.telecom.StatusHints.getPackageName(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.PhoneStateListener#PhoneStateListener(java.util.concurrent.Executor):
+ Constructor android.telephony.PhoneStateListener.PhoneStateListener(java.util.concurrent.Executor): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionManager#PROFILE_CLASS_DEFAULT:
+ Field SubscriptionManager.PROFILE_CLASS_DEFAULT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringDaily(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringMonthly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringWeekly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_12HOUR:
+ Field DateUtils.FORMAT_12HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_24HOUR:
+ Field DateUtils.FORMAT_24HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_AMPM:
+ Field DateUtils.FORMAT_CAP_AMPM: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON:
+ Field DateUtils.FORMAT_CAP_NOON: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_NO_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_NO_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.ViewGroup.LayoutParams#FILL_PARENT:
+ Field ViewGroup.LayoutParams.FILL_PARENT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.Window#setTitleColor(int):
+ Method android.view.Window.setTitleColor(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.accessibility.AccessibilityEvent#MAX_TEXT_LENGTH:
+ Field AccessibilityEvent.MAX_TEXT_LENGTH: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebSettings#getSaveFormData():
+ Method android.webkit.WebSettings.getSaveFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebView#shouldDelayChildPressedState():
+ Method android.webkit.WebView.shouldDelayChildPressedState(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#clearFormData():
+ Method android.webkit.WebViewDatabase.clearFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#hasFormData():
+ Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
+ Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
+RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+ Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+ Method 'hasFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ Method 'addOnUidImportanceListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getHistoricalProcessExitReasons(String, int, int):
+ Method 'getHistoricalProcessExitReasons' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getProcessesInErrorState():
+ Method 'getProcessesInErrorState' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#killProcessesWhenImperceptible(int[], String):
+ Method 'killProcessesWhenImperceptible' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#setDeviceLocales(android.os.LocaleList):
+ Method 'setDeviceLocales' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent):
+ Method 'setAlarmClock' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExact(int, long, android.app.PendingIntent):
+ Method 'setExact' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent):
+ Method 'setExactAndAllowWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setTime(long):
+ Method 'setTime' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#isOpActive(String, int, String):
+ Method 'isOpActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingActive(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpActiveChangedListener):
+ Method 'startWatchingActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingNoted(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpNotedListener):
+ Method 'startWatchingNoted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationInExternalPublicDir(String, String):
+ Method 'setDestinationInExternalPublicDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationUri(android.net.Uri):
+ Method 'setDestinationUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setNotificationVisibility(int):
+ Method 'setNotificationVisibility' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setShowRunningNotification(boolean):
+ Method 'setShowRunningNotification' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.GameManager#setGameMode(String, int):
+ Method 'setGameMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.GameManager#updateCustomGameModeConfiguration(String, android.app.GameModeConfiguration):
+ Method 'updateCustomGameModeConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.LocaleManager#getApplicationLocales(String):
+ Method 'getApplicationLocales' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.Notification.Builder#setFullScreenIntent(android.app.PendingIntent, boolean):
+ Method 'setFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.NotificationManager#canUseFullScreenIntent():
+ Method 'canUseFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.Service#startForeground(int, android.app.Notification):
+ Method 'startForeground' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.StatusBarManager#canLaunchCaptureContentActivityForNote(android.app.Activity):
+ Method 'canLaunchCaptureContentActivityForNote' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#releaseProjection(int):
+ Method 'releaseProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#requestProjection(int):
+ Method 'requestProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperInfo#getSettingsSliceUri():
+ Method 'getSettingsSliceUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clear():
+ Method 'clear' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clearWallpaper(int, int):
+ Method 'clearWallpaper' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable():
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable(int):
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable():
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable(int):
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperFile(int):
+ Method 'getWallpaperFile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperInfo(int):
+ Method 'getWallpaperInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable():
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable(int):
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable():
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable(int):
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setDisplayPadding(android.graphics.Rect):
+ Method 'setDisplayPadding' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setResource(int):
+ Method 'setResource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream, android.graphics.Rect, boolean):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setWallpaperComponentWithFlags(android.content.ComponentName, int):
+ Method 'setWallpaperComponentWithFlags' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#suggestDesiredDimensions(int, int):
+ Method 'suggestDesiredDimensions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'addCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName):
+ Method 'addPersistentPreferredActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindDeviceAdminServiceAsUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearPackagePersistentPreferredActivities(android.content.ComponentName, String):
+ Method 'clearPackagePersistentPreferredActivities' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearResetPasswordToken(android.content.ComponentName):
+ Method 'clearResetPasswordToken' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#createAndProvisionManagedProfile(android.app.admin.ManagedProfileProvisioningParams):
+ Method 'createAndProvisionManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#finalizeWorkProfileProvisioning(android.os.UserHandle, android.accounts.Account):
+ Method 'finalizeWorkProfileProvisioning' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#generateKeyPair(android.content.ComponentName, String, android.security.keystore.KeyGenParameterSpec, int):
+ Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
+ Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
+ Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
+ Method 'getLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskPackages(android.content.ComponentName):
+ Method 'getLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy():
+ Method 'getNearbyAppStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy():
+ Method 'getNearbyNotificationStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getOrganizationName(android.content.ComponentName):
+ Method 'getOrganizationName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getPasswordComplexity():
+ Method 'getPasswordComplexity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getShortSupportMessage(android.content.ComponentName):
+ Method 'getShortSupportMessage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getUserControlDisabledPackages(android.content.ComponentName):
+ Method 'getUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#hasKeyPair(String):
+ Method 'hasKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, String):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, boolean):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, int):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isDeviceProvisioningConfigApplied():
+ Method 'isDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isPackageSuspended(android.content.ComponentName, String):
+ Method 'isPackageSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isResetPasswordTokenActive(android.content.ComponentName):
+ Method 'isResetPasswordTokenActive' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#lockNow(int):
+ Method 'lockNow' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#provisionFullyManagedDevice(android.app.admin.FullyManagedDeviceProvisioningParams):
+ Method 'provisionFullyManagedDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#removeCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'removeCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#sendLostModeLocationUpdate(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'sendLostModeLocationUpdate' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setActiveProfileOwner(android.content.ComponentName, String):
+ Method 'setActiveProfileOwner' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
+ Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
+ Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
+ Method 'setDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
+ Method 'setLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, String[]):
+ Method 'setLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setPermittedInputMethods' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUninstallBlocked(android.content.ComponentName, String, boolean):
+ Method 'setUninstallBlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUserControlDisabledPackages(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int, CharSequence):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeDevice(int):
+ Method 'wipeDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicyChanged(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicyChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicySetResult(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicySetResult' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#dataChanged(String):
+ Method 'dataChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int):
+ Method 'requestBackup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#setFrameworkSchedulingEnabled(boolean):
+ Method 'setFrameworkSchedulingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, CharSequence):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, String):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetwork(android.net.NetworkRequest):
+ Method 'setRequiredNetwork' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetworkType(int):
+ Method 'setRequiredNetworkType' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setUserInitiated(boolean):
+ Method 'setUserInitiated' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.job.JobParameters#isUserInitiatedJob():
+ Method 'isUserInitiatedJob' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobScheduler#canRunUserInitiatedJobs():
+ Method 'canRunUserInitiatedJobs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.people.PeopleManager#isConversation(String, String):
+ Method 'isConversation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryExternalStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryExternalStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForPackage(java.util.UUID, String, android.os.UserHandle):
+ Method 'queryStatsForPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUid(java.util.UUID, int):
+ Method 'queryStatsForUid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootClassName():
+ Method 'getTaskRootClassName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
+ Method 'getTaskRootPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBucket(String):
+ Method 'getAppStandbyBucket' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBuckets():
+ Method 'getAppStandbyBuckets' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#isAppInactive(String):
+ Method 'isAppInactive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#onCarrierPrivilegedAppsChanged():
+ Method 'onCarrierPrivilegedAppsChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryAndAggregateUsageStats(long, long):
+ Method 'queryAndAggregateUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryConfigurations(int, long, long):
+ Method 'queryConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEventStats(int, long, long):
+ Method 'queryEventStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEvents(long, long):
+ Method 'queryEvents' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryUsageStats(int, long, long):
+ Method 'queryUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageLimitObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent):
+ Method 'registerAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageObserver(int, String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent):
+ Method 'registerAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerUsageSessionObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'registerUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String, long):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageLimitObserver(int):
+ Method 'unregisterAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageObserver(int):
+ Method 'unregisterAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterUsageSessionObserver(int):
+ Method 'unregisterUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle):
+ Method 'bindAppWidgetIdIfAllowed' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle):
+ Method 'isDeviceAssociatedForWifiConnection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#startObservingDevicePresence(String):
+ Method 'startObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#stopObservingDevicePresence(String):
+ Method 'stopObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int, android.companion.virtual.VirtualDeviceParams):
+ Method 'createVirtualDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceParams.Builder#setLockState(int):
+ Method 'setLockState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.ContentResolver#addPeriodicSync(android.accounts.Account, String, android.os.Bundle, long):
+ Method 'addPeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#cancelSync(android.content.SyncRequest):
+ Method 'cancelSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSync():
+ Method 'getCurrentSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSyncs():
+ Method 'getCurrentSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getIsSyncable(android.accounts.Account, String):
+ Method 'getIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getMasterSyncAutomatically():
+ Method 'getMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getPeriodicSyncs(android.accounts.Account, String):
+ Method 'getPeriodicSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getSyncAutomatically(android.accounts.Account, String):
+ Method 'getSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncActive(android.accounts.Account, String):
+ Method 'isSyncActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncPending(android.accounts.Account, String):
+ Method 'isSyncPending' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#registerContentObserverAsUser(android.net.Uri, boolean, android.database.ContentObserver, android.os.UserHandle):
+ Method 'registerContentObserverAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.ContentResolver#removePeriodicSync(android.accounts.Account, String, android.os.Bundle):
+ Method 'removePeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setIsSyncable(android.accounts.Account, String, int):
+ Method 'setIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setMasterSyncAutomatically(boolean):
+ Method 'setMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setSyncAutomatically(android.accounts.Account, String, boolean):
+ Method 'setSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindServiceAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#clearWallpaper():
+ Method 'clearWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDir():
+ Method 'getExternalCacheDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDirs():
+ Method 'getExternalCacheDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDir(String):
+ Method 'getExternalFilesDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDirs(String):
+ Method 'getExternalFilesDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalMediaDirs():
+ Method 'getExternalMediaDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDir():
+ Method 'getObbDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDirs():
+ Method 'getObbDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+ Method 'removeStickyBroadcastAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(android.graphics.Bitmap):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(java.io.InputStream):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#canRequestInteractAcrossProfiles():
+ Method 'canRequestInteractAcrossProfiles' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#getAllPackageInstallerSessions():
+ Method 'getAllPackageInstallerSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#registerPackageInstallerSessionCallback(java.util.concurrent.Executor, android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerPackageInstallerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps.Callback#onPackagesSuspended(String[], android.os.UserHandle, android.os.Bundle):
+ Method 'onPackagesSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getAllSessions():
+ Method 'getAllSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getSessionInfo(int):
+ Method 'getSessionInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getStagedSessions():
+ Method 'getStagedSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.Session#requestUserPreapproval(android.content.pm.PackageInstaller.PreapprovalDetails, android.content.IntentSender):
+ Method 'requestUserPreapproval' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setInstallerPackageName(String):
+ Method 'setInstallerPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setPermissionState(String, int):
+ Method 'setPermissionState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean):
+ Method 'setRequestUpdateOwnership' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int):
+ Method 'setRequireUserAction' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#canRequestPackageInstalls():
+ Method 'canRequestPackageInstalls' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getSuspendedPackageAppExtras():
+ Method 'getSuspendedPackageAppExtras' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getUnsuspendablePackages(String[]):
+ Method 'getUnsuspendablePackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#grantRuntimePermission(String, String, android.os.UserHandle):
+ Method 'grantRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isAutoRevokeWhitelisted(String):
+ Method 'isAutoRevokeWhitelisted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isPackageSuspended():
+ Method 'isPackageSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setDistractingPackageRestrictions(String[], int):
+ Method 'setDistractingPackageRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo, int):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#verifyIntentFilter(int, int, java.util.List<java.lang.String>):
+ Method 'verifyIntentFilter' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getHighestDirectReportRateLevel():
+ Method 'getHighestDirectReportRateLevel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getMinDelay():
+ Method 'getMinDelay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraCharacteristics#getKeysNeedingPermission():
+ Method 'getKeysNeedingPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraClosed(String):
+ Method 'onCameraClosed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraOpened(String, String):
+ Method 'onCameraOpened' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#getHdmiCecVersion():
+ Method 'getHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#setHdmiCecVersion(int):
+ Method 'setHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback):
+ Method 'addGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#getMonitoringTypes():
+ Method 'getMonitoringTypes' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#pauseGeofence(int, int):
+ Method 'pauseGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'registerForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#removeGeofence(int, int):
+ Method 'removeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#resumeGeofence(int, int, int):
+ Method 'resumeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'unregisterForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#grantPermission(android.hardware.usb.UsbDevice, String):
+ Method 'grantPermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#hasPermission(android.hardware.usb.UsbDevice):
+ Method 'hasPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent):
+ Method 'requestPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioAttributes.Builder#setHapticChannelsMuted(boolean):
+ Method 'setHapticChannelsMuted' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallDownlinkExtractionAudioRecord(android.media.AudioFormat):
+ Method 'getCallDownlinkExtractionAudioRecord' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallUplinkInjectionAudioTrack(android.media.AudioFormat):
+ Method 'getCallUplinkInjectionAudioTrack' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#registerAudioPolicy(android.media.audiopolicy.AudioPolicy):
+ Method 'registerAudioPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecord#shareAudioHistory(String, long):
+ Method 'shareAudioHistory' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecordingConfiguration#getClientUid():
+ Method 'getClientUid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaCodec#createByCodecNameForClient(String, int, int):
+ Method 'createByCodecNameForClient' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaPlayer#setWakeMode(android.content.Context, int):
+ Method 'setWakeMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaRouter2#getInstance(android.content.Context, String):
+ Method 'getInstance' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getCursor():
+ Method 'getCursor' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getValidRingtoneUri(android.content.Context):
+ Method 'getValidRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.audiofx.HapticGenerator#setEnabled(boolean):
+ Method 'setEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjection#createVirtualDisplay(String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler):
+ Method 'createVirtualDisplay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjectionManager#getMediaProjection(int, android.content.Intent):
+ Method 'getMediaProjection' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.content.ComponentName, android.os.UserHandle, java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnActiveSessionsChangedListener):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnMediaKeyEventSessionChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener):
+ Method 'addOnMediaKeyEventSessionChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessions(android.content.ComponentName):
+ Method 'getActiveSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessionsForUser(android.content.ComponentName, android.os.UserHandle):
+ Method 'getActiveSessionsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSession():
+ Method 'getMediaKeyEventSession' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSessionPackageName():
+ Method 'getMediaKeyEventSessionPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo):
+ Method 'isTrustedForMediaControl' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#deleteKeyphraseSoundModel(int, java.util.Locale):
+ Method 'deleteKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#getKeyphraseSoundModel(int, java.util.Locale):
+ Method 'getKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#updateKeyphraseSoundModel(android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel):
+ Method 'updateKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#clearScores():
+ Method 'clearScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#disableScoring():
+ Method 'disableScoring' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#getActiveScorerPackage():
+ Method 'getActiveScorerPackage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#registerNetworkScoreCallback(int, int, java.util.concurrent.Executor, android.net.NetworkScoreManager.NetworkScoreCallback):
+ Method 'registerNetworkScoreCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#requestScores(java.util.Collection<android.net.NetworkKey>):
+ Method 'requestScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#setActiveScorer(String):
+ Method 'setActiveScorer' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.VpnService#prepareAndAuthorize(android.content.Context):
+ Method 'prepareAndAuthorize' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean):
+ Method 'setSpeakerMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#startAudio():
+ Method 'startAudio' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#addVcnNetworkPolicyChangeListener(java.util.concurrent.Executor, android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'addVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#applyVcnNetworkPolicy(android.net.NetworkCapabilities, android.net.LinkProperties):
+ Method 'applyVcnNetworkPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#removeVcnNetworkPolicyChangeListener(android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'removeVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity):
+ Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]):
+ Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String):
+ Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String):
+ Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String):
+ Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]):
+ Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]):
+ Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int):
+ Method 'decrement' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int):
+ Method 'increment' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int):
+ Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#restore(int):
+ Method 'restore' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transfer(int):
+ Method 'transfer' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]):
+ Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int):
+ Method 'readPages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]):
+ Method 'writePage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#getNdefMessage():
+ Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#isWritable():
+ Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#makeReadOnly():
+ Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage):
+ Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage):
+ Method 'format' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage):
+ Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#close():
+ Method 'close' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#connect():
+ Method 'connect' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#cancelBugreport():
+ Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#preDumpUiData():
+ Method 'preDumpUiData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Build#getSerial():
+ Method 'getSerial' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Debug#dumpService(String, java.io.FileDescriptor, String[]):
+ Method 'dumpService' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.DropBoxManager#getNextEntry(String, long):
+ Method 'getNextEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Environment#getExternalStorageDirectory():
+ Method 'getExternalStorageDirectory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager():
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager(java.io.File):
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#dream(long):
+ Method 'dream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#forceSuspend():
+ Method 'forceSuspend' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#getPowerSaveModeTrigger():
+ Method 'getPowerSaveModeTrigger' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#newWakeLock(int, String):
+ Method 'newWakeLock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#reboot(String):
+ Method 'reboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setBatteryDischargePrediction(java.time.Duration, boolean):
+ Method 'setBatteryDischargePrediction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setDynamicPowerSaveHint(boolean, int):
+ Method 'setDynamicPowerSaveHint' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#userActivity(long, int, int):
+ Method 'userActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.RecoverySystem#rebootWipeUserData(android.content.Context):
+ Method 'rebootWipeUserData' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.StrictMode.VmPolicy.Builder#detectFileUriExposure():
+ Method 'detectFileUriExposure' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#retrieveSystemUpdateInfo():
+ Method 'retrieveSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#updateSystemUpdateInfo(android.os.PersistableBundle):
+ Method 'updateSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserProperties(android.os.UserHandle):
+ Method 'getUserProperties' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserRestrictions(android.os.UserHandle):
+ Method 'getUserRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#hasUserRestrictionForUser(String, android.os.UserHandle):
+ Method 'hasUserRestrictionForUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isManagedProfile(int):
+ Method 'isManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile():
+ Method 'isRestrictedProfile' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile(android.os.UserHandle):
+ Method 'isRestrictedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlocked(android.os.UserHandle):
+ Method 'isUserUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlockingOrUnlocked(android.os.UserHandle):
+ Method 'isUserUnlockingOrUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle):
+ Method 'requestQuietModeEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#setUserRestriction(String, boolean):
+ Method 'setUserRestriction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshot(int):
+ Method 'takeUidSnapshot' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshots(int[]):
+ Method 'takeUidSnapshots' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getCloudMediaProvider():
+ Method 'getCloudMediaProvider' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getManageSpaceActivityIntent(String, int):
+ Method 'getManageSpaceActivityIntent' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#notifyAppIoBlocked(java.util.UUID, int, int, int):
+ Method 'notifyAppIoBlocked' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#notifyAppIoResumed(java.util.UUID, int, int, int):
+ Method 'notifyAppIoResumed' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#setCloudMediaProvider(String):
+ Method 'setCloudMediaProvider' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageVolume#createAccessIntent(String):
+ Method 'createAccessIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDeliveryFromDataSource(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDeliveryFromDataSource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForStartDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForStartDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.provider.Settings#canDrawOverlays(android.content.Context):
+ Method 'canDrawOverlays' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.provider.Settings.System#canWrite(android.content.Context):
+ Method 'canWrite' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.security.KeyChain#removeCredentialManagementApp(android.content.Context):
+ Method 'removeCredentialManagementApp' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginCreateCredentialResponse.Builder#setRemoteCreateEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCreateEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginGetCredentialResponse.Builder#setRemoteCredentialEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCredentialEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.CallingAppInfo#getOrigin():
+ Method 'getOrigin' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactDisplayName():
+ Method 'getContactDisplayName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactPhotoUri():
+ Method 'getContactPhotoUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle):
+ Method 'acceptHandover' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingCall' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingConference(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingConference' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getLine1Number(android.telecom.PhoneAccountHandle):
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getOwnSelfManagedPhoneAccounts():
+ Method 'getOwnSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getPhoneAccount(android.telecom.PhoneAccountHandle):
+ Method 'getPhoneAccount' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getSelfManagedPhoneAccounts():
+ Method 'getSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#hasManageOngoingCallsPermission():
+ Method 'hasManageOngoingCallsPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#placeCall(android.net.Uri, android.os.Bundle):
+ Method 'placeCall' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#showInCallScreen(boolean):
+ Method 'showInCallScreen' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#silenceRinger():
+ Method 'silenceRinger' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig():
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig(java.lang.String...):
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigByComponentForSubId(String, int):
+ Method 'getConfigByComponentForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int, java.lang.String...):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#notifyConfigChangedForSubId(int):
+ Method 'notifyConfigChangedForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CellLocation#requestLocationUpdate():
+ Method 'requestLocationUpdate' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.NetworkRegistrationInfo#getCellIdentity():
+ Method 'getCellIdentity' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallStateChanged(int, String):
+ Method 'onCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onDisplayInfoChanged(android.telephony.TelephonyDisplayInfo):
+ Method 'onDisplayInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onRadioPowerStateChanged(int):
+ Method 'onRadioPowerStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaNetworkId():
+ Method 'getCdmaNetworkId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaSystemId():
+ Method 'getCdmaSystemId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaLong():
+ Method 'getOperatorAlphaLong' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaShort():
+ Method 'getOperatorAlphaShort' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorNumeric():
+ Method 'getOperatorNumeric' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SignalStrengthUpdateRequest.Builder#setSystemThresholdReportingRequestedWhileIdle(boolean):
+ Method 'setSystemThresholdReportingRequestedWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#disableCellBroadcastRange(int, int, int):
+ Method 'disableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#enableCellBroadcastRange(int, int, int):
+ Method 'enableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#getSmscAddress():
+ Method 'getSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#injectSmsPdu(byte[], String, android.app.PendingIntent):
+ Method 'injectSmsPdu' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendDataMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#setSmscAddress(String):
+ Method 'setSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#addSubscriptionsIntoGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'addSubscriptionsIntoGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#createSubscriptionGroup(java.util.List<java.lang.Integer>):
+ Method 'createSubscriptionGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfo(int):
+ Method 'getActiveSubscriptionInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoForSimSlotIndex(int):
+ Method 'getActiveSubscriptionInfoForSimSlotIndex' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoList():
+ Method 'getActiveSubscriptionInfoList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList():
+ Method 'getAvailableSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getCompleteActiveSubscriptionInfoList():
+ Method 'getCompleteActiveSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getOpportunisticSubscriptions():
+ Method 'getOpportunisticSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getResourcesForSubId(android.content.Context, int):
+ Method 'getResourcesForSubId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getSubscriptionsInGroup(android.os.ParcelUuid):
+ Method 'getSubscriptionsInGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#removeSubscriptionsFromGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'removeSubscriptionsFromGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh():
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh(int):
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setOpportunistic(boolean, int):
+ Method 'setOpportunistic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setPreferredDataSubscriptionId(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredDataSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.BarringInfoListener#onBarringInfoChanged(android.telephony.BarringInfo):
+ Method 'onBarringInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallStatesChanged(java.util.List<android.telephony.CallState>):
+ Method 'onCallStatesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallForwardingIndicatorListener#onCallForwardingIndicatorChanged(boolean):
+ Method 'onCallForwardingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.DataEnabledListener#onDataEnabledChanged(boolean, int):
+ Method 'onDataEnabledChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.EmergencyNumberListListener#onEmergencyNumberListChanged(java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>):
+ Method 'onEmergencyNumberListChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(android.telephony.ims.ImsReasonInfo):
+ Method 'onImsCallDisconnectCauseChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged(boolean):
+ Method 'onMessageWaitingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PhysicalChannelConfigListener#onPhysicalChannelConfigChanged(java.util.List<android.telephony.PhysicalChannelConfig>):
+ Method 'onPhysicalChannelConfigChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(android.telephony.PreciseCallState):
+ Method 'onPreciseCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.RegistrationFailedListener#onRegistrationFailed(android.telephony.CellIdentity, String, int, int, int):
+ Method 'onRegistrationFailed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ServiceStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#bootstrapAuthenticationRequest(int, android.net.Uri, android.telephony.gba.UaSecurityProtocolIdentifier, boolean, java.util.concurrent.Executor, android.telephony.TelephonyManager.BootstrapAuthenticationCallback):
+ Method 'bootstrapAuthenticationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#changeIccLockPin(String, String):
+ Method 'changeIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearRadioPowerOffForReason(int):
+ Method 'clearRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'clearSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#doesSwitchMultiSimConfigTriggerReboot():
+ Method 'doesSwitchMultiSimConfigTriggerReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#enableModemForSlot(int, boolean):
+ Method 'enableModemForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAidForAppType(int):
+ Method 'getAidForAppType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypes():
+ Method 'getAllowedNetworkTypes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesBitmask():
+ Method 'getAllowedNetworkTypesBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesForReason(int):
+ Method 'getAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallStateForSubscription():
+ Method 'getCallStateForSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierConfig():
+ Method 'getCarrierConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierInfoForImsiEncryption(int):
+ Method 'getCarrierInfoForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierRestrictionStatus(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getCarrierRestrictionStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaRoamingMode():
+ Method 'getCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaSubscriptionMode():
+ Method 'getCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataActivationState():
+ Method 'getDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataNetworkType():
+ Method 'getDataNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId():
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId(int):
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceSoftwareVersion(int):
+ Method 'getDeviceSoftwareVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberDbVersion():
+ Method 'getEmergencyNumberDbVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList():
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList(int):
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEquivalentHomePlmns():
+ Method 'getEquivalentHomePlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getForbiddenPlmns():
+ Method 'getForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getGroupIdLevel1():
+ Method 'getGroupIdLevel1' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getImei(int):
+ Method 'getImei' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getIsimDomain():
+ Method 'getIsimDomain' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getLine1Number():
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getManualNetworkSelectionPlmn():
+ Method 'getManualNetworkSelectionPlmn' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid():
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid(int):
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNai():
+ Method 'getNai' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSelectionMode():
+ Method 'getNetworkSelectionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSlicingConfiguration(java.util.concurrent.Executor, android.os.OutcomeReceiver<android.telephony.data.NetworkSlicingConfig,android.telephony.TelephonyManager.NetworkSlicingException>):
+ Method 'getNetworkSlicingConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPhoneAccountHandle():
+ Method 'getPhoneAccountHandle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredNetworkTypeBitmask():
+ Method 'getPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredOpportunisticDataSubscription():
+ Method 'getPreferredOpportunisticDataSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerOffReasons():
+ Method 'getRadioPowerOffReasons' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerState():
+ Method 'getRadioPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState():
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState(int):
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimLocale():
+ Method 'getSimLocale' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimSerialNumber():
+ Method 'getSimSerialNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSubscriberId():
+ Method 'getSubscriberId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSupportedRadioAccessFamily():
+ Method 'getSupportedRadioAccessFamily' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSystemSelectionChannels():
+ Method 'getSystemSelectionChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getTelephonyHistograms():
+ Method 'getTelephonyHistograms' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVisualVoicemailPackageName():
+ Method 'getVisualVoicemailPackageName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceActivationState():
+ Method 'getVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailAlphaTag():
+ Method 'getVoiceMailAlphaTag' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailNumber():
+ Method 'getVoiceMailNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceNetworkType():
+ Method 'getVoiceNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannel(int):
+ Method 'iccCloseLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannelBySlot(int, int):
+ Method 'iccCloseLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccExchangeSimIO(int, int, int, int, int, String):
+ Method 'iccExchangeSimIO' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String, int):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannelBySlot(int, String, int):
+ Method 'iccOpenLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannel(int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isApplicationOnUicc(int):
+ Method 'isApplicationOnUicc' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabled():
+ Method 'isDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabledForReason(int):
+ Method 'isDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataRoamingEnabled():
+ Method 'isDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isEmergencyAssistanceEnabled():
+ Method 'isEmergencyAssistanceEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isIccLockEnabled():
+ Method 'isIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isManualNetworkSelectionAllowed():
+ Method 'isManualNetworkSelectionAllowed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isModemEnabledForSlot(int):
+ Method 'isModemEnabledForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isMultiSimSupported():
+ Method 'isMultiSimSupported' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isOpportunisticNetworkEnabled():
+ Method 'isOpportunisticNetworkEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPotentialEmergencyNumber(String):
+ Method 'isPotentialEmergencyNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPremiumCapabilityAvailableForPurchase(int):
+ Method 'isPremiumCapabilityAvailableForPurchase' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isTetheringApnRequired():
+ Method 'isTetheringApnRequired' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#notifyOtaEmergencyNumberDbInstalled():
+ Method 'notifyOtaEmergencyNumberDbInstalled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#prepareForUnattendedReboot():
+ Method 'prepareForUnattendedReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#purchasePremiumCapability(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'purchasePremiumCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootModem():
+ Method 'rebootModem' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootRadio():
+ Method 'rebootRadio' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#reportDefaultNetworkStatus(boolean):
+ Method 'reportDefaultNetworkStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(int, android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback):
+ Method 'requestNumberVerification' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestRadioPowerOffForReason(int):
+ Method 'requestRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetAllCarrierActions():
+ Method 'resetAllCarrierActions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetCarrierKeysForImsiEncryption():
+ Method 'resetCarrierKeysForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetOtaEmergencyNumberDbFilePath():
+ Method 'resetOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetRadioConfig():
+ Method 'resetRadioConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendEnvelopeWithStatus(String):
+ Method 'sendEnvelopeWithStatus' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendThermalMitigationRequest(android.telephony.ThermalMitigationRequest):
+ Method 'sendThermalMitigationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler):
+ Method 'sendUssdRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendVisualVoicemailSms(String, int, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>):
+ Method 'setAllowedCarriers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedNetworkTypesForReason(int, long):
+ Method 'setAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierDataEnabled(boolean):
+ Method 'setCarrierDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierRestrictionRules(android.telephony.CarrierRestrictionRules):
+ Method 'setCarrierRestrictionRules' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaRoamingMode(int):
+ Method 'setCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaSubscriptionMode(int):
+ Method 'setCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataActivationState(int):
+ Method 'setDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabled(boolean):
+ Method 'setDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabledForReason(int, boolean):
+ Method 'setDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataRoamingEnabled(boolean):
+ Method 'setDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setForbiddenPlmns(java.util.List<java.lang.String>):
+ Method 'setForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setIccLockEnabled(boolean, String):
+ Method 'setIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeAutomatic():
+ Method 'setNetworkSelectionModeAutomatic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean, int):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setOpportunisticNetworkState(boolean):
+ Method 'setOpportunisticNetworkState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredNetworkTypeBitmask(long):
+ Method 'setPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredOpportunisticDataSubscription' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setRadioEnabled(boolean):
+ Method 'setRadioEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'setSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoiceActivationState(int):
+ Method 'setVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri):
+ Method 'setVoicemailRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean):
+ Method 'setVoicemailVibrationEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPin(String):
+ Method 'supplyIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPuk(String, String):
+ Method 'supplyIccLockPuk' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#switchMultiSimConfig(int):
+ Method 'switchMultiSimConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'updateAvailableNetworks' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateOtaEmergencyNumberDbFilePath(android.os.ParcelFileDescriptor):
+ Method 'updateOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings):
+ Method 'setSmsFilterSettings' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#continueOperation(android.content.Intent, android.os.Bundle):
+ Method 'continueOperation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#deleteSubscription(int, android.app.PendingIntent):
+ Method 'deleteSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent):
+ Method 'downloadSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(int, android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList(android.app.PendingIntent):
+ Method 'getDefaultDownloadableSubscriptionList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent):
+ Method 'getDownloadableSubscriptionMetadata' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getOtaStatus():
+ Method 'getOtaStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getSupportedCountries():
+ Method 'getSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getUnsupportedCountries():
+ Method 'getUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setSupportedCountries(java.util.List<java.lang.String>):
+ Method 'setSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setUnsupportedCountries(java.util.List<java.lang.String>):
+ Method 'setUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#updateSubscriptionNickname(int, String, android.app.PendingIntent):
+ Method 'updateSubscriptionNickname' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int):
+ Method 'createForSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getVoWiFiModeSetting():
+ Method 'getVoWiFiModeSetting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isAdvancedCallingSettingEnabled():
+ Method 'isAdvancedCallingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isCrossSimCallingEnabled():
+ Method 'isCrossSimCallingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isTtyOverVolteEnabled():
+ Method 'isTtyOverVolteEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiRoamingSettingEnabled():
+ Method 'isVoWiFiRoamingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiSettingEnabled():
+ Method 'isVoWiFiSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVtSettingEnabled():
+ Method 'isVtSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'registerMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'unregisterMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationState(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getProvisioningStatusForCapability(int, int):
+ Method 'getProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getRcsProvisioningStatusForCapability(int, int):
+ Method 'getRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isProvisioningRequiredForCapability(int, int):
+ Method 'isProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsProvisioningRequiredForCapability(int, int):
+ Method 'isRcsProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable():
+ Method 'isRcsVolteSingleRegistrationCapable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#notifyRcsAutoConfigurationReceived(byte[], boolean):
+ Method 'notifyRcsAutoConfigurationReceived' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerFeatureProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback):
+ Method 'registerFeatureProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback):
+ Method 'registerProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'registerRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setProvisioningStatusForCapability(int, int, boolean):
+ Method 'setProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback):
+ Method 'unregisterProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback(android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'unregisterRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#registerSipDialogStateCallback(java.util.concurrent.Executor, android.telephony.ims.SipDialogStateCallback):
+ Method 'registerSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#unregisterSipDialogStateCallback(android.telephony.ims.SipDialogStateCallback):
+ Method 'unregisterSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.WindowManager.LayoutParams#isSystemApplicationOverlay():
+ Method 'isSystemApplicationOverlay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#registerDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'registerDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#unregisterDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'unregisterDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningEnabled(boolean):
+ Method 'setSystemAudioCaptioningEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningUiEnabled(boolean):
+ Method 'setSystemAudioCaptioningUiEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype):
+ Method 'setCurrentInputMethodSubtype' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setBlockNetworkLoads(boolean):
+ Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
+ Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
+
+
SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
@@ -37,6 +1747,80 @@
SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.setOnVolumeKeyLongPressListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SdkConstant: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.pm.PackageInstaller#ACTION_CONFIRM_PRE_APPROVAL:
+ Field 'ACTION_CONFIRM_PRE_APPROVAL' is missing @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+
+
+Todo: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#READ_PEOPLE_DATA:
+ Documentation mentions 'TODO'
+Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
+ Documentation mentions 'TODO'
+Todo: android.hardware.camera2.params.StreamConfigurationMap:
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#loadNanoApp(int, android.hardware.location.NanoApp):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#unloadNanoApp(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.NanoAppInstanceInfo:
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvContentRatingSystemInfo#getXmlUri():
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvInputInfo#isConnectedToHdmiSwitch():
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#prepareForUnattendedUpdate(android.content.Context, String, android.content.IntentSender):
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#rebootAndApply(android.content.Context, String, String):
+ Documentation mentions 'TODO'
+Todo: android.os.SystemConfigManager:
+ Documentation mentions 'TODO'
+Todo: android.os.UpdateEngineCallback#onStatusUpdate(int, float):
+ Documentation mentions 'TODO'
+Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
+ Documentation mentions 'TODO'
+Todo: android.service.voice.AlwaysOnHotwordDetector:
+ Documentation mentions 'TODO'
+Todo: android.telephony.TelephonyManager#getCurrentPhoneType():
+ Documentation mentions 'TODO'
+
+
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
UnflaggedApi: android.Manifest.permission#USE_COMPANION_TRANSPORTS:
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 5bce8b2..814be2f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -37,7 +37,7 @@
field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES";
- field public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER";
+ field @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission") public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER";
field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
@@ -261,6 +261,7 @@
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE";
+ field @FlaggedApi("android.content.pm.quarantined_enabled") public static final String QUARANTINE_APPS = "android.permission.QUARANTINE_APPS";
field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
field public static final String QUERY_CLONED_APPS = "android.permission.QUERY_CLONED_APPS";
field @Deprecated public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
@@ -627,6 +628,7 @@
field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
field public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android:capture_consentless_bugreport_on_userdebug_build";
field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
+ field @FlaggedApi("android.view.contentprotection.flags.create_accessibility_overlay_app_op_enabled") public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY = "android:create_accessibility_overlay";
field public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
field public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service";
field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
@@ -980,7 +982,7 @@
field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb
}
- public static final class Notification.TvExtender implements android.app.Notification.Extender {
+ @FlaggedApi("android.app.api_tvextender") public static final class Notification.TvExtender implements android.app.Notification.Extender {
method public boolean getSuppressShowOverApps();
method public android.app.Notification.TvExtender setChannel(String);
}
@@ -3252,6 +3254,7 @@
field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
+ field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
}
@@ -3859,7 +3862,7 @@
method public boolean getInstallAsVirtualPreload();
method public int getPendingUserActionReason();
method public boolean getRequestDowngrade();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_INSTALLED_SESSION_PATHS) public String getResolvedBaseApkPath();
+ method @FlaggedApi("android.content.pm.get_resolved_apk_path") @Nullable @RequiresPermission(android.Manifest.permission.READ_INSTALLED_SESSION_PATHS) public String getResolvedBaseApkPath();
method public int getRollbackDataPolicy();
method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
}
@@ -3914,6 +3917,7 @@
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
@@ -6747,7 +6751,7 @@
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
- method @FlaggedApi("com.android.media.audio.flags.audio_policy_update_mixing_rules_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
+ method @FlaggedApi("android.media.audiopolicy.audio_policy_update_mixing_rules_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int updateMixingRules(@NonNull java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>);
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index a501031..8652402 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -3,6 +3,512 @@
Method should return Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
+BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
+ Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
+ Field 'ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.NotificationManager#ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL:
+ Field 'ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED:
+ Field 'ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_MANAGED_PROFILE_PROVISIONED:
+ Field 'ACTION_MANAGED_PROFILE_PROVISIONED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_AIRPLANE_MODE_CHANGED:
+ Field 'ACTION_AIRPLANE_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED:
+ Field 'ACTION_APPLICATION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED:
+ Field 'ACTION_APPLICATION_RESTRICTIONS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_CHANGED:
+ Field 'ACTION_BATTERY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LOW:
+ Field 'ACTION_BATTERY_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_OKAY:
+ Field 'ACTION_BATTERY_OKAY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CAMERA_BUTTON:
+ Field 'ACTION_CAMERA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS:
+ Field 'ACTION_CLOSE_SYSTEM_DIALOGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CONFIGURATION_CHANGED:
+ Field 'ACTION_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DATE_CHANGED:
+ Field 'ACTION_DATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_LOW:
+ Field 'ACTION_DEVICE_STORAGE_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_OK:
+ Field 'ACTION_DEVICE_STORAGE_OK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOCK_EVENT:
+ Field 'ACTION_DOCK_EVENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOMAINS_NEED_VERIFICATION:
+ Field 'ACTION_DOMAINS_NEED_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STARTED:
+ Field 'ACTION_DREAMING_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STOPPED:
+ Field 'ACTION_DREAMING_STOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_CONNECTED:
+ Field 'ACTION_GTALK_SERVICE_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_DISCONNECTED:
+ Field 'ACTION_GTALK_SERVICE_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INPUT_METHOD_CHANGED:
+ Field 'ACTION_INPUT_METHOD_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE:
+ Field 'ACTION_INSTALL_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS:
+ Field 'ACTION_INSTANT_APP_RESOLVER_SETTINGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION:
+ Field 'ACTION_INTENT_FILTER_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOAD_DATA:
+ Field 'ACTION_LOAD_DATA' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCALE_CHANGED:
+ Field 'ACTION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED:
+ Field 'ACTION_LOCKED_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MANAGE_PACKAGE_STORAGE:
+ Field 'ACTION_MANAGE_PACKAGE_STORAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BAD_REMOVAL:
+ Field 'ACTION_MEDIA_BAD_REMOVAL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BUTTON:
+ Field 'ACTION_MEDIA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_CHECKING:
+ Field 'ACTION_MEDIA_CHECKING' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_EJECT:
+ Field 'ACTION_MEDIA_EJECT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_MOUNTED:
+ Field 'ACTION_MEDIA_MOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_NOFS:
+ Field 'ACTION_MEDIA_NOFS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_REMOVED:
+ Field 'ACTION_MEDIA_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_FINISHED:
+ Field 'ACTION_MEDIA_SCANNER_FINISHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_SCAN_FILE:
+ Field 'ACTION_MEDIA_SCANNER_SCAN_FILE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_STARTED:
+ Field 'ACTION_MEDIA_SCANNER_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SHARED:
+ Field 'ACTION_MEDIA_SHARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTABLE:
+ Field 'ACTION_MEDIA_UNMOUNTABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTED:
+ Field 'ACTION_MEDIA_UNMOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_REPLACED:
+ Field 'ACTION_MY_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_SUSPENDED:
+ Field 'ACTION_MY_PACKAGE_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_UNSUSPENDED:
+ Field 'ACTION_MY_PACKAGE_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_NEW_OUTGOING_CALL:
+ Field 'ACTION_NEW_OUTGOING_CALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_SUSPENDED:
+ Field 'ACTION_PACKAGES_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_UNSUSPENDED:
+ Field 'ACTION_PACKAGES_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_ADDED:
+ Field 'ACTION_PACKAGE_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_CHANGED:
+ Field 'ACTION_PACKAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_DATA_CLEARED:
+ Field 'ACTION_PACKAGE_DATA_CLEARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH:
+ Field 'ACTION_PACKAGE_FIRST_LAUNCH' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FULLY_REMOVED:
+ Field 'ACTION_PACKAGE_FULLY_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_INSTALL:
+ Field 'ACTION_PACKAGE_INSTALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REMOVED:
+ Field 'ACTION_PACKAGE_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REPLACED:
+ Field 'ACTION_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_RESTARTED:
+ Field 'ACTION_PACKAGE_RESTARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSTOPPED:
+ Field 'ACTION_PACKAGE_UNSTOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY:
+ Field 'ACTION_PACKAGE_UNSUSPENDED_MANUALLY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_VERIFIED:
+ Field 'ACTION_PACKAGE_VERIFIED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_CONNECTED:
+ Field 'ACTION_POWER_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_DISCONNECTED:
+ Field 'ACTION_POWER_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PROVIDER_CHANGED:
+ Field 'ACTION_PROVIDER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_QUERY_PACKAGE_RESTART:
+ Field 'ACTION_QUERY_PACKAGE_RESTART' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_REBOOT:
+ Field 'ACTION_REBOOT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_RESOLVE_INSTANT_APP_PACKAGE:
+ Field 'ACTION_RESOLVE_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_ROLLBACK_COMMITTED:
+ Field 'ACTION_ROLLBACK_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_OFF:
+ Field 'ACTION_SCREEN_OFF' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_ON:
+ Field 'ACTION_SCREEN_ON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS:
+ Field 'ACTION_SHOW_SUSPENDED_APP_DETAILS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHUTDOWN:
+ Field 'ACTION_SHUTDOWN' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SIM_STATE_CHANGED:
+ Field 'ACTION_SIM_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SPLIT_CONFIGURATION_CHANGED:
+ Field 'ACTION_SPLIT_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIMEZONE_CHANGED:
+ Field 'ACTION_TIMEZONE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_CHANGED:
+ Field 'ACTION_TIME_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_TICK:
+ Field 'ACTION_TIME_TICK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UID_REMOVED:
+ Field 'ACTION_UID_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_CONNECTED:
+ Field 'ACTION_UMS_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_DISCONNECTED:
+ Field 'ACTION_UMS_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_PRESENT:
+ Field 'ACTION_USER_PRESENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_UNLOCKED:
+ Field 'ACTION_USER_UNLOCKED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_WALLPAPER_CHANGED:
+ Field 'ACTION_WALLPAPER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_COMMITTED:
+ Field 'ACTION_SESSION_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_UPDATED:
+ Field 'ACTION_SESSION_UPDATED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_PICTURE:
+ Field 'ACTION_NEW_PICTURE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_VIDEO:
+ Field 'ACTION_NEW_VIDEO' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.hdmi.HdmiControlManager#ACTION_OSD_MESSAGE:
+ Field 'ACTION_OSD_MESSAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS:
+ Field 'ACTION_QUERY_KEYBOARD_LAYOUTS' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_DETACHED:
+ Field 'ACTION_USB_ACCESSORY_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_HANDSHAKE:
+ Field 'ACTION_USB_ACCESSORY_HANDSHAKE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_DEVICE_DETACHED:
+ Field 'ACTION_USB_DEVICE_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HDMI_AUDIO_PLUG:
+ Field 'ACTION_HDMI_AUDIO_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_MICROPHONE_MUTE_CHANGED:
+ Field 'ACTION_MICROPHONE_MUTE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_SPEAKERPHONE_STATE_CHANGED:
+ Field 'ACTION_SPEAKERPHONE_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_CHANNEL_BROWSABLE_REQUESTED:
+ Field 'ACTION_CHANNEL_BROWSABLE_REQUESTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_INITIALIZE_PROGRAMS:
+ Field 'ACTION_INITIALIZE_PROGRAMS' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+ Field 'ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORER_CHANGED:
+ Field 'ACTION_SCORER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORE_NETWORKS:
+ Field 'ACTION_SCORE_NETWORKS' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION:
+ Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED:
+ Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED:
+ Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED:
+ Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED:
+ Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER:
+ Field 'ACTION_EVENT_REMINDER' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.ContactsContract.SimContacts#ACTION_SIM_ACCOUNTS_CHANGED:
+ Field 'ACTION_SIM_ACCOUNTS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
+ Field 'ACTION_SMS_EMERGENCY_CB_RECEIVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#DATA_SMS_RECEIVED_ACTION:
+ Field 'DATA_SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION:
+ Field 'SECRET_CODE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SIM_FULL_ACTION:
+ Field 'SIM_FULL_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION:
+ Field 'SMS_CB_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION:
+ Field 'SMS_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION:
+ Field 'SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_REJECTED_ACTION:
+ Field 'SMS_REJECTED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION:
+ Field 'SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION:
+ Field 'WAP_PUSH_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_RECEIVED_ACTION:
+ Field 'WAP_PUSH_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEYCHAIN_CHANGED:
+ Field 'ACTION_KEYCHAIN_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED:
+ Field 'ACTION_KEY_ACCESS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_STORAGE_CHANGED:
+ Field 'ACTION_STORAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_TRUST_STORE_CHANGED:
+ Field 'ACTION_TRUST_STORE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_START_EUICC_ACTIVATION:
+ Field 'ACTION_START_EUICC_ACTIVATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech#ACTION_TTS_QUEUE_PROCESSING_COMPLETED:
+ Field 'ACTION_TTS_QUEUE_PROCESSING_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech.Engine#ACTION_TTS_DATA_INSTALLED:
+ Field 'ACTION_TTS_DATA_INSTALLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_REFRESH_SUBSCRIPTION_PLANS:
+ Field 'ACTION_REFRESH_SUBSCRIPTION_PLANS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_SUBSCRIPTION_PLANS_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_PLANS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+ Field 'ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_PCO_VALUE:
+ Field 'ACTION_CARRIER_SIGNAL_PCO_VALUE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REDIRECTED:
+ Field 'ACTION_CARRIER_SIGNAL_REDIRECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+ Field 'ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_RESET:
+ Field 'ACTION_CARRIER_SIGNAL_RESET' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALLBACK_MODE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALLBACK_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALL_STATE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALL_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SECRET_CODE:
+ Field 'ACTION_SECRET_CODE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_APPLICATION_STATE_CHANGED:
+ Field 'ACTION_SIM_APPLICATION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_CARD_STATE_CHANGED:
+ Field 'ACTION_SIM_CARD_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_SLOT_STATUS_CHANGED:
+ Field 'ACTION_SIM_SLOT_STATUS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
+ Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_OTA_STATUS_CHANGED:
+ Field 'ACTION_OTA_STATUS_CHANGED' is missing @BroadcastBehavior
+
+
+DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
+ Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
+ Method android.app.Activity.enterPictureInPictureMode(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#startAllocCounting():
+ Method android.app.Instrumentation.startAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#stopAllocCounting():
+ Method android.app.Instrumentation.stopAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#bigContentView:
+ Field Notification.bigContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#contentView:
+ Field Notification.contentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#headsUpContentView:
+ Field Notification.headsUpContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#tickerView:
+ Field Notification.tickerView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.Builder#Builder(int, CharSequence, android.app.PendingIntent):
+ Constructor android.app.Notification.Action.Builder.Builder(int, CharSequence, android.app.PendingIntent): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getCancelLabel():
+ Method android.app.Notification.Action.WearableExtender.getCancelLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getConfirmLabel():
+ Method android.app.Notification.Action.WearableExtender.getConfirmLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getInProgressLabel():
+ Method android.app.Notification.Action.WearableExtender.getInProgressLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setCancelLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setCancelLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setConfirmLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setConfirmLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setInProgressLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setInProgressLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setContent(android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setContent(android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setTicker(CharSequence, android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setTicker(CharSequence, android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIcon():
+ Method android.app.Notification.WearableExtender.getContentIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIconGravity():
+ Method android.app.Notification.WearableExtender.getContentIconGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomContentHeight():
+ Method android.app.Notification.WearableExtender.getCustomContentHeight(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomSizePreset():
+ Method android.app.Notification.WearableExtender.getCustomSizePreset(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getGravity():
+ Method android.app.Notification.WearableExtender.getGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintAvoidBackgroundClipping():
+ Method android.app.Notification.WearableExtender.getHintAvoidBackgroundClipping(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintHideIcon():
+ Method android.app.Notification.WearableExtender.getHintHideIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintScreenTimeout():
+ Method android.app.Notification.WearableExtender.getHintScreenTimeout(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintShowBackgroundOnly():
+ Method android.app.Notification.WearableExtender.getHintShowBackgroundOnly(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIcon(int):
+ Method android.app.Notification.WearableExtender.setContentIcon(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIconGravity(int):
+ Method android.app.Notification.WearableExtender.setContentIconGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomContentHeight(int):
+ Method android.app.Notification.WearableExtender.setCustomContentHeight(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomSizePreset(int):
+ Method android.app.Notification.WearableExtender.setCustomSizePreset(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setGravity(int):
+ Method android.app.Notification.WearableExtender.setGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintAvoidBackgroundClipping(boolean):
+ Method android.app.Notification.WearableExtender.setHintAvoidBackgroundClipping(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintHideIcon(boolean):
+ Method android.app.Notification.WearableExtender.setHintHideIcon(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintScreenTimeout(int):
+ Method android.app.Notification.WearableExtender.setHintScreenTimeout(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintShowBackgroundOnly(boolean):
+ Method android.app.Notification.WearableExtender.setHintShowBackgroundOnly(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.backup.BackupManager#selectBackupTransport(String):
+ Method android.app.backup.BackupManager.selectBackupTransport(String): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND:
+ Field Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#WIFI_RTT_SERVICE:
+ Field Context.WIFI_RTT_SERVICE: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.ComposeShader#ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode):
+ Constructor android.graphics.ComposeShader.ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#A_8:
+ Field PixelFormat.A_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#LA_88:
+ Field PixelFormat.LA_88: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#L_8:
+ Field PixelFormat.L_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_4444:
+ Field PixelFormat.RGBA_4444: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_5551:
+ Field PixelFormat.RGBA_5551: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGB_332:
+ Field PixelFormat.RGB_332: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.hardware.hdmi.HdmiControlManager#RESULT_ALREADY_IN_PROGRESS:
+ Field HdmiControlManager.RESULT_ALREADY_IN_PROGRESS: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setCodeRate(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setCodeRate(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setModulation(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setModulation(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.EGL14#eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int):
+ Method android.opengl.EGL14.eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLES20#GL_STENCIL_INDEX:
+ Field GLES20.GL_STENCIL_INDEX: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLSurfaceView#surfaceRedrawNeeded(android.view.SurfaceHolder):
+ Method android.opengl.GLSurfaceView.surfaceRedrawNeeded(android.view.SurfaceHolder): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle, android.os.UserHandle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle, android.os.UserHandle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.provider.Contacts.People#markAsContacted(android.content.ContentResolver, long):
+ Method android.provider.Contacts.People.markAsContacted(android.content.ContentResolver, long): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_X:
+ Field Type.CubemapFace.POSITVE_X: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Y:
+ Field Type.CubemapFace.POSITVE_Y: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Z:
+ Field Type.CubemapFace.POSITVE_Z: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.speech.tts.TextToSpeech#areDefaultsEnforced():
+ Method android.speech.tts.TextToSpeech.areDefaultsEnforced(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle):
+ Constructor android.telecom.StatusHints.StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIcon(android.content.Context):
+ Method android.telecom.StatusHints.getIcon(android.content.Context): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIconResId():
+ Method android.telecom.StatusHints.getIconResId(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getPackageName():
+ Method android.telecom.StatusHints.getPackageName(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.PhoneStateListener#PhoneStateListener(java.util.concurrent.Executor):
+ Constructor android.telephony.PhoneStateListener.PhoneStateListener(java.util.concurrent.Executor): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionManager#PROFILE_CLASS_DEFAULT:
+ Field SubscriptionManager.PROFILE_CLASS_DEFAULT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringDaily(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringMonthly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringWeekly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_12HOUR:
+ Field DateUtils.FORMAT_12HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_24HOUR:
+ Field DateUtils.FORMAT_24HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_AMPM:
+ Field DateUtils.FORMAT_CAP_AMPM: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON:
+ Field DateUtils.FORMAT_CAP_NOON: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_NO_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_NO_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.ViewGroup.LayoutParams#FILL_PARENT:
+ Field ViewGroup.LayoutParams.FILL_PARENT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.Window#setTitleColor(int):
+ Method android.view.Window.setTitleColor(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.accessibility.AccessibilityEvent#MAX_TEXT_LENGTH:
+ Field AccessibilityEvent.MAX_TEXT_LENGTH: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebSettings#getSaveFormData():
+ Method android.webkit.WebSettings.getSaveFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebView#shouldDelayChildPressedState():
+ Method android.webkit.WebView.shouldDelayChildPressedState(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#clearFormData():
+ Method android.webkit.WebViewDatabase.clearFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#hasFormData():
+ Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
+ Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -19,12 +525,16 @@
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmissionUpdates(android.telephony.satellite.SatelliteTransmissionUpdateCallback, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>) parameter #2:
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
+
+
MissingGetterMatchingBuilder: android.service.voice.HotwordTrainingData.Builder#addTrainingAudio(android.service.voice.HotwordTrainingAudio):
android.service.voice.HotwordTrainingData does not declare a `getTrainingAudios()` method matching method android.service.voice.HotwordTrainingData.Builder.addTrainingAudio(android.service.voice.HotwordTrainingAudio)
MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
+
+
MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
@@ -57,6 +567,1196 @@
Protected methods not allowed; must be public: method android.service.notification.NotificationAssistantService.attachBaseContext(android.content.Context)}
+RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+ Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+ Method 'hasFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ Method 'addOnUidImportanceListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getHistoricalProcessExitReasons(String, int, int):
+ Method 'getHistoricalProcessExitReasons' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getProcessesInErrorState():
+ Method 'getProcessesInErrorState' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#killProcessesWhenImperceptible(int[], String):
+ Method 'killProcessesWhenImperceptible' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#setDeviceLocales(android.os.LocaleList):
+ Method 'setDeviceLocales' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent):
+ Method 'setAlarmClock' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExact(int, long, android.app.PendingIntent):
+ Method 'setExact' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent):
+ Method 'setExactAndAllowWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setTime(long):
+ Method 'setTime' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#isOpActive(String, int, String):
+ Method 'isOpActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingActive(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpActiveChangedListener):
+ Method 'startWatchingActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingNoted(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpNotedListener):
+ Method 'startWatchingNoted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationInExternalPublicDir(String, String):
+ Method 'setDestinationInExternalPublicDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationUri(android.net.Uri):
+ Method 'setDestinationUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setNotificationVisibility(int):
+ Method 'setNotificationVisibility' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setShowRunningNotification(boolean):
+ Method 'setShowRunningNotification' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.GameManager#setGameMode(String, int):
+ Method 'setGameMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.GameManager#updateCustomGameModeConfiguration(String, android.app.GameModeConfiguration):
+ Method 'updateCustomGameModeConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.LocaleManager#getApplicationLocales(String):
+ Method 'getApplicationLocales' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.Notification.Builder#setFullScreenIntent(android.app.PendingIntent, boolean):
+ Method 'setFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.NotificationManager#canUseFullScreenIntent():
+ Method 'canUseFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.Service#startForeground(int, android.app.Notification):
+ Method 'startForeground' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.StatusBarManager#canLaunchCaptureContentActivityForNote(android.app.Activity):
+ Method 'canLaunchCaptureContentActivityForNote' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#releaseProjection(int):
+ Method 'releaseProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#requestProjection(int):
+ Method 'requestProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperInfo#getSettingsSliceUri():
+ Method 'getSettingsSliceUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clear():
+ Method 'clear' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clearWallpaper(int, int):
+ Method 'clearWallpaper' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable():
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable(int):
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable():
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable(int):
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperFile(int):
+ Method 'getWallpaperFile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperInfo(int):
+ Method 'getWallpaperInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable():
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable(int):
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable():
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable(int):
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setDisplayPadding(android.graphics.Rect):
+ Method 'setDisplayPadding' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setResource(int):
+ Method 'setResource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream, android.graphics.Rect, boolean):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setWallpaperComponentWithFlags(android.content.ComponentName, int):
+ Method 'setWallpaperComponentWithFlags' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#suggestDesiredDimensions(int, int):
+ Method 'suggestDesiredDimensions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'addCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName):
+ Method 'addPersistentPreferredActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindDeviceAdminServiceAsUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearPackagePersistentPreferredActivities(android.content.ComponentName, String):
+ Method 'clearPackagePersistentPreferredActivities' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearResetPasswordToken(android.content.ComponentName):
+ Method 'clearResetPasswordToken' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#createAndProvisionManagedProfile(android.app.admin.ManagedProfileProvisioningParams):
+ Method 'createAndProvisionManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#finalizeWorkProfileProvisioning(android.os.UserHandle, android.accounts.Account):
+ Method 'finalizeWorkProfileProvisioning' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#generateKeyPair(android.content.ComponentName, String, android.security.keystore.KeyGenParameterSpec, int):
+ Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
+ Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
+ Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
+ Method 'getLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskPackages(android.content.ComponentName):
+ Method 'getLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy():
+ Method 'getNearbyAppStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy():
+ Method 'getNearbyNotificationStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getOrganizationName(android.content.ComponentName):
+ Method 'getOrganizationName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getPasswordComplexity():
+ Method 'getPasswordComplexity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getShortSupportMessage(android.content.ComponentName):
+ Method 'getShortSupportMessage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getUserControlDisabledPackages(android.content.ComponentName):
+ Method 'getUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#hasKeyPair(String):
+ Method 'hasKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, String):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, boolean):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, int):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isDeviceProvisioningConfigApplied():
+ Method 'isDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isPackageSuspended(android.content.ComponentName, String):
+ Method 'isPackageSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isResetPasswordTokenActive(android.content.ComponentName):
+ Method 'isResetPasswordTokenActive' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#lockNow(int):
+ Method 'lockNow' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#provisionFullyManagedDevice(android.app.admin.FullyManagedDeviceProvisioningParams):
+ Method 'provisionFullyManagedDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#removeCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'removeCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#sendLostModeLocationUpdate(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'sendLostModeLocationUpdate' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setActiveProfileOwner(android.content.ComponentName, String):
+ Method 'setActiveProfileOwner' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
+ Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
+ Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
+ Method 'setDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
+ Method 'setLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, String[]):
+ Method 'setLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setPermittedInputMethods' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUninstallBlocked(android.content.ComponentName, String, boolean):
+ Method 'setUninstallBlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUserControlDisabledPackages(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int, CharSequence):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeDevice(int):
+ Method 'wipeDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicyChanged(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicyChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicySetResult(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicySetResult' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#dataChanged(String):
+ Method 'dataChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int):
+ Method 'requestBackup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#setFrameworkSchedulingEnabled(boolean):
+ Method 'setFrameworkSchedulingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, CharSequence):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, String):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetwork(android.net.NetworkRequest):
+ Method 'setRequiredNetwork' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetworkType(int):
+ Method 'setRequiredNetworkType' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setUserInitiated(boolean):
+ Method 'setUserInitiated' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.job.JobParameters#isUserInitiatedJob():
+ Method 'isUserInitiatedJob' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobScheduler#canRunUserInitiatedJobs():
+ Method 'canRunUserInitiatedJobs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.people.PeopleManager#isConversation(String, String):
+ Method 'isConversation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryExternalStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryExternalStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForPackage(java.util.UUID, String, android.os.UserHandle):
+ Method 'queryStatsForPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUid(java.util.UUID, int):
+ Method 'queryStatsForUid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootClassName():
+ Method 'getTaskRootClassName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
+ Method 'getTaskRootPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBucket(String):
+ Method 'getAppStandbyBucket' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBuckets():
+ Method 'getAppStandbyBuckets' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#isAppInactive(String):
+ Method 'isAppInactive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#onCarrierPrivilegedAppsChanged():
+ Method 'onCarrierPrivilegedAppsChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryAndAggregateUsageStats(long, long):
+ Method 'queryAndAggregateUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryConfigurations(int, long, long):
+ Method 'queryConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEventStats(int, long, long):
+ Method 'queryEventStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEvents(long, long):
+ Method 'queryEvents' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryUsageStats(int, long, long):
+ Method 'queryUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageLimitObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent):
+ Method 'registerAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageObserver(int, String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent):
+ Method 'registerAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerUsageSessionObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'registerUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String, long):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageLimitObserver(int):
+ Method 'unregisterAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageObserver(int):
+ Method 'unregisterAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterUsageSessionObserver(int):
+ Method 'unregisterUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle):
+ Method 'bindAppWidgetIdIfAllowed' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle):
+ Method 'isDeviceAssociatedForWifiConnection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#startObservingDevicePresence(String):
+ Method 'startObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#stopObservingDevicePresence(String):
+ Method 'stopObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int, android.companion.virtual.VirtualDeviceParams):
+ Method 'createVirtualDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceParams.Builder#setLockState(int):
+ Method 'setLockState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.ContentResolver#addPeriodicSync(android.accounts.Account, String, android.os.Bundle, long):
+ Method 'addPeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#cancelSync(android.content.SyncRequest):
+ Method 'cancelSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSync():
+ Method 'getCurrentSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSyncs():
+ Method 'getCurrentSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getIsSyncable(android.accounts.Account, String):
+ Method 'getIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getMasterSyncAutomatically():
+ Method 'getMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getPeriodicSyncs(android.accounts.Account, String):
+ Method 'getPeriodicSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getSyncAutomatically(android.accounts.Account, String):
+ Method 'getSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncActive(android.accounts.Account, String):
+ Method 'isSyncActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncPending(android.accounts.Account, String):
+ Method 'isSyncPending' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#removePeriodicSync(android.accounts.Account, String, android.os.Bundle):
+ Method 'removePeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setIsSyncable(android.accounts.Account, String, int):
+ Method 'setIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setMasterSyncAutomatically(boolean):
+ Method 'setMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setSyncAutomatically(android.accounts.Account, String, boolean):
+ Method 'setSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindServiceAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#clearWallpaper():
+ Method 'clearWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDir():
+ Method 'getExternalCacheDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDirs():
+ Method 'getExternalCacheDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDir(String):
+ Method 'getExternalFilesDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDirs(String):
+ Method 'getExternalFilesDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalMediaDirs():
+ Method 'getExternalMediaDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDir():
+ Method 'getObbDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDirs():
+ Method 'getObbDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+ Method 'removeStickyBroadcastAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(android.graphics.Bitmap):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(java.io.InputStream):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#canRequestInteractAcrossProfiles():
+ Method 'canRequestInteractAcrossProfiles' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#getAllPackageInstallerSessions():
+ Method 'getAllPackageInstallerSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#registerPackageInstallerSessionCallback(java.util.concurrent.Executor, android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerPackageInstallerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps.Callback#onPackagesSuspended(String[], android.os.UserHandle, android.os.Bundle):
+ Method 'onPackagesSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getAllSessions():
+ Method 'getAllSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getSessionInfo(int):
+ Method 'getSessionInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getStagedSessions():
+ Method 'getStagedSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.Session#requestUserPreapproval(android.content.pm.PackageInstaller.PreapprovalDetails, android.content.IntentSender):
+ Method 'requestUserPreapproval' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setInstallerPackageName(String):
+ Method 'setInstallerPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setPermissionState(String, int):
+ Method 'setPermissionState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean):
+ Method 'setRequestUpdateOwnership' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int):
+ Method 'setRequireUserAction' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#canRequestPackageInstalls():
+ Method 'canRequestPackageInstalls' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getSuspendedPackageAppExtras():
+ Method 'getSuspendedPackageAppExtras' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getUnsuspendablePackages(String[]):
+ Method 'getUnsuspendablePackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#grantRuntimePermission(String, String, android.os.UserHandle):
+ Method 'grantRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isAutoRevokeWhitelisted(String):
+ Method 'isAutoRevokeWhitelisted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isPackageSuspended():
+ Method 'isPackageSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setDistractingPackageRestrictions(String[], int):
+ Method 'setDistractingPackageRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo, int):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#verifyIntentFilter(int, int, java.util.List<java.lang.String>):
+ Method 'verifyIntentFilter' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getHighestDirectReportRateLevel():
+ Method 'getHighestDirectReportRateLevel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getMinDelay():
+ Method 'getMinDelay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraCharacteristics#getKeysNeedingPermission():
+ Method 'getKeysNeedingPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraClosed(String):
+ Method 'onCameraClosed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraOpened(String, String):
+ Method 'onCameraOpened' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#getHdmiCecVersion():
+ Method 'getHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#setHdmiCecVersion(int):
+ Method 'setHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback):
+ Method 'addGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#getMonitoringTypes():
+ Method 'getMonitoringTypes' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#pauseGeofence(int, int):
+ Method 'pauseGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'registerForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#removeGeofence(int, int):
+ Method 'removeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#resumeGeofence(int, int, int):
+ Method 'resumeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'unregisterForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#grantPermission(android.hardware.usb.UsbDevice, String):
+ Method 'grantPermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#hasPermission(android.hardware.usb.UsbDevice):
+ Method 'hasPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent):
+ Method 'requestPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioAttributes.Builder#setHapticChannelsMuted(boolean):
+ Method 'setHapticChannelsMuted' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallDownlinkExtractionAudioRecord(android.media.AudioFormat):
+ Method 'getCallDownlinkExtractionAudioRecord' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallUplinkInjectionAudioTrack(android.media.AudioFormat):
+ Method 'getCallUplinkInjectionAudioTrack' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#registerAudioPolicy(android.media.audiopolicy.AudioPolicy):
+ Method 'registerAudioPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecord#shareAudioHistory(String, long):
+ Method 'shareAudioHistory' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecordingConfiguration#getClientUid():
+ Method 'getClientUid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaCodec#createByCodecNameForClient(String, int, int):
+ Method 'createByCodecNameForClient' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaPlayer#setWakeMode(android.content.Context, int):
+ Method 'setWakeMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaRouter2#getInstance(android.content.Context, String):
+ Method 'getInstance' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getCursor():
+ Method 'getCursor' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getValidRingtoneUri(android.content.Context):
+ Method 'getValidRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.audiofx.HapticGenerator#setEnabled(boolean):
+ Method 'setEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjection#createVirtualDisplay(String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler):
+ Method 'createVirtualDisplay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjectionManager#getMediaProjection(int, android.content.Intent):
+ Method 'getMediaProjection' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnMediaKeyEventSessionChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener):
+ Method 'addOnMediaKeyEventSessionChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessions(android.content.ComponentName):
+ Method 'getActiveSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSession():
+ Method 'getMediaKeyEventSession' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSessionPackageName():
+ Method 'getMediaKeyEventSessionPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo):
+ Method 'isTrustedForMediaControl' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#deleteKeyphraseSoundModel(int, java.util.Locale):
+ Method 'deleteKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#getKeyphraseSoundModel(int, java.util.Locale):
+ Method 'getKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#updateKeyphraseSoundModel(android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel):
+ Method 'updateKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#clearScores():
+ Method 'clearScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#disableScoring():
+ Method 'disableScoring' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#getActiveScorerPackage():
+ Method 'getActiveScorerPackage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#registerNetworkScoreCallback(int, int, java.util.concurrent.Executor, android.net.NetworkScoreManager.NetworkScoreCallback):
+ Method 'registerNetworkScoreCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#requestScores(java.util.Collection<android.net.NetworkKey>):
+ Method 'requestScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#setActiveScorer(String):
+ Method 'setActiveScorer' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.VpnService#prepareAndAuthorize(android.content.Context):
+ Method 'prepareAndAuthorize' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean):
+ Method 'setSpeakerMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#startAudio():
+ Method 'startAudio' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#addVcnNetworkPolicyChangeListener(java.util.concurrent.Executor, android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'addVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#applyVcnNetworkPolicy(android.net.NetworkCapabilities, android.net.LinkProperties):
+ Method 'applyVcnNetworkPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#removeVcnNetworkPolicyChangeListener(android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'removeVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity):
+ Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]):
+ Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String):
+ Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String):
+ Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String):
+ Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]):
+ Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]):
+ Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int):
+ Method 'decrement' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int):
+ Method 'increment' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int):
+ Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#restore(int):
+ Method 'restore' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transfer(int):
+ Method 'transfer' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]):
+ Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int):
+ Method 'readPages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]):
+ Method 'writePage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#getNdefMessage():
+ Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#isWritable():
+ Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#makeReadOnly():
+ Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage):
+ Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage):
+ Method 'format' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage):
+ Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#close():
+ Method 'close' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#connect():
+ Method 'connect' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#cancelBugreport():
+ Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#preDumpUiData():
+ Method 'preDumpUiData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Build#getSerial():
+ Method 'getSerial' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Debug#dumpService(String, java.io.FileDescriptor, String[]):
+ Method 'dumpService' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.DropBoxManager#getNextEntry(String, long):
+ Method 'getNextEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Environment#getExternalStorageDirectory():
+ Method 'getExternalStorageDirectory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager():
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager(java.io.File):
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#dream(long):
+ Method 'dream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#forceSuspend():
+ Method 'forceSuspend' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#getPowerSaveModeTrigger():
+ Method 'getPowerSaveModeTrigger' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#newWakeLock(int, String):
+ Method 'newWakeLock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#reboot(String):
+ Method 'reboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setBatteryDischargePrediction(java.time.Duration, boolean):
+ Method 'setBatteryDischargePrediction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setDynamicPowerSaveHint(boolean, int):
+ Method 'setDynamicPowerSaveHint' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#userActivity(long, int, int):
+ Method 'userActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.RecoverySystem#rebootWipeUserData(android.content.Context):
+ Method 'rebootWipeUserData' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.StrictMode.VmPolicy.Builder#detectFileUriExposure():
+ Method 'detectFileUriExposure' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#retrieveSystemUpdateInfo():
+ Method 'retrieveSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#updateSystemUpdateInfo(android.os.PersistableBundle):
+ Method 'updateSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserProperties(android.os.UserHandle):
+ Method 'getUserProperties' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserRestrictions(android.os.UserHandle):
+ Method 'getUserRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#hasUserRestrictionForUser(String, android.os.UserHandle):
+ Method 'hasUserRestrictionForUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isManagedProfile(int):
+ Method 'isManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile():
+ Method 'isRestrictedProfile' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile(android.os.UserHandle):
+ Method 'isRestrictedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlocked(android.os.UserHandle):
+ Method 'isUserUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlockingOrUnlocked(android.os.UserHandle):
+ Method 'isUserUnlockingOrUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle):
+ Method 'requestQuietModeEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#setUserRestriction(String, boolean):
+ Method 'setUserRestriction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshot(int):
+ Method 'takeUidSnapshot' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshots(int[]):
+ Method 'takeUidSnapshots' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getManageSpaceActivityIntent(String, int):
+ Method 'getManageSpaceActivityIntent' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.storage.StorageVolume#createAccessIntent(String):
+ Method 'createAccessIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDeliveryFromDataSource(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDeliveryFromDataSource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForStartDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForStartDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.provider.Settings#canDrawOverlays(android.content.Context):
+ Method 'canDrawOverlays' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.provider.Settings.System#canWrite(android.content.Context):
+ Method 'canWrite' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.security.KeyChain#removeCredentialManagementApp(android.content.Context):
+ Method 'removeCredentialManagementApp' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginCreateCredentialResponse.Builder#setRemoteCreateEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCreateEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginGetCredentialResponse.Builder#setRemoteCredentialEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCredentialEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.CallingAppInfo#getOrigin():
+ Method 'getOrigin' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactDisplayName():
+ Method 'getContactDisplayName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactPhotoUri():
+ Method 'getContactPhotoUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle):
+ Method 'acceptHandover' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingCall' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingConference(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingConference' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getLine1Number(android.telecom.PhoneAccountHandle):
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getOwnSelfManagedPhoneAccounts():
+ Method 'getOwnSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getPhoneAccount(android.telecom.PhoneAccountHandle):
+ Method 'getPhoneAccount' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getSelfManagedPhoneAccounts():
+ Method 'getSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#hasManageOngoingCallsPermission():
+ Method 'hasManageOngoingCallsPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#placeCall(android.net.Uri, android.os.Bundle):
+ Method 'placeCall' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#showInCallScreen(boolean):
+ Method 'showInCallScreen' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#silenceRinger():
+ Method 'silenceRinger' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig():
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig(java.lang.String...):
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigByComponentForSubId(String, int):
+ Method 'getConfigByComponentForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int, java.lang.String...):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#notifyConfigChangedForSubId(int):
+ Method 'notifyConfigChangedForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CellLocation#requestLocationUpdate():
+ Method 'requestLocationUpdate' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.NetworkRegistrationInfo#getCellIdentity():
+ Method 'getCellIdentity' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallStateChanged(int, String):
+ Method 'onCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onDisplayInfoChanged(android.telephony.TelephonyDisplayInfo):
+ Method 'onDisplayInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onRadioPowerStateChanged(int):
+ Method 'onRadioPowerStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaNetworkId():
+ Method 'getCdmaNetworkId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaSystemId():
+ Method 'getCdmaSystemId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaLong():
+ Method 'getOperatorAlphaLong' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaShort():
+ Method 'getOperatorAlphaShort' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorNumeric():
+ Method 'getOperatorNumeric' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SignalStrengthUpdateRequest.Builder#setSystemThresholdReportingRequestedWhileIdle(boolean):
+ Method 'setSystemThresholdReportingRequestedWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#disableCellBroadcastRange(int, int, int):
+ Method 'disableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#enableCellBroadcastRange(int, int, int):
+ Method 'enableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#getSmscAddress():
+ Method 'getSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#injectSmsPdu(byte[], String, android.app.PendingIntent):
+ Method 'injectSmsPdu' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendDataMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#setSmscAddress(String):
+ Method 'setSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#addSubscriptionsIntoGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'addSubscriptionsIntoGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#createSubscriptionGroup(java.util.List<java.lang.Integer>):
+ Method 'createSubscriptionGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfo(int):
+ Method 'getActiveSubscriptionInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoForSimSlotIndex(int):
+ Method 'getActiveSubscriptionInfoForSimSlotIndex' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoList():
+ Method 'getActiveSubscriptionInfoList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList():
+ Method 'getAvailableSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getCompleteActiveSubscriptionInfoList():
+ Method 'getCompleteActiveSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getOpportunisticSubscriptions():
+ Method 'getOpportunisticSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getResourcesForSubId(android.content.Context, int):
+ Method 'getResourcesForSubId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getSubscriptionsInGroup(android.os.ParcelUuid):
+ Method 'getSubscriptionsInGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#removeSubscriptionsFromGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'removeSubscriptionsFromGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh():
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh(int):
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setOpportunistic(boolean, int):
+ Method 'setOpportunistic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setPreferredDataSubscriptionId(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredDataSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.BarringInfoListener#onBarringInfoChanged(android.telephony.BarringInfo):
+ Method 'onBarringInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallStatesChanged(java.util.List<android.telephony.CallState>):
+ Method 'onCallStatesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallForwardingIndicatorListener#onCallForwardingIndicatorChanged(boolean):
+ Method 'onCallForwardingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.DataEnabledListener#onDataEnabledChanged(boolean, int):
+ Method 'onDataEnabledChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.EmergencyNumberListListener#onEmergencyNumberListChanged(java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>):
+ Method 'onEmergencyNumberListChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(android.telephony.ims.ImsReasonInfo):
+ Method 'onImsCallDisconnectCauseChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged(boolean):
+ Method 'onMessageWaitingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PhysicalChannelConfigListener#onPhysicalChannelConfigChanged(java.util.List<android.telephony.PhysicalChannelConfig>):
+ Method 'onPhysicalChannelConfigChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(android.telephony.PreciseCallState):
+ Method 'onPreciseCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.RegistrationFailedListener#onRegistrationFailed(android.telephony.CellIdentity, String, int, int, int):
+ Method 'onRegistrationFailed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ServiceStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#bootstrapAuthenticationRequest(int, android.net.Uri, android.telephony.gba.UaSecurityProtocolIdentifier, boolean, java.util.concurrent.Executor, android.telephony.TelephonyManager.BootstrapAuthenticationCallback):
+ Method 'bootstrapAuthenticationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#changeIccLockPin(String, String):
+ Method 'changeIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearRadioPowerOffForReason(int):
+ Method 'clearRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'clearSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#doesSwitchMultiSimConfigTriggerReboot():
+ Method 'doesSwitchMultiSimConfigTriggerReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#enableModemForSlot(int, boolean):
+ Method 'enableModemForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAidForAppType(int):
+ Method 'getAidForAppType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypes():
+ Method 'getAllowedNetworkTypes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesBitmask():
+ Method 'getAllowedNetworkTypesBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesForReason(int):
+ Method 'getAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallStateForSubscription():
+ Method 'getCallStateForSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierConfig():
+ Method 'getCarrierConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierInfoForImsiEncryption(int):
+ Method 'getCarrierInfoForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierRestrictionStatus(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getCarrierRestrictionStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaRoamingMode():
+ Method 'getCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaSubscriptionMode():
+ Method 'getCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataActivationState():
+ Method 'getDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataNetworkType():
+ Method 'getDataNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId():
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId(int):
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceSoftwareVersion(int):
+ Method 'getDeviceSoftwareVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberDbVersion():
+ Method 'getEmergencyNumberDbVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList():
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList(int):
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEquivalentHomePlmns():
+ Method 'getEquivalentHomePlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getForbiddenPlmns():
+ Method 'getForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getGroupIdLevel1():
+ Method 'getGroupIdLevel1' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getImei(int):
+ Method 'getImei' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getIsimDomain():
+ Method 'getIsimDomain' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getLine1Number():
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getManualNetworkSelectionPlmn():
+ Method 'getManualNetworkSelectionPlmn' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid():
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid(int):
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNai():
+ Method 'getNai' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSelectionMode():
+ Method 'getNetworkSelectionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSlicingConfiguration(java.util.concurrent.Executor, android.os.OutcomeReceiver<android.telephony.data.NetworkSlicingConfig,android.telephony.TelephonyManager.NetworkSlicingException>):
+ Method 'getNetworkSlicingConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPhoneAccountHandle():
+ Method 'getPhoneAccountHandle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredNetworkTypeBitmask():
+ Method 'getPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredOpportunisticDataSubscription():
+ Method 'getPreferredOpportunisticDataSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerOffReasons():
+ Method 'getRadioPowerOffReasons' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerState():
+ Method 'getRadioPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState():
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState(int):
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimLocale():
+ Method 'getSimLocale' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimSerialNumber():
+ Method 'getSimSerialNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSubscriberId():
+ Method 'getSubscriberId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSupportedRadioAccessFamily():
+ Method 'getSupportedRadioAccessFamily' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSystemSelectionChannels():
+ Method 'getSystemSelectionChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getTelephonyHistograms():
+ Method 'getTelephonyHistograms' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVisualVoicemailPackageName():
+ Method 'getVisualVoicemailPackageName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceActivationState():
+ Method 'getVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailAlphaTag():
+ Method 'getVoiceMailAlphaTag' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailNumber():
+ Method 'getVoiceMailNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceNetworkType():
+ Method 'getVoiceNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannel(int):
+ Method 'iccCloseLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannelBySlot(int, int):
+ Method 'iccCloseLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccExchangeSimIO(int, int, int, int, int, String):
+ Method 'iccExchangeSimIO' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String, int):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannelBySlot(int, String, int):
+ Method 'iccOpenLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannel(int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isApplicationOnUicc(int):
+ Method 'isApplicationOnUicc' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabled():
+ Method 'isDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabledForReason(int):
+ Method 'isDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataRoamingEnabled():
+ Method 'isDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isEmergencyAssistanceEnabled():
+ Method 'isEmergencyAssistanceEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isIccLockEnabled():
+ Method 'isIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isManualNetworkSelectionAllowed():
+ Method 'isManualNetworkSelectionAllowed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isModemEnabledForSlot(int):
+ Method 'isModemEnabledForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isMultiSimSupported():
+ Method 'isMultiSimSupported' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isOpportunisticNetworkEnabled():
+ Method 'isOpportunisticNetworkEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPotentialEmergencyNumber(String):
+ Method 'isPotentialEmergencyNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPremiumCapabilityAvailableForPurchase(int):
+ Method 'isPremiumCapabilityAvailableForPurchase' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isTetheringApnRequired():
+ Method 'isTetheringApnRequired' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#notifyOtaEmergencyNumberDbInstalled():
+ Method 'notifyOtaEmergencyNumberDbInstalled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#prepareForUnattendedReboot():
+ Method 'prepareForUnattendedReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#purchasePremiumCapability(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'purchasePremiumCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootModem():
+ Method 'rebootModem' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootRadio():
+ Method 'rebootRadio' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#reportDefaultNetworkStatus(boolean):
+ Method 'reportDefaultNetworkStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(int, android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback):
+ Method 'requestNumberVerification' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestRadioPowerOffForReason(int):
+ Method 'requestRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetAllCarrierActions():
+ Method 'resetAllCarrierActions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetCarrierKeysForImsiEncryption():
+ Method 'resetCarrierKeysForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetOtaEmergencyNumberDbFilePath():
+ Method 'resetOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetRadioConfig():
+ Method 'resetRadioConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendEnvelopeWithStatus(String):
+ Method 'sendEnvelopeWithStatus' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendThermalMitigationRequest(android.telephony.ThermalMitigationRequest):
+ Method 'sendThermalMitigationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler):
+ Method 'sendUssdRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendVisualVoicemailSms(String, int, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>):
+ Method 'setAllowedCarriers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedNetworkTypesForReason(int, long):
+ Method 'setAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierDataEnabled(boolean):
+ Method 'setCarrierDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierRestrictionRules(android.telephony.CarrierRestrictionRules):
+ Method 'setCarrierRestrictionRules' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaRoamingMode(int):
+ Method 'setCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaSubscriptionMode(int):
+ Method 'setCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataActivationState(int):
+ Method 'setDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabled(boolean):
+ Method 'setDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabledForReason(int, boolean):
+ Method 'setDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataRoamingEnabled(boolean):
+ Method 'setDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setForbiddenPlmns(java.util.List<java.lang.String>):
+ Method 'setForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setIccLockEnabled(boolean, String):
+ Method 'setIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeAutomatic():
+ Method 'setNetworkSelectionModeAutomatic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean, int):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setOpportunisticNetworkState(boolean):
+ Method 'setOpportunisticNetworkState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredNetworkTypeBitmask(long):
+ Method 'setPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredOpportunisticDataSubscription' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setRadioEnabled(boolean):
+ Method 'setRadioEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'setSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoiceActivationState(int):
+ Method 'setVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri):
+ Method 'setVoicemailRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean):
+ Method 'setVoicemailVibrationEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPin(String):
+ Method 'supplyIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPuk(String, String):
+ Method 'supplyIccLockPuk' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#switchMultiSimConfig(int):
+ Method 'switchMultiSimConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'updateAvailableNetworks' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateOtaEmergencyNumberDbFilePath(android.os.ParcelFileDescriptor):
+ Method 'updateOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings):
+ Method 'setSmsFilterSettings' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#continueOperation(android.content.Intent, android.os.Bundle):
+ Method 'continueOperation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#deleteSubscription(int, android.app.PendingIntent):
+ Method 'deleteSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent):
+ Method 'downloadSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(int, android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList(android.app.PendingIntent):
+ Method 'getDefaultDownloadableSubscriptionList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent):
+ Method 'getDownloadableSubscriptionMetadata' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getOtaStatus():
+ Method 'getOtaStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getSupportedCountries():
+ Method 'getSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getUnsupportedCountries():
+ Method 'getUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setSupportedCountries(java.util.List<java.lang.String>):
+ Method 'setSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setUnsupportedCountries(java.util.List<java.lang.String>):
+ Method 'setUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#updateSubscriptionNickname(int, String, android.app.PendingIntent):
+ Method 'updateSubscriptionNickname' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int):
+ Method 'createForSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getVoWiFiModeSetting():
+ Method 'getVoWiFiModeSetting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isAdvancedCallingSettingEnabled():
+ Method 'isAdvancedCallingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isCrossSimCallingEnabled():
+ Method 'isCrossSimCallingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isTtyOverVolteEnabled():
+ Method 'isTtyOverVolteEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiRoamingSettingEnabled():
+ Method 'isVoWiFiRoamingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiSettingEnabled():
+ Method 'isVoWiFiSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVtSettingEnabled():
+ Method 'isVtSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'registerMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'unregisterMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationState(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getProvisioningStatusForCapability(int, int):
+ Method 'getProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getRcsProvisioningStatusForCapability(int, int):
+ Method 'getRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isProvisioningRequiredForCapability(int, int):
+ Method 'isProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsProvisioningRequiredForCapability(int, int):
+ Method 'isRcsProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable():
+ Method 'isRcsVolteSingleRegistrationCapable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#notifyRcsAutoConfigurationReceived(byte[], boolean):
+ Method 'notifyRcsAutoConfigurationReceived' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerFeatureProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback):
+ Method 'registerFeatureProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback):
+ Method 'registerProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'registerRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setProvisioningStatusForCapability(int, int, boolean):
+ Method 'setProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback):
+ Method 'unregisterProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback(android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'unregisterRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#registerSipDialogStateCallback(java.util.concurrent.Executor, android.telephony.ims.SipDialogStateCallback):
+ Method 'registerSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#unregisterSipDialogStateCallback(android.telephony.ims.SipDialogStateCallback):
+ Method 'unregisterSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.WindowManager.LayoutParams#isSystemApplicationOverlay():
+ Method 'isSystemApplicationOverlay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#registerDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'registerDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#unregisterDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'unregisterDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningEnabled(boolean):
+ Method 'setSystemAudioCaptioningEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningUiEnabled(boolean):
+ Method 'setSystemAudioCaptioningUiEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype):
+ Method 'setCurrentInputMethodSubtype' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setBlockNetworkLoads(boolean):
+ Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
+ Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
+
+
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SAM-compatible parameters (such as parameter 6, "callback", in android.accounts.AccountManager.addAccount) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
@@ -201,6 +1901,80 @@
SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SdkConstant: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.pm.PackageInstaller#ACTION_CONFIRM_PRE_APPROVAL:
+ Field 'ACTION_CONFIRM_PRE_APPROVAL' is missing @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+
+
+Todo: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#READ_PEOPLE_DATA:
+ Documentation mentions 'TODO'
+Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
+ Documentation mentions 'TODO'
+Todo: android.hardware.camera2.params.StreamConfigurationMap:
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#loadNanoApp(int, android.hardware.location.NanoApp):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#unloadNanoApp(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.NanoAppInstanceInfo:
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvContentRatingSystemInfo#getXmlUri():
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvInputInfo#isConnectedToHdmiSwitch():
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#prepareForUnattendedUpdate(android.content.Context, String, android.content.IntentSender):
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#rebootAndApply(android.content.Context, String, String):
+ Documentation mentions 'TODO'
+Todo: android.os.SystemConfigManager:
+ Documentation mentions 'TODO'
+Todo: android.os.UpdateEngineCallback#onStatusUpdate(int, float):
+ Documentation mentions 'TODO'
+Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
+ Documentation mentions 'TODO'
+Todo: android.service.voice.AlwaysOnHotwordDetector:
+ Documentation mentions 'TODO'
+Todo: android.telephony.TelephonyManager#getCurrentPhoneType():
+ Documentation mentions 'TODO'
+
+
UnflaggedApi: android.Manifest.permission#ACCESS_SMARTSPACE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESS_SMARTSPACE
UnflaggedApi: android.Manifest.permission#ALWAYS_UPDATE_WALLPAPER:
@@ -325,10 +2099,10 @@
New API must be flagged with @FlaggedApi: field android.service.voice.HotwordTrainingData.TIMEOUT_STAGE_VERY_EARLY
UnflaggedApi: android.service.voice.HotwordTrainingData#describeContents():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.describeContents()
-UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize():
- New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize()
UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataBytes():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataBytes()
+UnflaggedApi: android.service.voice.HotwordTrainingData#getMaxTrainingDataSize():
+ New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getMaxTrainingDataSize()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTimeoutStage():
New API must be flagged with @FlaggedApi: method android.service.voice.HotwordTrainingData.getTimeoutStage()
UnflaggedApi: android.service.voice.HotwordTrainingData#getTrainingAudioList():
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index aa17df3..402a718 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -15,7 +15,7 @@
method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String);
}
- public static final class Notification.TvExtender implements android.app.Notification.Extender {
+ @FlaggedApi("android.app.api_tvextender") public static final class Notification.TvExtender implements android.app.Notification.Extender {
method @Deprecated public String getChannel();
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a3ebe6e..6643663 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -575,7 +575,6 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, int);
method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setOverrideKeepProfilesRunning(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean triggerDevicePolicyEngineMigration(boolean);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
@@ -1802,8 +1801,8 @@
public class AudioManager {
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
- method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
- method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
+ method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
+ method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceComputeCsdOnAllDevices(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceUseFrameworkMel(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -1811,9 +1810,9 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getCsd();
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
- method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
- method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
- method @FlaggedApi("com.android.media.audio.flags.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
+ method @FlaggedApi("android.media.audio.focus_freeze_test_api") @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
+ method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
+ method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
method public static final int[] getPublicStreamTypes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
@@ -1935,7 +1934,7 @@
field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
}
- public static final class RingtoneSelection.Builder {
+ @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public static final class RingtoneSelection.Builder {
ctor public RingtoneSelection.Builder();
ctor public RingtoneSelection.Builder(@NonNull android.media.RingtoneSelection);
method @NonNull public android.media.RingtoneSelection build();
@@ -3573,8 +3572,8 @@
method public default void holdLock(android.os.IBinder, int);
method public default boolean isGlobalKey(int);
method public default boolean isTaskSnapshotSupported();
- method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
- method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
method public default void setDisplayImePolicy(int, int);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 105e764..559db51 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1,4 +1,512 @@
// Baseline format: 1.0
+BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
+ Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED:
+ Field 'ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.NotificationManager#ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL:
+ Field 'ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED:
+ Field 'ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_MANAGED_PROFILE_PROVISIONED:
+ Field 'ACTION_MANAGED_PROFILE_PROVISIONED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_AIRPLANE_MODE_CHANGED:
+ Field 'ACTION_AIRPLANE_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_LOCALE_CHANGED:
+ Field 'ACTION_APPLICATION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED:
+ Field 'ACTION_APPLICATION_RESTRICTIONS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_CHANGED:
+ Field 'ACTION_BATTERY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LOW:
+ Field 'ACTION_BATTERY_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_OKAY:
+ Field 'ACTION_BATTERY_OKAY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CAMERA_BUTTON:
+ Field 'ACTION_CAMERA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS:
+ Field 'ACTION_CLOSE_SYSTEM_DIALOGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_CONFIGURATION_CHANGED:
+ Field 'ACTION_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DATE_CHANGED:
+ Field 'ACTION_DATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_LOW:
+ Field 'ACTION_DEVICE_STORAGE_LOW' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_OK:
+ Field 'ACTION_DEVICE_STORAGE_OK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOCK_EVENT:
+ Field 'ACTION_DOCK_EVENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DOMAINS_NEED_VERIFICATION:
+ Field 'ACTION_DOMAINS_NEED_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STARTED:
+ Field 'ACTION_DREAMING_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STOPPED:
+ Field 'ACTION_DREAMING_STOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+ Field 'ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_CONNECTED:
+ Field 'ACTION_GTALK_SERVICE_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_DISCONNECTED:
+ Field 'ACTION_GTALK_SERVICE_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INPUT_METHOD_CHANGED:
+ Field 'ACTION_INPUT_METHOD_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE:
+ Field 'ACTION_INSTALL_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS:
+ Field 'ACTION_INSTANT_APP_RESOLVER_SETTINGS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION:
+ Field 'ACTION_INTENT_FILTER_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOAD_DATA:
+ Field 'ACTION_LOAD_DATA' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCALE_CHANGED:
+ Field 'ACTION_LOCALE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED:
+ Field 'ACTION_LOCKED_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MANAGE_PACKAGE_STORAGE:
+ Field 'ACTION_MANAGE_PACKAGE_STORAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BAD_REMOVAL:
+ Field 'ACTION_MEDIA_BAD_REMOVAL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BUTTON:
+ Field 'ACTION_MEDIA_BUTTON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_CHECKING:
+ Field 'ACTION_MEDIA_CHECKING' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_EJECT:
+ Field 'ACTION_MEDIA_EJECT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_MOUNTED:
+ Field 'ACTION_MEDIA_MOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_NOFS:
+ Field 'ACTION_MEDIA_NOFS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_REMOVED:
+ Field 'ACTION_MEDIA_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_FINISHED:
+ Field 'ACTION_MEDIA_SCANNER_FINISHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_SCAN_FILE:
+ Field 'ACTION_MEDIA_SCANNER_SCAN_FILE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_STARTED:
+ Field 'ACTION_MEDIA_SCANNER_STARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SHARED:
+ Field 'ACTION_MEDIA_SHARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTABLE:
+ Field 'ACTION_MEDIA_UNMOUNTABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTED:
+ Field 'ACTION_MEDIA_UNMOUNTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_REPLACED:
+ Field 'ACTION_MY_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_SUSPENDED:
+ Field 'ACTION_MY_PACKAGE_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_UNSUSPENDED:
+ Field 'ACTION_MY_PACKAGE_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_NEW_OUTGOING_CALL:
+ Field 'ACTION_NEW_OUTGOING_CALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_SUSPENDED:
+ Field 'ACTION_PACKAGES_SUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_UNSUSPENDED:
+ Field 'ACTION_PACKAGES_UNSUSPENDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_ADDED:
+ Field 'ACTION_PACKAGE_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_CHANGED:
+ Field 'ACTION_PACKAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_DATA_CLEARED:
+ Field 'ACTION_PACKAGE_DATA_CLEARED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH:
+ Field 'ACTION_PACKAGE_FIRST_LAUNCH' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FULLY_REMOVED:
+ Field 'ACTION_PACKAGE_FULLY_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_INSTALL:
+ Field 'ACTION_PACKAGE_INSTALL' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_VERIFICATION:
+ Field 'ACTION_PACKAGE_NEEDS_VERIFICATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REMOVED:
+ Field 'ACTION_PACKAGE_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REPLACED:
+ Field 'ACTION_PACKAGE_REPLACED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_RESTARTED:
+ Field 'ACTION_PACKAGE_RESTARTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSTOPPED:
+ Field 'ACTION_PACKAGE_UNSTOPPED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_UNSUSPENDED_MANUALLY:
+ Field 'ACTION_PACKAGE_UNSUSPENDED_MANUALLY' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_VERIFIED:
+ Field 'ACTION_PACKAGE_VERIFIED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_CONNECTED:
+ Field 'ACTION_POWER_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_POWER_DISCONNECTED:
+ Field 'ACTION_POWER_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_PROVIDER_CHANGED:
+ Field 'ACTION_PROVIDER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_QUERY_PACKAGE_RESTART:
+ Field 'ACTION_QUERY_PACKAGE_RESTART' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_REBOOT:
+ Field 'ACTION_REBOOT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_RESOLVE_INSTANT_APP_PACKAGE:
+ Field 'ACTION_RESOLVE_INSTANT_APP_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_ROLLBACK_COMMITTED:
+ Field 'ACTION_ROLLBACK_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_OFF:
+ Field 'ACTION_SCREEN_OFF' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_ON:
+ Field 'ACTION_SCREEN_ON' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS:
+ Field 'ACTION_SHOW_SUSPENDED_APP_DETAILS' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SHUTDOWN:
+ Field 'ACTION_SHUTDOWN' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SIM_STATE_CHANGED:
+ Field 'ACTION_SIM_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_SPLIT_CONFIGURATION_CHANGED:
+ Field 'ACTION_SPLIT_CONFIGURATION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIMEZONE_CHANGED:
+ Field 'ACTION_TIMEZONE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_CHANGED:
+ Field 'ACTION_TIME_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_TIME_TICK:
+ Field 'ACTION_TIME_TICK' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UID_REMOVED:
+ Field 'ACTION_UID_REMOVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_CONNECTED:
+ Field 'ACTION_UMS_CONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UMS_DISCONNECTED:
+ Field 'ACTION_UMS_DISCONNECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_PRESENT:
+ Field 'ACTION_USER_PRESENT' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_USER_UNLOCKED:
+ Field 'ACTION_USER_UNLOCKED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.Intent#ACTION_WALLPAPER_CHANGED:
+ Field 'ACTION_WALLPAPER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_COMMITTED:
+ Field 'ACTION_SESSION_COMMITTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_UPDATED:
+ Field 'ACTION_SESSION_UPDATED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_PICTURE:
+ Field 'ACTION_NEW_PICTURE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_VIDEO:
+ Field 'ACTION_NEW_VIDEO' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.hdmi.HdmiControlManager#ACTION_OSD_MESSAGE:
+ Field 'ACTION_OSD_MESSAGE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS:
+ Field 'ACTION_QUERY_KEYBOARD_LAYOUTS' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_DETACHED:
+ Field 'ACTION_USB_ACCESSORY_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_HANDSHAKE:
+ Field 'ACTION_USB_ACCESSORY_HANDSHAKE' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_DEVICE_DETACHED:
+ Field 'ACTION_USB_DEVICE_DETACHED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HDMI_AUDIO_PLUG:
+ Field 'ACTION_HDMI_AUDIO_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_HEADSET_PLUG:
+ Field 'ACTION_HEADSET_PLUG' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_MICROPHONE_MUTE_CHANGED:
+ Field 'ACTION_MICROPHONE_MUTE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.AudioManager#ACTION_SPEAKERPHONE_STATE_CHANGED:
+ Field 'ACTION_SPEAKERPHONE_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_CHANNEL_BROWSABLE_REQUESTED:
+ Field 'ACTION_CHANNEL_BROWSABLE_REQUESTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_INITIALIZE_PROGRAMS:
+ Field 'ACTION_INITIALIZE_PROGRAMS' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+ Field 'ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+ Field 'ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORER_CHANGED:
+ Field 'ACTION_SCORER_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.NetworkScoreManager#ACTION_SCORE_NETWORKS:
+ Field 'ACTION_SCORE_NETWORKS' is missing @BroadcastBehavior
+BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION:
+ Field 'PROXY_CHANGE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED:
+ Field 'ACTION_ADAPTER_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_PREFERRED_PAYMENT_CHANGED:
+ Field 'ACTION_PREFERRED_PAYMENT_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @BroadcastBehavior
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED:
+ Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED:
+ Field 'ACTION_DROPBOX_ENTRY_ADDED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER:
+ Field 'ACTION_EVENT_REMINDER' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.ContactsContract.SimContacts#ACTION_SIM_ACCOUNTS_CHANGED:
+ Field 'ACTION_SIM_ACCOUNTS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED:
+ Field 'ACTION_SMS_EMERGENCY_CB_RECEIVED' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#DATA_SMS_RECEIVED_ACTION:
+ Field 'DATA_SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION:
+ Field 'SECRET_CODE_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SIM_FULL_ACTION:
+ Field 'SIM_FULL_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION:
+ Field 'SMS_CB_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION:
+ Field 'SMS_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION:
+ Field 'SMS_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_REJECTED_ACTION:
+ Field 'SMS_REJECTED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION:
+ Field 'SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION:
+ Field 'WAP_PUSH_DELIVER_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_RECEIVED_ACTION:
+ Field 'WAP_PUSH_RECEIVED_ACTION' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEYCHAIN_CHANGED:
+ Field 'ACTION_KEYCHAIN_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED:
+ Field 'ACTION_KEY_ACCESS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_STORAGE_CHANGED:
+ Field 'ACTION_STORAGE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.security.KeyChain#ACTION_TRUST_STORE_CHANGED:
+ Field 'ACTION_TRUST_STORE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_START_EUICC_ACTIVATION:
+ Field 'ACTION_START_EUICC_ACTIVATION' is missing @BroadcastBehavior
+BroadcastBehavior: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech#ACTION_TTS_QUEUE_PROCESSING_COMPLETED:
+ Field 'ACTION_TTS_QUEUE_PROCESSING_COMPLETED' is missing @BroadcastBehavior
+BroadcastBehavior: android.speech.tts.TextToSpeech.Engine#ACTION_TTS_DATA_INSTALLED:
+ Field 'ACTION_TTS_DATA_INSTALLED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_REFRESH_SUBSCRIPTION_PLANS:
+ Field 'ACTION_REFRESH_SUBSCRIPTION_PLANS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_SUBSCRIPTION_PLANS_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_PLANS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+ Field 'ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_PCO_VALUE:
+ Field 'ACTION_CARRIER_SIGNAL_PCO_VALUE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REDIRECTED:
+ Field 'ACTION_CARRIER_SIGNAL_REDIRECTED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+ Field 'ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_CARRIER_SIGNAL_RESET:
+ Field 'ACTION_CARRIER_SIGNAL_RESET' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALLBACK_MODE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALLBACK_MODE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_EMERGENCY_CALL_STATE_CHANGED:
+ Field 'ACTION_EMERGENCY_CALL_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SECRET_CODE:
+ Field 'ACTION_SECRET_CODE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_APPLICATION_STATE_CHANGED:
+ Field 'ACTION_SIM_APPLICATION_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_CARD_STATE_CHANGED:
+ Field 'ACTION_SIM_CARD_STATE_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SIM_SLOT_STATUS_CHANGED:
+ Field 'ACTION_SIM_SLOT_STATUS_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED:
+ Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
+ Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_OTA_STATUS_CHANGED:
+ Field 'ACTION_OTA_STATUS_CHANGED' is missing @BroadcastBehavior
+
+
+DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
+ Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
+ Method android.app.Activity.enterPictureInPictureMode(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#startAllocCounting():
+ Method android.app.Instrumentation.startAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Instrumentation#stopAllocCounting():
+ Method android.app.Instrumentation.stopAllocCounting(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#bigContentView:
+ Field Notification.bigContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#contentView:
+ Field Notification.contentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#headsUpContentView:
+ Field Notification.headsUpContentView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification#tickerView:
+ Field Notification.tickerView: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.Builder#Builder(int, CharSequence, android.app.PendingIntent):
+ Constructor android.app.Notification.Action.Builder.Builder(int, CharSequence, android.app.PendingIntent): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getCancelLabel():
+ Method android.app.Notification.Action.WearableExtender.getCancelLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getConfirmLabel():
+ Method android.app.Notification.Action.WearableExtender.getConfirmLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getInProgressLabel():
+ Method android.app.Notification.Action.WearableExtender.getInProgressLabel(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setCancelLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setCancelLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setConfirmLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setConfirmLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setInProgressLabel(CharSequence):
+ Method android.app.Notification.Action.WearableExtender.setInProgressLabel(CharSequence): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setContent(android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setContent(android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.Builder#setTicker(CharSequence, android.widget.RemoteViews):
+ Method android.app.Notification.Builder.setTicker(CharSequence, android.widget.RemoteViews): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIcon():
+ Method android.app.Notification.WearableExtender.getContentIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIconGravity():
+ Method android.app.Notification.WearableExtender.getContentIconGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomContentHeight():
+ Method android.app.Notification.WearableExtender.getCustomContentHeight(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomSizePreset():
+ Method android.app.Notification.WearableExtender.getCustomSizePreset(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getGravity():
+ Method android.app.Notification.WearableExtender.getGravity(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintAvoidBackgroundClipping():
+ Method android.app.Notification.WearableExtender.getHintAvoidBackgroundClipping(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintHideIcon():
+ Method android.app.Notification.WearableExtender.getHintHideIcon(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintScreenTimeout():
+ Method android.app.Notification.WearableExtender.getHintScreenTimeout(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintShowBackgroundOnly():
+ Method android.app.Notification.WearableExtender.getHintShowBackgroundOnly(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIcon(int):
+ Method android.app.Notification.WearableExtender.setContentIcon(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIconGravity(int):
+ Method android.app.Notification.WearableExtender.setContentIconGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomContentHeight(int):
+ Method android.app.Notification.WearableExtender.setCustomContentHeight(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomSizePreset(int):
+ Method android.app.Notification.WearableExtender.setCustomSizePreset(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setGravity(int):
+ Method android.app.Notification.WearableExtender.setGravity(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintAvoidBackgroundClipping(boolean):
+ Method android.app.Notification.WearableExtender.setHintAvoidBackgroundClipping(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintHideIcon(boolean):
+ Method android.app.Notification.WearableExtender.setHintHideIcon(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintScreenTimeout(int):
+ Method android.app.Notification.WearableExtender.setHintScreenTimeout(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintShowBackgroundOnly(boolean):
+ Method android.app.Notification.WearableExtender.setHintShowBackgroundOnly(boolean): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.app.backup.BackupManager#selectBackupTransport(String):
+ Method android.app.backup.BackupManager.selectBackupTransport(String): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND:
+ Field Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#POWER_WHITELIST_MANAGER:
+ Field Context.POWER_WHITELIST_MANAGER: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.content.Context#WIFI_RTT_SERVICE:
+ Field Context.WIFI_RTT_SERVICE: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.ComposeShader#ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode):
+ Constructor android.graphics.ComposeShader.ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#A_8:
+ Field PixelFormat.A_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#LA_88:
+ Field PixelFormat.LA_88: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#L_8:
+ Field PixelFormat.L_8: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_4444:
+ Field PixelFormat.RGBA_4444: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_5551:
+ Field PixelFormat.RGBA_5551: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.graphics.PixelFormat#RGB_332:
+ Field PixelFormat.RGB_332: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.hardware.hdmi.HdmiControlManager#RESULT_ALREADY_IN_PROGRESS:
+ Field HdmiControlManager.RESULT_ALREADY_IN_PROGRESS: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setCodeRate(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setCodeRate(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder#setModulation(int):
+ Method android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder.setModulation(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.EGL14#eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int):
+ Method android.opengl.EGL14.eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLES20#GL_STENCIL_INDEX:
+ Field GLES20.GL_STENCIL_INDEX: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.opengl.GLSurfaceView#surfaceRedrawNeeded(android.view.SurfaceHolder):
+ Method android.opengl.GLSurfaceView.surfaceRedrawNeeded(android.view.SurfaceHolder): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle, android.os.UserHandle):
+ Method android.os.UserManager.setUserRestrictions(android.os.Bundle, android.os.UserHandle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.provider.Contacts.People#markAsContacted(android.content.ContentResolver, long):
+ Method android.provider.Contacts.People.markAsContacted(android.content.ContentResolver, long): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_X:
+ Field Type.CubemapFace.POSITVE_X: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Y:
+ Field Type.CubemapFace.POSITVE_Y: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Z:
+ Field Type.CubemapFace.POSITVE_Z: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.speech.tts.TextToSpeech#areDefaultsEnforced():
+ Method android.speech.tts.TextToSpeech.areDefaultsEnforced(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle):
+ Constructor android.telecom.StatusHints.StatusHints(android.content.ComponentName, CharSequence, int, android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIcon(android.content.Context):
+ Method android.telecom.StatusHints.getIcon(android.content.Context): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getIconResId():
+ Method android.telecom.StatusHints.getIconResId(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telecom.StatusHints#getPackageName():
+ Method android.telecom.StatusHints.getPackageName(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.PhoneStateListener#PhoneStateListener(java.util.concurrent.Executor):
+ Constructor android.telephony.PhoneStateListener.PhoneStateListener(java.util.concurrent.Executor): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionManager#PROFILE_CLASS_DEFAULT:
+ Field SubscriptionManager.PROFILE_CLASS_DEFAULT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringDaily(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringMonthly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime):
+ Method android.telephony.SubscriptionPlan.Builder.createRecurringWeekly(java.time.ZonedDateTime): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_12HOUR:
+ Field DateUtils.FORMAT_12HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_24HOUR:
+ Field DateUtils.FORMAT_24HOUR: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_AMPM:
+ Field DateUtils.FORMAT_CAP_AMPM: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON:
+ Field DateUtils.FORMAT_CAP_NOON: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_CAP_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_NO_NOON_MIDNIGHT:
+ Field DateUtils.FORMAT_NO_NOON_MIDNIGHT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.ViewGroup.LayoutParams#FILL_PARENT:
+ Field ViewGroup.LayoutParams.FILL_PARENT: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.Window#setTitleColor(int):
+ Method android.view.Window.setTitleColor(int): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.view.accessibility.AccessibilityEvent#MAX_TEXT_LENGTH:
+ Field AccessibilityEvent.MAX_TEXT_LENGTH: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebSettings#getSaveFormData():
+ Method android.webkit.WebSettings.getSaveFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebView#shouldDelayChildPressedState():
+ Method android.webkit.WebView.shouldDelayChildPressedState(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#clearFormData():
+ Method android.webkit.WebViewDatabase.clearFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: android.webkit.WebViewDatabase#hasFormData():
+ Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
+ Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+
+
KotlinKeyword: android.app.Notification#when:
Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
@@ -19,6 +527,1254 @@
Protected methods not allowed; must be public: method android.view.ViewGroup.resetResolvedDrawables()}
+RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+ Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+ Method 'hasFeatures' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ Method 'addOnUidImportanceListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getHistoricalProcessExitReasons(String, int, int):
+ Method 'getHistoricalProcessExitReasons' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getProcessesInErrorState():
+ Method 'getProcessesInErrorState' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getUidProcessCapabilities(int):
+ Method 'getUidProcessCapabilities' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#getUidProcessState(int):
+ Method 'getUidProcessState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#killProcessesWhenImperceptible(int[], String):
+ Method 'killProcessesWhenImperceptible' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.ActivityManager#setDeviceLocales(android.os.LocaleList):
+ Method 'setDeviceLocales' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent):
+ Method 'setAlarmClock' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExact(int, long, android.app.PendingIntent):
+ Method 'setExact' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setExactAndAllowWhileIdle(int, long, android.app.PendingIntent):
+ Method 'setExactAndAllowWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AlarmManager#setTime(long):
+ Method 'setTime' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#isOpActive(String, int, String):
+ Method 'isOpActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#isOperationActive(int, int, String):
+ Method 'isOperationActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingActive(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpActiveChangedListener):
+ Method 'startWatchingActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.AppOpsManager#startWatchingNoted(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpNotedListener):
+ Method 'startWatchingNoted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationInExternalPublicDir(String, String):
+ Method 'setDestinationInExternalPublicDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setDestinationUri(android.net.Uri):
+ Method 'setDestinationUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setNotificationVisibility(int):
+ Method 'setNotificationVisibility' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.DownloadManager.Request#setShowRunningNotification(boolean):
+ Method 'setShowRunningNotification' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.GameManager#getGameMode(String):
+ Method 'getGameMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.GameManager#isAngleEnabled(String):
+ Method 'isAngleEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.GameManager#setGameMode(String, int):
+ Method 'setGameMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.GameManager#updateCustomGameModeConfiguration(String, android.app.GameModeConfiguration):
+ Method 'updateCustomGameModeConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.LocaleManager#getApplicationLocales(String):
+ Method 'getApplicationLocales' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.Notification.Builder#setFullScreenIntent(android.app.PendingIntent, boolean):
+ Method 'setFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.NotificationManager#canUseFullScreenIntent():
+ Method 'canUseFullScreenIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.Service#startForeground(int, android.app.Notification):
+ Method 'startForeground' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.StatusBarManager#canLaunchCaptureContentActivityForNote(android.app.Activity):
+ Method 'canLaunchCaptureContentActivityForNote' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.StatusBarManager#collapsePanels():
+ Method 'collapsePanels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#releaseProjection(int):
+ Method 'releaseProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.UiModeManager#requestProjection(int):
+ Method 'requestProjection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperInfo#getSettingsSliceUri():
+ Method 'getSettingsSliceUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clear():
+ Method 'clear' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#clearWallpaper(int, int):
+ Method 'clearWallpaper' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable():
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getDrawable(int):
+ Method 'getDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable():
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getFastDrawable(int):
+ Method 'getFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperFile(int):
+ Method 'getWallpaperFile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#getWallpaperInfo(int):
+ Method 'getWallpaperInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable():
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekDrawable(int):
+ Method 'peekDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable():
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#peekFastDrawable(int):
+ Method 'peekFastDrawable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean):
+ Method 'setBitmap' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setDisplayPadding(android.graphics.Rect):
+ Method 'setDisplayPadding' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setResource(int):
+ Method 'setResource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream, android.graphics.Rect, boolean):
+ Method 'setStream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#setWallpaperComponentWithFlags(android.content.ComponentName, int):
+ Method 'setWallpaperComponentWithFlags' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.WallpaperManager#suggestDesiredDimensions(int, int):
+ Method 'suggestDesiredDimensions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'addCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName):
+ Method 'addPersistentPreferredActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindDeviceAdminServiceAsUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearPackagePersistentPreferredActivities(android.content.ComponentName, String):
+ Method 'clearPackagePersistentPreferredActivities' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#clearResetPasswordToken(android.content.ComponentName):
+ Method 'clearResetPasswordToken' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#createAndProvisionManagedProfile(android.app.admin.ManagedProfileProvisioningParams):
+ Method 'createAndProvisionManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#finalizeWorkProfileProvisioning(android.os.UserHandle, android.accounts.Account):
+ Method 'finalizeWorkProfileProvisioning' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#forceUpdateUserSetupComplete(int):
+ Method 'forceUpdateUserSetupComplete' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#generateKeyPair(android.content.ComponentName, String, android.security.keystore.KeyGenParameterSpec, int):
+ Method 'generateKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getApplicationExemptions(String):
+ Method 'getApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getCrossProfileWidgetProviders(android.content.ComponentName):
+ Method 'getCrossProfileWidgetProviders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskFeatures(android.content.ComponentName):
+ Method 'getLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getLockTaskPackages(android.content.ComponentName):
+ Method 'getLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy():
+ Method 'getNearbyAppStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy():
+ Method 'getNearbyNotificationStreamingPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getOrganizationName(android.content.ComponentName):
+ Method 'getOrganizationName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
+ Method 'getOwnerInstalledCaCerts' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getPasswordComplexity():
+ Method 'getPasswordComplexity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getShortSupportMessage(android.content.ComponentName):
+ Method 'getShortSupportMessage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#getUserControlDisabledPackages(android.content.ComponentName):
+ Method 'getUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#hasKeyPair(String):
+ Method 'hasKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, String):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, boolean):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], String, int):
+ Method 'installKeyPair' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isDeviceProvisioningConfigApplied():
+ Method 'isDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isPackageSuspended(android.content.ComponentName, String):
+ Method 'isPackageSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#isResetPasswordTokenActive(android.content.ComponentName):
+ Method 'isResetPasswordTokenActive' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#lockNow(int):
+ Method 'lockNow' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#provisionFullyManagedDevice(android.app.admin.FullyManagedDeviceProvisioningParams):
+ Method 'provisionFullyManagedDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#removeCrossProfileWidgetProvider(android.content.ComponentName, String):
+ Method 'removeCrossProfileWidgetProvider' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#sendLostModeLocationUpdate(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'sendLostModeLocationUpdate' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setActiveProfileOwner(android.content.ComponentName, String):
+ Method 'setActiveProfileOwner' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
+ Method 'setAlwaysOnVpnPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setApplicationExemptions(String, java.util.Set<java.lang.Integer>):
+ Method 'setApplicationExemptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceOwner(android.content.ComponentName, int):
+ Method 'setDeviceOwner' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setDeviceProvisioningConfigApplied():
+ Method 'setDeviceProvisioningConfigApplied' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskFeatures(android.content.ComponentName, int):
+ Method 'setLockTaskFeatures' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, String[]):
+ Method 'setLockTaskPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setPermittedInputMethods' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUninstallBlocked(android.content.ComponentName, String, boolean):
+ Method 'setUninstallBlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#setUserControlDisabledPackages(android.content.ComponentName, java.util.List<java.lang.String>):
+ Method 'setUserControlDisabledPackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeData(int, CharSequence):
+ Method 'wipeData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.DevicePolicyManager#wipeDevice(int):
+ Method 'wipeDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicyChanged(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicyChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.admin.PolicyUpdateReceiver#onPolicySetResult(android.content.Context, String, android.os.Bundle, android.app.admin.TargetUser, android.app.admin.PolicyUpdateResult):
+ Method 'onPolicySetResult' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#dataChanged(String):
+ Method 'dataChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int):
+ Method 'requestBackup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#setFrameworkSchedulingEnabled(boolean):
+ Method 'setFrameworkSchedulingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, CharSequence):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.BackupManager#updateTransportAttributes(android.content.ComponentName, String, android.content.Intent, String, android.content.Intent, String):
+ Method 'updateTransportAttributes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restoreAll(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restoreAll' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackage(String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.backup.RestoreSession#restorePackages(long, android.app.backup.RestoreObserver, java.util.Set<java.lang.String>, android.app.backup.BackupManagerMonitor):
+ Method 'restorePackages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetwork(android.net.NetworkRequest):
+ Method 'setRequiredNetwork' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setRequiredNetworkType(int):
+ Method 'setRequiredNetworkType' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobInfo.Builder#setUserInitiated(boolean):
+ Method 'setUserInitiated' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.job.JobParameters#isUserInitiatedJob():
+ Method 'isUserInitiatedJob' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.job.JobScheduler#canRunUserInitiatedJobs():
+ Method 'canRunUserInitiatedJobs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.people.PeopleManager#isConversation(String, String):
+ Method 'isConversation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryExternalStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryExternalStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForPackage(java.util.UUID, String, android.os.UserHandle):
+ Method 'queryStatsForPackage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUid(java.util.UUID, int):
+ Method 'queryStatsForUid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUser(java.util.UUID, android.os.UserHandle):
+ Method 'queryStatsForUser' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootClassName():
+ Method 'getTaskRootClassName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageEvents.Event#getTaskRootPackageName():
+ Method 'getTaskRootPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBucket(String):
+ Method 'getAppStandbyBucket' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#getAppStandbyBuckets():
+ Method 'getAppStandbyBuckets' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#isAppInactive(String):
+ Method 'isAppInactive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#onCarrierPrivilegedAppsChanged():
+ Method 'onCarrierPrivilegedAppsChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryAndAggregateUsageStats(long, long):
+ Method 'queryAndAggregateUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryConfigurations(int, long, long):
+ Method 'queryConfigurations' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEventStats(int, long, long):
+ Method 'queryEventStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryEvents(long, long):
+ Method 'queryEvents' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#queryUsageStats(int, long, long):
+ Method 'queryUsageStats' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageLimitObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent):
+ Method 'registerAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerAppUsageObserver(int, String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent):
+ Method 'registerAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#registerUsageSessionObserver(int, String[], java.time.Duration, java.time.Duration, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'registerUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#reportUsageStart(android.app.Activity, String, long):
+ Method 'reportUsageStart' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageLimitObserver(int):
+ Method 'unregisterAppUsageLimitObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterAppUsageObserver(int):
+ Method 'unregisterAppUsageObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.usage.UsageStatsManager#unregisterUsageSessionObserver(int):
+ Method 'unregisterUsageSessionObserver' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle):
+ Method 'bindAppWidgetIdIfAllowed' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle):
+ Method 'isDeviceAssociatedForWifiConnection' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#startObservingDevicePresence(String):
+ Method 'startObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.CompanionDeviceManager#stopObservingDevicePresence(String):
+ Method 'stopObservingDevicePresence' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceManager#createVirtualDevice(int, android.companion.virtual.VirtualDeviceParams):
+ Method 'createVirtualDevice' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.companion.virtual.VirtualDeviceParams.Builder#setLockState(int):
+ Method 'setLockState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.ContentResolver#addPeriodicSync(android.accounts.Account, String, android.os.Bundle, long):
+ Method 'addPeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#cancelSync(android.content.SyncRequest):
+ Method 'cancelSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSync():
+ Method 'getCurrentSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getCurrentSyncs():
+ Method 'getCurrentSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getIsSyncable(android.accounts.Account, String):
+ Method 'getIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getMasterSyncAutomatically():
+ Method 'getMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getPeriodicSyncs(android.accounts.Account, String):
+ Method 'getPeriodicSyncs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#getSyncAutomatically(android.accounts.Account, String):
+ Method 'getSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncActive(android.accounts.Account, String):
+ Method 'isSyncActive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#isSyncPending(android.accounts.Account, String):
+ Method 'isSyncPending' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#removePeriodicSync(android.accounts.Account, String, android.os.Bundle):
+ Method 'removePeriodicSync' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setIsSyncable(android.accounts.Account, String, int):
+ Method 'setIsSyncable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setMasterSyncAutomatically(boolean):
+ Method 'setMasterSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.ContentResolver#setSyncAutomatically(android.accounts.Account, String, boolean):
+ Method 'setSyncAutomatically' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+ Method 'bindServiceAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#clearWallpaper():
+ Method 'clearWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDir():
+ Method 'getExternalCacheDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalCacheDirs():
+ Method 'getExternalCacheDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDir(String):
+ Method 'getExternalFilesDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalFilesDirs(String):
+ Method 'getExternalFilesDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getExternalMediaDirs():
+ Method 'getExternalMediaDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDir():
+ Method 'getObbDir' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#getObbDirs():
+ Method 'getObbDirs' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+ Method 'removeStickyBroadcastAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(android.graphics.Bitmap):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.Context#setWallpaper(java.io.InputStream):
+ Method 'setWallpaper' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#canRequestInteractAcrossProfiles():
+ Method 'canRequestInteractAcrossProfiles' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.ComponentName, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.CrossProfileApps#startActivity(android.content.Intent, android.os.UserHandle, android.app.Activity, android.os.Bundle):
+ Method 'startActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#getAllPackageInstallerSessions():
+ Method 'getAllPackageInstallerSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps#registerPackageInstallerSessionCallback(java.util.concurrent.Executor, android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerPackageInstallerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.LauncherApps.Callback#onPackagesSuspended(String[], android.os.UserHandle, android.os.Bundle):
+ Method 'onPackagesSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getAllSessions():
+ Method 'getAllSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getSessionInfo(int):
+ Method 'getSessionInfo' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#getStagedSessions():
+ Method 'getStagedSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller#registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback):
+ Method 'registerSessionCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.Session#requestUserPreapproval(android.content.pm.PackageInstaller.PreapprovalDetails, android.content.IntentSender):
+ Method 'requestUserPreapproval' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setInstallerPackageName(String):
+ Method 'setInstallerPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setPermissionState(String, int):
+ Method 'setPermissionState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean):
+ Method 'setRequestUpdateOwnership' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int):
+ Method 'setRequireUserAction' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#canRequestPackageInstalls():
+ Method 'canRequestPackageInstalls' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getSuspendedPackageAppExtras():
+ Method 'getSuspendedPackageAppExtras' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#getUnsuspendablePackages(String[]):
+ Method 'getUnsuspendablePackages' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#grantRuntimePermission(String, String, android.os.UserHandle):
+ Method 'grantRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isAutoRevokeWhitelisted(String):
+ Method 'isAutoRevokeWhitelisted' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#isPackageSuspended():
+ Method 'isPackageSuspended' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String):
+ Method 'revokeRuntimePermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setDistractingPackageRestrictions(String[], int):
+ Method 'setDistractingPackageRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo, int):
+ Method 'setPackagesSuspended' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.content.pm.PackageManager#verifyIntentFilter(int, int, java.util.List<java.lang.String>):
+ Method 'verifyIntentFilter' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getHighestDirectReportRateLevel():
+ Method 'getHighestDirectReportRateLevel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.Sensor#getMinDelay():
+ Method 'getMinDelay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraCharacteristics#getKeysNeedingPermission():
+ Method 'getKeysNeedingPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraClosed(String):
+ Method 'onCameraClosed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.camera2.CameraManager.AvailabilityCallback#onCameraOpened(String, String):
+ Method 'onCameraOpened' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.devicestate.DeviceStateManager#cancelBaseStateOverride():
+ Method 'cancelBaseStateOverride' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.devicestate.DeviceStateManager#cancelStateRequest():
+ Method 'cancelStateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.devicestate.DeviceStateManager#requestBaseStateOverride(android.hardware.devicestate.DeviceStateRequest, java.util.concurrent.Executor, android.hardware.devicestate.DeviceStateRequest.Callback):
+ Method 'requestBaseStateOverride' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.devicestate.DeviceStateManager#requestState(android.hardware.devicestate.DeviceStateRequest, java.util.concurrent.Executor, android.hardware.devicestate.DeviceStateRequest.Callback):
+ Method 'requestState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#getHdmiCecVersion():
+ Method 'getHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.hdmi.HdmiControlManager#setHdmiCecVersion(int):
+ Method 'setHdmiCecVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#addUniqueIdAssociation(String, String):
+ Method 'addUniqueIdAssociation' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.input.InputManager#removeUniqueIdAssociation(String):
+ Method 'removeUniqueIdAssociation' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback):
+ Method 'addGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#getMonitoringTypes():
+ Method 'getMonitoringTypes' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#pauseGeofence(int, int):
+ Method 'pauseGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'registerForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#removeGeofence(int, int):
+ Method 'removeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#resumeGeofence(int, int, int):
+ Method 'resumeGeofence' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.location.GeofenceHardware#unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareMonitorCallback):
+ Method 'unregisterForMonitorStateChangeCallback' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#grantPermission(android.hardware.usb.UsbDevice, String):
+ Method 'grantPermission' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#hasPermission(android.hardware.usb.UsbDevice):
+ Method 'hasPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent):
+ Method 'requestPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioAttributes.Builder#setHapticChannelsMuted(boolean):
+ Method 'setHapticChannelsMuted' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallDownlinkExtractionAudioRecord(android.media.AudioFormat):
+ Method 'getCallDownlinkExtractionAudioRecord' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#getCallUplinkInjectionAudioTrack(android.media.AudioFormat):
+ Method 'getCallUplinkInjectionAudioTrack' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioManager#registerAudioPolicy(android.media.audiopolicy.AudioPolicy):
+ Method 'registerAudioPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecord#shareAudioHistory(String, long):
+ Method 'shareAudioHistory' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.AudioRecordingConfiguration#getClientUid():
+ Method 'getClientUid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaCodec#createByCodecNameForClient(String, int, int):
+ Method 'createByCodecNameForClient' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaExtractor#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>):
+ Method 'setDataSource' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaPlayer#setWakeMode(android.content.Context, int):
+ Method 'setWakeMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.MediaRouter2#getInstance(android.content.Context, String):
+ Method 'getInstance' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getCursor():
+ Method 'getCursor' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.RingtoneManager#getValidRingtoneUri(android.content.Context):
+ Method 'getValidRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.audiofx.HapticGenerator#setEnabled(boolean):
+ Method 'setEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjection#createVirtualDisplay(String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler):
+ Method 'createVirtualDisplay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.projection.MediaProjectionManager#getMediaProjection(int, android.content.Intent):
+ Method 'getMediaProjection' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+ Method 'addOnActiveSessionsChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#addOnMediaKeyEventSessionChangedListener(java.util.concurrent.Executor, android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener):
+ Method 'addOnMediaKeyEventSessionChangedListener' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessions(android.content.ComponentName):
+ Method 'getActiveSessions' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSession():
+ Method 'getMediaKeyEventSession' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#getMediaKeyEventSessionPackageName():
+ Method 'getMediaKeyEventSessionPackageName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.session.MediaSessionManager#isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo):
+ Method 'isTrustedForMediaControl' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#deleteKeyphraseSoundModel(int, java.util.Locale):
+ Method 'deleteKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#getKeyphraseSoundModel(int, java.util.Locale):
+ Method 'getKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.media.voice.KeyphraseModelManager#updateKeyphraseSoundModel(android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel):
+ Method 'updateKeyphraseSoundModel' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#clearScores():
+ Method 'clearScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#disableScoring():
+ Method 'disableScoring' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#getActiveScorerPackage():
+ Method 'getActiveScorerPackage' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#registerNetworkScoreCallback(int, int, java.util.concurrent.Executor, android.net.NetworkScoreManager.NetworkScoreCallback):
+ Method 'registerNetworkScoreCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#requestScores(java.util.Collection<android.net.NetworkKey>):
+ Method 'requestScores' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.NetworkScoreManager#setActiveScorer(String):
+ Method 'setActiveScorer' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.VpnService#prepareAndAuthorize(android.content.Context):
+ Method 'prepareAndAuthorize' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean):
+ Method 'setSpeakerMode' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.sip.SipAudioCall#startAudio():
+ Method 'startAudio' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#addVcnNetworkPolicyChangeListener(java.util.concurrent.Executor, android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'addVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#applyVcnNetworkPolicy(android.net.NetworkCapabilities, android.net.LinkProperties):
+ Method 'applyVcnNetworkPolicy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.net.vcn.VcnManager#removeVcnNetworkPolicyChangeListener(android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener):
+ Method 'removeVcnNetworkPolicyChangeListener' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity):
+ Method 'disableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]):
+ Method 'enableForegroundDispatch' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String):
+ Method 'isDefaultServiceForAid' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String):
+ Method 'isDefaultServiceForCategory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String):
+ Method 'setOffHostForService' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]):
+ Method 'authenticateSectorWithKeyA' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]):
+ Method 'authenticateSectorWithKeyB' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int):
+ Method 'decrement' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int):
+ Method 'increment' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int):
+ Method 'readBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#restore(int):
+ Method 'restore' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#transfer(int):
+ Method 'transfer' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]):
+ Method 'writeBlock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int):
+ Method 'readPages' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]):
+ Method 'writePage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#getNdefMessage():
+ Method 'getNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#isWritable():
+ Method 'isWritable' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#makeReadOnly():
+ Method 'makeReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage):
+ Method 'writeNdefMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage):
+ Method 'format' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage):
+ Method 'formatReadOnly' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#getTimeout():
+ Method 'getTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#setTimeout(int):
+ Method 'setTimeout' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]):
+ Method 'transceive' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#close():
+ Method 'close' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.nfc.tech.TagTechnology#connect():
+ Method 'connect' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#cancelBugreport():
+ Method 'cancelBugreport' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.BugreportManager#preDumpUiData():
+ Method 'preDumpUiData' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Build#getSerial():
+ Method 'getSerial' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Debug#dumpService(String, java.io.FileDescriptor, String[]):
+ Method 'dumpService' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.DropBoxManager#getNextEntry(String, long):
+ Method 'getNextEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.Environment#getExternalStorageDirectory():
+ Method 'getExternalStorageDirectory' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager():
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.Environment#isExternalStorageManager(java.io.File):
+ Method 'isExternalStorageManager' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#dream(long):
+ Method 'dream' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#forceSuspend():
+ Method 'forceSuspend' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#getPowerSaveModeTrigger():
+ Method 'getPowerSaveModeTrigger' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#newWakeLock(int, String):
+ Method 'newWakeLock' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.PowerManager#reboot(String):
+ Method 'reboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setBatteryDischargePrediction(java.time.Duration, boolean):
+ Method 'setBatteryDischargePrediction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#setDynamicPowerSaveHint(boolean, int):
+ Method 'setDynamicPowerSaveHint' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.PowerManager#userActivity(long, int, int):
+ Method 'userActivity' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.RecoverySystem#rebootWipeUserData(android.content.Context):
+ Method 'rebootWipeUserData' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.StrictMode.VmPolicy.Builder#detectFileUriExposure():
+ Method 'detectFileUriExposure' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#retrieveSystemUpdateInfo():
+ Method 'retrieveSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.SystemUpdateManager#updateSystemUpdateInfo(android.os.PersistableBundle):
+ Method 'updateSystemUpdateInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#createUser(String, String, int):
+ Method 'createUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserProperties(android.os.UserHandle):
+ Method 'getUserProperties' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#getUserRestrictions(android.os.UserHandle):
+ Method 'getUserRestrictions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#hasUserRestrictionForUser(String, android.os.UserHandle):
+ Method 'hasUserRestrictionForUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isManagedProfile(int):
+ Method 'isManagedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile():
+ Method 'isRestrictedProfile' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.UserManager#isRestrictedProfile(android.os.UserHandle):
+ Method 'isRestrictedProfile' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlocked(android.os.UserHandle):
+ Method 'isUserUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#isUserUnlockingOrUnlocked(android.os.UserHandle):
+ Method 'isUserUnlockingOrUnlocked' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#preCreateUser(String):
+ Method 'preCreateUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#requestQuietModeEnabled(boolean, android.os.UserHandle):
+ Method 'requestQuietModeEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.UserManager#setUserRestriction(String, boolean):
+ Method 'setUserRestriction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshot(int):
+ Method 'takeUidSnapshot' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshots(int[]):
+ Method 'takeUidSnapshots' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getCloudMediaProvider():
+ Method 'getCloudMediaProvider' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#getManageSpaceActivityIntent(String, int):
+ Method 'getManageSpaceActivityIntent' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.os.storage.StorageManager#isAppIoBlocked(java.util.UUID, int, int, int):
+ Method 'isAppIoBlocked' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.os.storage.StorageVolume#createAccessIntent(String):
+ Method 'createAccessIntent' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForDataDeliveryFromDataSource(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForDataDeliveryFromDataSource' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.permission.PermissionManager#checkPermissionForStartDataDelivery(String, android.content.AttributionSource, String):
+ Method 'checkPermissionForStartDataDelivery' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.provider.Settings#canDrawOverlays(android.content.Context):
+ Method 'canDrawOverlays' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.provider.Settings.System#canWrite(android.content.Context):
+ Method 'canWrite' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.security.KeyChain#removeCredentialManagementApp(android.content.Context):
+ Method 'removeCredentialManagementApp' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginCreateCredentialResponse.Builder#setRemoteCreateEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCreateEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.BeginGetCredentialResponse.Builder#setRemoteCredentialEntry(android.service.credentials.RemoteEntry):
+ Method 'setRemoteCredentialEntry' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.service.credentials.CallingAppInfo#getOrigin():
+ Method 'getOrigin' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactDisplayName():
+ Method 'getContactDisplayName' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.Call.Details#getContactPhotoUri():
+ Method 'getContactPhotoUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle):
+ Method 'acceptHandover' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingCall' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingConference(android.telecom.PhoneAccountHandle, android.os.Bundle):
+ Method 'addNewIncomingConference' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getLine1Number(android.telecom.PhoneAccountHandle):
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getOwnSelfManagedPhoneAccounts():
+ Method 'getOwnSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getPhoneAccount(android.telecom.PhoneAccountHandle):
+ Method 'getPhoneAccount' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#getSelfManagedPhoneAccounts():
+ Method 'getSelfManagedPhoneAccounts' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#hasManageOngoingCallsPermission():
+ Method 'hasManageOngoingCallsPermission' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#placeCall(android.net.Uri, android.os.Bundle):
+ Method 'placeCall' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#showInCallScreen(boolean):
+ Method 'showInCallScreen' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telecom.TelecomManager#silenceRinger():
+ Method 'silenceRinger' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig():
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig(java.lang.String...):
+ Method 'getConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigByComponentForSubId(String, int):
+ Method 'getConfigByComponentForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int, java.lang.String...):
+ Method 'getConfigForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CarrierConfigManager#notifyConfigChangedForSubId(int):
+ Method 'notifyConfigChangedForSubId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.CellLocation#requestLocationUpdate():
+ Method 'requestLocationUpdate' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.NetworkRegistrationInfo#getCellIdentity():
+ Method 'getCellIdentity' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onCallStateChanged(int, String):
+ Method 'onCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onDisplayInfoChanged(android.telephony.TelephonyDisplayInfo):
+ Method 'onDisplayInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onRadioPowerStateChanged(int):
+ Method 'onRadioPowerStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.PhoneStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaNetworkId():
+ Method 'getCdmaNetworkId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getCdmaSystemId():
+ Method 'getCdmaSystemId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaLong():
+ Method 'getOperatorAlphaLong' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorAlphaShort():
+ Method 'getOperatorAlphaShort' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ServiceState#getOperatorNumeric():
+ Method 'getOperatorNumeric' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SignalStrengthUpdateRequest.Builder#setSystemThresholdReportingRequestedWhileIdle(boolean):
+ Method 'setSystemThresholdReportingRequestedWhileIdle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#disableCellBroadcastRange(int, int, int):
+ Method 'disableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#enableCellBroadcastRange(int, int, int):
+ Method 'enableCellBroadcastRange' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#getSmscAddress():
+ Method 'getSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#injectSmsPdu(byte[], String, android.app.PendingIntent):
+ Method 'injectSmsPdu' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendDataMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>):
+ Method 'sendMultipartTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessage' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+ Method 'sendTextMessageWithoutPersisting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SmsManager#setSmscAddress(String):
+ Method 'setSmscAddress' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#addSubscriptionsIntoGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'addSubscriptionsIntoGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#createSubscriptionGroup(java.util.List<java.lang.Integer>):
+ Method 'createSubscriptionGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfo(int):
+ Method 'getActiveSubscriptionInfo' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoForSimSlotIndex(int):
+ Method 'getActiveSubscriptionInfoForSimSlotIndex' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoList():
+ Method 'getActiveSubscriptionInfoList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList():
+ Method 'getAvailableSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getCompleteActiveSubscriptionInfoList():
+ Method 'getCompleteActiveSubscriptionInfoList' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getOpportunisticSubscriptions():
+ Method 'getOpportunisticSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getResourcesForSubId(android.content.Context, int):
+ Method 'getResourcesForSubId' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#getSubscriptionsInGroup(android.os.ParcelUuid):
+ Method 'getSubscriptionsInGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#removeSubscriptionsFromGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+ Method 'removeSubscriptionsFromGroup' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh():
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#requestEmbeddedSubscriptionInfoListRefresh(int):
+ Method 'requestEmbeddedSubscriptionInfoListRefresh' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setOpportunistic(boolean, int):
+ Method 'setOpportunistic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.SubscriptionManager#setPreferredDataSubscriptionId(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredDataSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged(int):
+ Method 'onActiveDataSubscriptionIdChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.BarringInfoListener#onBarringInfoChanged(android.telephony.BarringInfo):
+ Method 'onBarringInfoChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallAttributesChanged(android.telephony.CallAttributes):
+ Method 'onCallAttributesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallAttributesListener#onCallStatesChanged(java.util.List<android.telephony.CallState>):
+ Method 'onCallStatesChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.CallForwardingIndicatorListener#onCallForwardingIndicatorChanged(boolean):
+ Method 'onCallForwardingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.DataEnabledListener#onDataEnabledChanged(boolean, int):
+ Method 'onDataEnabledChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.EmergencyNumberListListener#onEmergencyNumberListChanged(java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>):
+ Method 'onEmergencyNumberListChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(android.telephony.ims.ImsReasonInfo):
+ Method 'onImsCallDisconnectCauseChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged(boolean):
+ Method 'onMessageWaitingIndicatorChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PhysicalChannelConfigListener#onPhysicalChannelConfigChanged(java.util.List<android.telephony.PhysicalChannelConfig>):
+ Method 'onPhysicalChannelConfigChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(android.telephony.PreciseCallState):
+ Method 'onPreciseCallStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState):
+ Method 'onPreciseDataConnectionStateChanged' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.RegistrationFailedListener#onRegistrationFailed(android.telephony.CellIdentity, String, int, int, int):
+ Method 'onRegistrationFailed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyCallback.ServiceStateListener#onServiceStateChanged(android.telephony.ServiceState):
+ Method 'onServiceStateChanged' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#bootstrapAuthenticationRequest(int, android.net.Uri, android.telephony.gba.UaSecurityProtocolIdentifier, boolean, java.util.concurrent.Executor, android.telephony.TelephonyManager.BootstrapAuthenticationCallback):
+ Method 'bootstrapAuthenticationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#changeIccLockPin(String, String):
+ Method 'changeIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearRadioPowerOffForReason(int):
+ Method 'clearRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#clearSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'clearSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#doesSwitchMultiSimConfigTriggerReboot():
+ Method 'doesSwitchMultiSimConfigTriggerReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#enableModemForSlot(int, boolean):
+ Method 'enableModemForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAidForAppType(int):
+ Method 'getAidForAppType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypes():
+ Method 'getAllowedNetworkTypes' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesBitmask():
+ Method 'getAllowedNetworkTypesBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getAllowedNetworkTypesForReason(int):
+ Method 'getAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallState():
+ Method 'getCallState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCallStateForSubscription():
+ Method 'getCallStateForSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierConfig():
+ Method 'getCarrierConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierIdListVersion():
+ Method 'getCarrierIdListVersion' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierInfoForImsiEncryption(int):
+ Method 'getCarrierInfoForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCarrierRestrictionStatus(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getCarrierRestrictionStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaRoamingMode():
+ Method 'getCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getCdmaSubscriptionMode():
+ Method 'getCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataActivationState():
+ Method 'getDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDataNetworkType():
+ Method 'getDataNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId():
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId(int):
+ Method 'getDeviceId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getDeviceSoftwareVersion(int):
+ Method 'getDeviceSoftwareVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberDbVersion():
+ Method 'getEmergencyNumberDbVersion' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList():
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList(int):
+ Method 'getEmergencyNumberList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getEquivalentHomePlmns():
+ Method 'getEquivalentHomePlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getForbiddenPlmns():
+ Method 'getForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getGroupIdLevel1():
+ Method 'getGroupIdLevel1' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getImei(int):
+ Method 'getImei' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getIsimDomain():
+ Method 'getIsimDomain' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getLine1Number():
+ Method 'getLine1Number' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getManualNetworkSelectionPlmn():
+ Method 'getManualNetworkSelectionPlmn' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid():
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getMeid(int):
+ Method 'getMeid' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNai():
+ Method 'getNai' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSelectionMode():
+ Method 'getNetworkSelectionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getNetworkSlicingConfiguration(java.util.concurrent.Executor, android.os.OutcomeReceiver<android.telephony.data.NetworkSlicingConfig,android.telephony.TelephonyManager.NetworkSlicingException>):
+ Method 'getNetworkSlicingConfiguration' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPhoneAccountHandle():
+ Method 'getPhoneAccountHandle' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredNetworkTypeBitmask():
+ Method 'getPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getPreferredOpportunisticDataSubscription():
+ Method 'getPreferredOpportunisticDataSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerOffReasons():
+ Method 'getRadioPowerOffReasons' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getRadioPowerState():
+ Method 'getRadioPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState():
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getServiceState(int):
+ Method 'getServiceState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimLocale():
+ Method 'getSimLocale' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSimSerialNumber():
+ Method 'getSimSerialNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSubscriberId():
+ Method 'getSubscriberId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSupportedRadioAccessFamily():
+ Method 'getSupportedRadioAccessFamily' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getSystemSelectionChannels():
+ Method 'getSystemSelectionChannels' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getTelephonyHistograms():
+ Method 'getTelephonyHistograms' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVisualVoicemailPackageName():
+ Method 'getVisualVoicemailPackageName' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceActivationState():
+ Method 'getVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailAlphaTag():
+ Method 'getVoiceMailAlphaTag' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailNumber():
+ Method 'getVoiceMailNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#getVoiceNetworkType():
+ Method 'getVoiceNetworkType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannel(int):
+ Method 'iccCloseLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannelBySlot(int, int):
+ Method 'iccCloseLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccExchangeSimIO(int, int, int, int, int, String):
+ Method 'iccExchangeSimIO' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String, int):
+ Method 'iccOpenLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannelBySlot(int, String, int):
+ Method 'iccOpenLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannel(int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduBasicChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannel' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, String):
+ Method 'iccTransmitApduLogicalChannelBySlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isApplicationOnUicc(int):
+ Method 'isApplicationOnUicc' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabled():
+ Method 'isDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabledForReason(int):
+ Method 'isDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDataRoamingEnabled():
+ Method 'isDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isDomainSelectionSupported():
+ Method 'isDomainSelectionSupported' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isEmergencyAssistanceEnabled():
+ Method 'isEmergencyAssistanceEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isIccLockEnabled():
+ Method 'isIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isManualNetworkSelectionAllowed():
+ Method 'isManualNetworkSelectionAllowed' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isModemEnabledForSlot(int):
+ Method 'isModemEnabledForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isMultiSimSupported():
+ Method 'isMultiSimSupported' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isOpportunisticNetworkEnabled():
+ Method 'isOpportunisticNetworkEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPotentialEmergencyNumber(String):
+ Method 'isPotentialEmergencyNumber' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isPremiumCapabilityAvailableForPurchase(int):
+ Method 'isPremiumCapabilityAvailableForPurchase' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#isTetheringApnRequired():
+ Method 'isTetheringApnRequired' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#notifyOtaEmergencyNumberDbInstalled():
+ Method 'notifyOtaEmergencyNumberDbInstalled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#prepareForUnattendedReboot():
+ Method 'prepareForUnattendedReboot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#purchasePremiumCapability(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'purchasePremiumCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootModem():
+ Method 'rebootModem' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#rebootRadio():
+ Method 'rebootRadio' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#reportDefaultNetworkStatus(boolean):
+ Method 'reportDefaultNetworkStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(int, android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+ Method 'requestNetworkScan' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback):
+ Method 'requestNumberVerification' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#requestRadioPowerOffForReason(int):
+ Method 'requestRadioPowerOffForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetAllCarrierActions():
+ Method 'resetAllCarrierActions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetCarrierKeysForImsiEncryption():
+ Method 'resetCarrierKeysForImsiEncryption' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetOtaEmergencyNumberDbFilePath():
+ Method 'resetOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#resetRadioConfig():
+ Method 'resetRadioConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendEnvelopeWithStatus(String):
+ Method 'sendEnvelopeWithStatus' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendThermalMitigationRequest(android.telephony.ThermalMitigationRequest):
+ Method 'sendThermalMitigationRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler):
+ Method 'sendUssdRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#sendVisualVoicemailSms(String, int, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>):
+ Method 'setAllowedCarriers' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setAllowedNetworkTypesForReason(int, long):
+ Method 'setAllowedNetworkTypesForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierDataEnabled(boolean):
+ Method 'setCarrierDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierRestrictionRules(android.telephony.CarrierRestrictionRules):
+ Method 'setCarrierRestrictionRules' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String):
+ Method 'setCarrierTestOverride' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String):
+ Method 'setCarrierTestOverride' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaRoamingMode(int):
+ Method 'setCdmaRoamingMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setCdmaSubscriptionMode(int):
+ Method 'setCdmaSubscriptionMode' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataActivationState(int):
+ Method 'setDataActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabled(boolean):
+ Method 'setDataEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabledForReason(int, boolean):
+ Method 'setDataEnabledForReason' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setDataRoamingEnabled(boolean):
+ Method 'setDataRoamingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setForbiddenPlmns(java.util.List<java.lang.String>):
+ Method 'setForbiddenPlmns' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setIccLockEnabled(boolean, String):
+ Method 'setIccLockEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeAutomatic():
+ Method 'setNetworkSelectionModeAutomatic' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean, int):
+ Method 'setNetworkSelectionModeManual' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setOpportunisticNetworkState(boolean):
+ Method 'setOpportunisticNetworkState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredNetworkTypeBitmask(long):
+ Method 'setPreferredNetworkTypeBitmask' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setPreferredOpportunisticDataSubscription' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setRadioEnabled(boolean):
+ Method 'setRadioEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSignalStrengthUpdateRequest(android.telephony.SignalStrengthUpdateRequest):
+ Method 'setSignalStrengthUpdateRequest' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerState(int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setSimPowerStateForSlot(int, int, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'setSimPowerStateForSlot' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoiceActivationState(int):
+ Method 'setVoiceActivationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri):
+ Method 'setVoicemailRingtoneUri' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean):
+ Method 'setVoicemailVibrationEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPin(String):
+ Method 'supplyIccLockPin' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#supplyIccLockPuk(String, String):
+ Method 'supplyIccLockPuk' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#switchMultiSimConfig(int):
+ Method 'switchMultiSimConfig' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'updateAvailableNetworks' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.TelephonyManager#updateOtaEmergencyNumberDbFilePath(android.os.ParcelFileDescriptor):
+ Method 'updateOtaEmergencyNumberDbFilePath' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent):
+ Method 'sendVisualVoicemailSms' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.VisualVoicemailService#setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings):
+ Method 'setSmsFilterSettings' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#continueOperation(android.content.Intent, android.os.Bundle):
+ Method 'continueOperation' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#deleteSubscription(int, android.app.PendingIntent):
+ Method 'deleteSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent):
+ Method 'downloadSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#eraseSubscriptions(int, android.app.PendingIntent):
+ Method 'eraseSubscriptions' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDefaultDownloadableSubscriptionList(android.app.PendingIntent):
+ Method 'getDefaultDownloadableSubscriptionList' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent):
+ Method 'getDownloadableSubscriptionMetadata' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getOtaStatus():
+ Method 'getOtaStatus' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getSupportedCountries():
+ Method 'getSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#getUnsupportedCountries():
+ Method 'getUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setSupportedCountries(java.util.List<java.lang.String>):
+ Method 'setSupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#setUnsupportedCountries(java.util.List<java.lang.String>):
+ Method 'setUnsupportedCountries' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, int, android.app.PendingIntent):
+ Method 'switchToSubscription' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.euicc.EuiccManager#updateSubscriptionNickname(int, String, android.app.PendingIntent):
+ Method 'updateSubscriptionNickname' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int):
+ Method 'createForSubscriptionId' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#getVoWiFiModeSetting():
+ Method 'getVoWiFiModeSetting' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isAdvancedCallingSettingEnabled():
+ Method 'isAdvancedCallingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isCrossSimCallingEnabled():
+ Method 'isCrossSimCallingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isTtyOverVolteEnabled():
+ Method 'isTtyOverVolteEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiRoamingSettingEnabled():
+ Method 'isVoWiFiRoamingSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVoWiFiSettingEnabled():
+ Method 'isVoWiFiSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#isVtSettingEnabled():
+ Method 'isVtSettingEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'registerMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsMmTelManager#unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback):
+ Method 'unregisterMmTelCapabilityCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationState(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationState' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#getRegistrationTransportType(java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+ Method 'getRegistrationTransportType' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'registerImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#registerImsStateCallback(java.util.concurrent.Executor, android.telephony.ims.ImsStateCallback):
+ Method 'registerImsStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ImsRcsManager#unregisterImsRegistrationCallback(android.telephony.ims.RegistrationManager.RegistrationCallback):
+ Method 'unregisterImsRegistrationCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getProvisioningStatusForCapability(int, int):
+ Method 'getProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#getRcsProvisioningStatusForCapability(int, int):
+ Method 'getRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isProvisioningRequiredForCapability(int, int):
+ Method 'isProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsProvisioningRequiredForCapability(int, int):
+ Method 'isRcsProvisioningRequiredForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable():
+ Method 'isRcsVolteSingleRegistrationCapable' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#notifyRcsAutoConfigurationReceived(byte[], boolean):
+ Method 'notifyRcsAutoConfigurationReceived' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerFeatureProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback):
+ Method 'registerFeatureProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback):
+ Method 'registerProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'registerRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setProvisioningStatusForCapability(int, int, boolean):
+ Method 'setProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#setRcsProvisioningStatusForCapability(int, int, boolean):
+ Method 'setRcsProvisioningStatusForCapability' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback):
+ Method 'unregisterProvisioningChangedCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback(android.telephony.ims.ProvisioningManager.RcsProvisioningCallback):
+ Method 'unregisterRcsProvisioningCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#registerSipDialogStateCallback(java.util.concurrent.Executor, android.telephony.ims.SipDialogStateCallback):
+ Method 'registerSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.telephony.ims.SipDelegateManager#unregisterSipDialogStateCallback(android.telephony.ims.SipDialogStateCallback):
+ Method 'unregisterSipDialogStateCallback' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.WindowManager.LayoutParams#isSystemApplicationOverlay():
+ Method 'isSystemApplicationOverlay' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#registerDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'registerDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.AccessibilityManager#unregisterDisplayProxy(android.view.accessibility.AccessibilityDisplayProxy):
+ Method 'unregisterDisplayProxy' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningEnabled(boolean):
+ Method 'setSystemAudioCaptioningEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.accessibility.CaptioningManager#setSystemAudioCaptioningUiEnabled(boolean):
+ Method 'setSystemAudioCaptioningUiEnabled' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#getEnabledInputMethodListAsUser(android.os.UserHandle):
+ Method 'getEnabledInputMethodListAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#getEnabledInputMethodSubtypeListAsUser(String, boolean, android.os.UserHandle):
+ Method 'getEnabledInputMethodSubtypeListAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#getInputMethodListAsUser(int):
+ Method 'getInputMethodListAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#isStylusHandwritingAvailableAsUser(android.os.UserHandle):
+ Method 'isStylusHandwritingAvailableAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.view.inputmethod.InputMethodManager#setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype):
+ Method 'setCurrentInputMethodSubtype' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setBlockNetworkLoads(boolean):
+ Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
+ Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
+RequiresPermission: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
+ Method 'applySyncTransaction' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.window.WindowOrganizer#applyTransaction(android.window.WindowContainerTransaction):
+ Method 'applyTransaction' documentation mentions permissions already declared by @RequiresPermission
+
+
SamShouldBeLast: android.animation.ValueAnimator#ofObject(android.animation.TypeEvaluator, java.lang.Object...):
SAM-compatible parameters (such as parameter 1, "evaluator", in android.animation.ValueAnimator.ofObject) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
@@ -109,6 +1865,84 @@
SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SdkConstant: android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED:
+ Field 'ACTION_BATTERY_LEVEL_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY:
+ Field 'ACTION_DEVICE_CUSTOMIZATION_READY' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_GLOBAL_BUTTON:
+ Field 'ACTION_GLOBAL_BUTTON' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_PRE_BOOT_COMPLETED:
+ Field 'ACTION_PRE_BOOT_COMPLETED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.Intent#ACTION_UNARCHIVE_PACKAGE:
+ Field 'ACTION_UNARCHIVE_PACKAGE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.content.pm.PackageInstaller#ACTION_CONFIRM_PRE_APPROVAL:
+ Field 'ACTION_CONFIRM_PRE_APPROVAL' is missing @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_CHANGED:
+ Field 'ACTION_USB_PORT_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_PORT_COMPLIANCE_CHANGED:
+ Field 'ACTION_USB_PORT_COMPLIANCE_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.hardware.usb.UsbManager#ACTION_USB_STATE:
+ Field 'ACTION_USB_STATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
+ Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_DELETE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_RENAME_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.service.euicc.EuiccService#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED:
+ Field 'ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+ Field 'ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE:
+ Field 'ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+SdkConstant: android.telephony.TelephonyManager#ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS:
+ Field 'ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+
+
+Todo: android.Manifest.permission#DOMAIN_VERIFICATION_AGENT:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#INSTALL_EXISTING_PACKAGES:
+ Documentation mentions 'TODO'
+Todo: android.Manifest.permission#READ_PEOPLE_DATA:
+ Documentation mentions 'TODO'
+Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
+ Documentation mentions 'TODO'
+Todo: android.hardware.camera2.params.StreamConfigurationMap:
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#loadNanoApp(int, android.hardware.location.NanoApp):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.ContextHubManager#unloadNanoApp(int):
+ Documentation mentions 'TODO'
+Todo: android.hardware.location.NanoAppInstanceInfo:
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvContentRatingSystemInfo#getXmlUri():
+ Documentation mentions 'TODO'
+Todo: android.media.tv.TvInputInfo#isConnectedToHdmiSwitch():
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#prepareForUnattendedUpdate(android.content.Context, String, android.content.IntentSender):
+ Documentation mentions 'TODO'
+Todo: android.os.RecoverySystem#rebootAndApply(android.content.Context, String, String):
+ Documentation mentions 'TODO'
+Todo: android.os.SystemConfigManager:
+ Documentation mentions 'TODO'
+Todo: android.os.UpdateEngineCallback#onStatusUpdate(int, float):
+ Documentation mentions 'TODO'
+Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
+ Documentation mentions 'TODO'
+Todo: android.service.voice.AlwaysOnHotwordDetector:
+ Documentation mentions 'TODO'
+Todo: android.telephony.TelephonyManager#getCurrentPhoneType():
+ Documentation mentions 'TODO'
+Todo: android.telephony.TelephonyManager#setVoiceServiceStateOverride(boolean):
+ Documentation mentions 'TODO'
+Todo: android.window.WindowContainerTransaction#setActivityWindowingMode(android.window.WindowContainerToken, int):
+ Documentation mentions 'TODO'
+
+
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index f2c0051..26f1c4b 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -16,7 +16,6 @@
package android.app;
-import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -413,8 +412,9 @@
private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
"android.activity.launchIntoPipParams";
- /** See {@link #setDismissKeyguard()}. */
- private static final String KEY_DISMISS_KEYGUARD = "android.activity.dismissKeyguard";
+ /** See {@link #setDismissKeyguardIfInsecure()}. */
+ private static final String KEY_DISMISS_KEYGUARD_IF_INSECURE =
+ "android.activity.dismissKeyguardIfInsecure";
private static final String KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE =
"android.activity.pendingIntentCreatorBackgroundActivityStartMode";
@@ -519,7 +519,7 @@
private boolean mLaunchedFromBubble;
private boolean mTransientLaunch;
private PictureInPictureParams mLaunchIntoPipParams;
- private boolean mDismissKeyguard;
+ private boolean mDismissKeyguardIfInsecure;
@BackgroundActivityStartMode
private int mPendingIntentCreatorBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -1333,7 +1333,7 @@
mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, android.app.PictureInPictureParams.class);
mIsEligibleForLegacyPermissionPrompt =
opts.getBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE);
- mDismissKeyguard = opts.getBoolean(KEY_DISMISS_KEYGUARD);
+ mDismissKeyguardIfInsecure = opts.getBoolean(KEY_DISMISS_KEYGUARD_IF_INSECURE);
mPendingIntentCreatorBackgroundActivityStartMode = opts.getInt(
KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE,
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
@@ -2036,24 +2036,24 @@
}
/**
- * Sets whether the keyguard should go away when this activity launches.
+ * Sets whether the insecure keyguard should go away when this activity launches. In case the
+ * keyguard is secure, this option will be ignored.
*
* @see Activity#setShowWhenLocked(boolean)
* @see android.R.attr#showWhenLocked
* @hide
*/
- @RequiresPermission(CONTROL_KEYGUARD)
- public void setDismissKeyguard() {
- mDismissKeyguard = true;
+ public void setDismissKeyguardIfInsecure() {
+ mDismissKeyguardIfInsecure = true;
}
/**
- * @see #setDismissKeyguard()
+ * @see #setDismissKeyguardIfInsecure()
* @return whether the insecure keyguard should go away when the activity launches.
* @hide
*/
- public boolean getDismissKeyguard() {
- return mDismissKeyguard;
+ public boolean getDismissKeyguardIfInsecure() {
+ return mDismissKeyguardIfInsecure;
}
/**
@@ -2367,8 +2367,8 @@
b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE,
mIsEligibleForLegacyPermissionPrompt);
}
- if (mDismissKeyguard) {
- b.putBoolean(KEY_DISMISS_KEYGUARD, mDismissKeyguard);
+ if (mDismissKeyguardIfInsecure) {
+ b.putBoolean(KEY_DISMISS_KEYGUARD_IF_INSECURE, mDismissKeyguardIfInsecure);
}
if (mPendingIntentCreatorBackgroundActivityStartMode
!= MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7e84ceb..b03bd59 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,10 +16,13 @@
package android.app;
+import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
+
import static java.lang.Long.max;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -1495,9 +1498,17 @@
public static final int OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
+ /**
+ * Creation of an overlay using accessibility services
+ *
+ * @hide
+ */
+ public static final int OP_CREATE_ACCESSIBILITY_OVERLAY =
+ AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 138;
+ public static final int _NUM_OP = 139;
/**
* All app ops represented as strings.
@@ -1641,7 +1652,8 @@
OPSTR_CAMERA_SANDBOXED,
OPSTR_RECORD_AUDIO_SANDBOXED,
OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
- OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
+ OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+ OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
})
public @interface AppOpString {}
@@ -2270,6 +2282,16 @@
public static final String OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
"android:RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA";
+ /**
+ * Creation of an overlay using accessibility services
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED)
+ public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY =
+ "android:create_accessibility_overlay";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2819,7 +2841,11 @@
OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
"RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA")
.setPermission(Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA)
- .setDefaultMode(AppOpsManager.MODE_DEFAULT).build()
+ .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
+ new AppOpInfo.Builder(OP_CREATE_ACCESSIBILITY_OVERLAY,
+ OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
+ "CREATE_ACCESSIBILITY_OVERLAY")
+ .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fd308ce..ca6d8df 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,7 +48,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ArchivedPackage;
+import android.content.pm.ArchivedPackageInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
@@ -2586,6 +2586,16 @@
}
@Override
+ public boolean isAppArchivable(String packageName) throws NameNotFoundException {
+ try {
+ Objects.requireNonNull(packageName);
+ return mPM.isAppArchivable(packageName, new UserHandle(getUserId()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int getMoveStatus(int moveId) {
try {
return mPM.getMoveStatus(moveId);
@@ -3937,13 +3947,13 @@
}
@Override
- public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+ public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) {
try {
var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId());
if (parcel == null) {
return null;
}
- return new ArchivedPackage(parcel);
+ return new ArchivedPackageInfo(parcel);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/BackgroundStartPrivileges.java b/core/java/android/app/BackgroundStartPrivileges.java
index 76c0ccf..20278ea 100644
--- a/core/java/android/app/BackgroundStartPrivileges.java
+++ b/core/java/android/app/BackgroundStartPrivileges.java
@@ -174,6 +174,15 @@
@Override
public String toString() {
+ if (this == ALLOW_BAL) {
+ return "BSP.ALLOW_BAL";
+ }
+ if (this == ALLOW_FGS) {
+ return "BSP.ALLOW_FGS";
+ }
+ if (this == NONE) {
+ return "BSP.NONE";
+ }
return "BackgroundStartPrivileges["
+ "allowsBackgroundActivityStarts=" + mAllowsBackgroundActivityStarts
+ ", allowsBackgroundForegroundServiceStarts="
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 2c428ef..1f8784b 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -259,12 +259,10 @@
* @param taskId the id of the task to retrieve the sAutoapshots for
* @param isLowResolution if set, if the snapshot needs to be loaded from disk, this will load
* a reduced resolution of it, which is much faster
- * @param takeSnapshotIfNeeded if set, call {@link #takeTaskSnapshot} to trigger the snapshot
- if no cache exists.
* @return a graphic buffer representing a screenshot of a task
*/
android.window.TaskSnapshot getTaskSnapshot(
- int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded);
+ int taskId, boolean isLowResolution);
/**
* Requests for a new snapshot to be taken for the task with the given id, storing it in the
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bbc843b..2d80b1f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -31,6 +31,7 @@
import android.annotation.DimenRes;
import android.annotation.Dimension;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -12242,6 +12243,7 @@
* {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
* to access values.
*/
+ @FlaggedApi(Flags.FLAG_API_TVEXTENDER)
public static final class TvExtender implements Extender {
private static final String TAG = "TvExtender";
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 9cf54e3..cc56a1c 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -36,6 +36,7 @@
per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
per-file IGameMode* = file:/GAME_MANAGER_OWNERS
+per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS
# ActivityThread
per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7704486..4c70c91 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -49,6 +49,7 @@
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
+import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -17047,23 +17048,6 @@
}
/**
- * Overrides the effective cached value of enable_keep_profiles_running for testing purposes.
- *
- * @hide
- */
- @TestApi
- @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public void setOverrideKeepProfilesRunning(boolean enabled) {
- if (mService != null) {
- try {
- mService.setOverrideKeepProfilesRunning(enabled);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
* Triggers the data migration of device policies for existing DPCs to the Device Policy Engine.
* If {@code forceMigration} is set to {@code true} it skips the prerequisite checks before
* triggering the migration.
@@ -17136,4 +17120,14 @@
}
return null;
}
-}
+
+ // TODO(b/308755220): Remove once the build is finalised.
+ /**
+ * Returns true if the flag for the onboarding bugreport V2 is enabled.
+ *
+ * @hide
+ */
+ public boolean isOnboardingBugreportV2FlagEnabled() {
+ return onboardingBugreportV2Enabled();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8dd50f0..304359b 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -312,21 +312,11 @@
int targetUserId);
/**
- * Returns whether new "turn off work" behavior is enabled via feature flag.
- */
- public abstract boolean isKeepProfilesRunningEnabled();
-
- /**
* True if either the entire device or the user is organization managed.
*/
public abstract boolean isUserOrganizationManaged(@UserIdInt int userId);
/**
- * Returns the list of packages suspended by admin on a given user.
- */
- public abstract Set<String> getPackagesSuspendedByAdmin(@UserIdInt int userId);
-
- /**
* Returns whether the application exemptions feature flag is enabled.
*/
public abstract boolean isApplicationExemptionsFlagEnabled();
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 58f9d57..6fe40be 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -603,8 +603,6 @@
DevicePolicyState getDevicePolicyState();
- void setOverrideKeepProfilesRunning(boolean enabled);
-
boolean triggerDevicePolicyEngineMigration(boolean forceMigration);
boolean isDeviceFinanced(String callerPackageName);
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index cd1d8ce..d9b521f 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -5,4 +5,13 @@
namespace: "systemui"
description: "This flag controls new and updated DND apis"
bug: "300477976"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "api_tvextender"
+ namespace: "systemui"
+ description: "Guards new android.app.Notification.TvExtender api"
+ bug: "308164892"
+ is_fixed_read_only: true
+}
+
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 49543a1..ebd5d64 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -20,10 +20,9 @@
import android.app.usage.BroadcastResponseStats;
import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEventsQuery;
import android.content.pm.ParceledListSlice;
-import java.util.Map;
-
/**
* System private API for talking with the UsageStatsManagerService.
*
@@ -42,6 +41,8 @@
UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
UsageEvents queryEventsForUser(long beginTime, long endTime, int userId, String callingPackage);
UsageEvents queryEventsForPackageForUser(long beginTime, long endTime, int userId, String pkg, String callingPackage);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
+ UsageEvents queryEventsWithFilter(in UsageEventsQuery query, String callingPackage);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void setAppInactive(String packageName, boolean inactive, int userId);
boolean isAppStandbyEnabled();
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index c188686..1eb452c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -349,6 +349,47 @@
*/
public static final int MAX_EVENT_TYPE = 31;
+ /**
+ * Keep in sync with the event types defined above.
+ * @hide
+ */
+ @IntDef(flag = false, value = {
+ NONE,
+ ACTIVITY_RESUMED,
+ ACTIVITY_PAUSED,
+ END_OF_DAY,
+ CONTINUE_PREVIOUS_DAY,
+ CONFIGURATION_CHANGE,
+ SYSTEM_INTERACTION,
+ USER_INTERACTION,
+ SHORTCUT_INVOCATION,
+ CHOOSER_ACTION,
+ NOTIFICATION_SEEN,
+ STANDBY_BUCKET_CHANGED,
+ NOTIFICATION_INTERRUPTION,
+ SLICE_PINNED_PRIV,
+ SLICE_PINNED,
+ SCREEN_INTERACTIVE,
+ SCREEN_NON_INTERACTIVE,
+ KEYGUARD_SHOWN,
+ KEYGUARD_HIDDEN,
+ FOREGROUND_SERVICE_START,
+ FOREGROUND_SERVICE_STOP,
+ CONTINUING_FOREGROUND_SERVICE,
+ ROLLOVER_FOREGROUND_SERVICE,
+ ACTIVITY_STOPPED,
+ ACTIVITY_DESTROYED,
+ FLUSH_TO_DISK,
+ DEVICE_SHUTDOWN,
+ DEVICE_STARTUP,
+ USER_UNLOCKED,
+ USER_STOPPED,
+ LOCUS_ID_SET,
+ APP_COMPONENT_USED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/app/usage/UsageEventsQuery.aidl b/core/java/android/app/usage/UsageEventsQuery.aidl
new file mode 100644
index 0000000..5ed370d
--- /dev/null
+++ b/core/java/android/app/usage/UsageEventsQuery.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 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.usage;
+
+parcelable UsageEventsQuery;
\ No newline at end of file
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
new file mode 100644
index 0000000..8c63d18
--- /dev/null
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 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.usage;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.usage.UsageEvents.Event;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An Object-Oriented representation for a {@link UsageEvents} query.
+ * Used by {@link UsageStatsManager#queryEvents(UsageEventsQuery)} call.
+ */
+@FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API)
+public final class UsageEventsQuery implements Parcelable {
+ private final @CurrentTimeMillisLong long mBeginTimeMillis;
+ private final @CurrentTimeMillisLong long mEndTimeMillis;
+ private final @Event.EventType int[] mEventTypes;
+
+ private UsageEventsQuery(@NonNull Builder builder) {
+ mBeginTimeMillis = builder.mBeginTimeMillis;
+ mEndTimeMillis = builder.mEndTimeMillis;
+ mEventTypes = ArrayUtils.convertToIntArray(builder.mEventTypes);
+ }
+
+ private UsageEventsQuery(Parcel in) {
+ mBeginTimeMillis = in.readLong();
+ mEndTimeMillis = in.readLong();
+ int eventTypesLength = in.readInt();
+ mEventTypes = new int[eventTypesLength];
+ in.readIntArray(mEventTypes);
+ }
+
+ /**
+ * Returns the inclusive timestamp to indicate the beginning of the range of events.
+ * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ */
+ public @CurrentTimeMillisLong long getBeginTimeMillis() {
+ return mBeginTimeMillis;
+ }
+
+ /**
+ * Returns the exclusive timpstamp to indicate the end of the range of events.
+ * Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
+ */
+ public @CurrentTimeMillisLong long getEndTimeMillis() {
+ return mEndTimeMillis;
+ }
+
+ /**
+ * Returns the set of usage event types for the query.
+ * <em>Note: An empty set indicates query for all usage events. </em>
+ */
+ public @NonNull Set<Integer> getEventTypes() {
+ if (ArrayUtils.isEmpty(mEventTypes)) {
+ return Collections.emptySet();
+ }
+
+ HashSet<Integer> eventTypeSet = new HashSet<>();
+ for (int eventType : mEventTypes) {
+ eventTypeSet.add(eventType);
+ }
+ return eventTypeSet;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mBeginTimeMillis);
+ dest.writeLong(mEndTimeMillis);
+ dest.writeInt(mEventTypes.length);
+ dest.writeIntArray(mEventTypes);
+ }
+
+ @NonNull
+ public static final Creator<UsageEventsQuery> CREATOR =
+ new Creator<UsageEventsQuery>() {
+ @Override
+ public UsageEventsQuery createFromParcel(Parcel in) {
+ return new UsageEventsQuery(in);
+ }
+
+ @Override
+ public UsageEventsQuery[] newArray(int size) {
+ return new UsageEventsQuery[size];
+ }
+ };
+
+ /** @hide */
+ public int[] getEventTypeFilter() {
+ return Arrays.copyOf(mEventTypes, mEventTypes.length);
+ }
+
+ /**
+ * Builder for UsageEventsQuery.
+ */
+ public static final class Builder {
+ private final @CurrentTimeMillisLong long mBeginTimeMillis;
+ private final @CurrentTimeMillisLong long mEndTimeMillis;
+ private final ArraySet<Integer> mEventTypes = new ArraySet<>();
+
+ /**
+ * Constructor that specifies the period for which to return events.
+ * @param beginTimeMillis Inclusive beginning timestamp, as per
+ * {@link java.lang.System#currentTimeMillis()}
+ * @param endTimeMillis Exclusive ending timestamp, as per
+ * {@link java.lang.System#currentTimeMillis()}
+ *
+ * @throws IllegalArgumentException if {@code beginTimeMillis} <
+ * {@code endTimeMillis}
+ */
+ public Builder(@CurrentTimeMillisLong long beginTimeMillis,
+ @CurrentTimeMillisLong long endTimeMillis) {
+ if (beginTimeMillis < 0 || endTimeMillis < beginTimeMillis) {
+ throw new IllegalArgumentException("Invalid period");
+ }
+ mBeginTimeMillis = beginTimeMillis;
+ mEndTimeMillis = endTimeMillis;
+ }
+
+ /**
+ * Builds a read-only UsageEventsQuery object.
+ */
+ public @NonNull UsageEventsQuery build() {
+ return new UsageEventsQuery(this);
+ }
+
+ /**
+ * Specifies the list of usage event types to be included in the query.
+ * @param eventTypes List of the usage event types. See {@link UsageEvents.Event}
+ *
+ * @throws llegalArgumentException if the event type is not valid.
+ */
+ public @NonNull Builder addEventTypes(@NonNull @Event.EventType int... eventTypes) {
+ for (int i = 0; i < eventTypes.length; i++) {
+ final int eventType = eventTypes[i];
+ if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) {
+ throw new IllegalArgumentException("Invalid usage event type: " + eventType);
+ }
+ mEventTypes.add(eventType);
+ }
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2a10ed1..4f1c993 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -581,6 +582,29 @@
}
/**
+ * Query for events with specific UsageEventsQuery object.
+ * <em>Note: if the user's device is not in an unlocked state (as defined by
+ * {@link UserManager#isUserUnlocked()}), then {@code null} will be returned.</em>
+ *
+ * @param query The query object used to specify the query parameters.
+ * @return A {@link UsageEvents}.
+ */
+ @FlaggedApi(Flags.FLAG_FILTER_BASED_EVENT_QUERY_API)
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+ public UsageEvents queryEvents(@NonNull UsageEventsQuery query) {
+ try {
+ UsageEvents iter = mService.queryEventsWithFilter(query, mContext.getOpPackageName());
+ if (iter != null) {
+ return iter;
+ }
+ } catch (RemoteException e) {
+ // fallthrough and return empty result.
+ }
+ return sEmptyResults;
+ }
+
+ /**
* Like {@link #queryEvents(long, long)}, but only returns events for the calling package.
* <em>Note: Starting from {@link android.os.Build.VERSION_CODES#R Android R}, if the user's
* device is not in an unlocked state (as defined by {@link UserManager#isUserUnlocked()}),
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index 0b8e29f..a611255 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -28,3 +28,10 @@
description: "Flag for parcelable usage event list"
bug: "301254110"
}
+
+flag {
+ name: "filter_based_event_query_api"
+ namespace: "backstage_power"
+ description: " Feature flag to support filter based event query API"
+ bug: "194321117"
+}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3abe1d9..c6012bb 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -562,13 +562,6 @@
});
}
- private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
- return Looper.myLooper() == Looper.getMainLooper()
- && RemoteViews.isAdapterConversionEnabled()
- && (mHasPostedLegacyLists = mHasPostedLegacyLists
- || (views != null && views.hasLegacyLists()));
- }
-
/**
* Set the RemoteViews to use for the specified appWidgetIds.
* <p>
@@ -593,16 +586,25 @@
return;
}
- if (isPostingTaskToBackground(views)) {
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget views in background", e);
- }
- });
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (views != null && views.hasLegacyLists()));
- return;
+ if (isConvertingAdapter) {
+ views.collectAllIntents();
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ RemoteViews viewsCopy = new RemoteViews(views);
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
}
try {
@@ -714,16 +716,25 @@
return;
}
- if (isPostingTaskToBackground(views)) {
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
- } catch (RemoteException e) {
- Log.e(TAG, "Error partially updating app widget views in background", e);
- }
- });
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (views != null && views.hasLegacyLists()));
- return;
+ if (isConvertingAdapter) {
+ views.collectAllIntents();
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ RemoteViews viewsCopy = new RemoteViews(views);
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error partially updating app widget views in background", e);
+ }
+ });
+
+ return;
+ }
}
try {
@@ -782,16 +793,26 @@
return;
}
- if (isPostingTaskToBackground(views)) {
- createUpdateExecutorIfNull().execute(() -> {
- try {
- mService.updateAppWidgetProvider(provider, views);
- } catch (RemoteException e) {
- Log.e(TAG, "Error updating app widget view using provider in background", e);
- }
- });
+ final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+ && (mHasPostedLegacyLists = mHasPostedLegacyLists
+ || (views != null && views.hasLegacyLists()));
- return;
+ if (isConvertingAdapter) {
+ views.collectAllIntents();
+
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ RemoteViews viewsCopy = new RemoteViews(views);
+ createUpdateExecutorIfNull().execute(() -> {
+ try {
+ mService.updateAppWidgetProvider(provider, viewsCopy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error updating app widget view using provider in background",
+ e);
+ }
+ });
+
+ return;
+ }
}
try {
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 0975cbb..97a7aa4 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -169,7 +169,8 @@
* @see VirtualDeviceManager.VirtualDevice#setDevicePolicy
* @hide
*/
- @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY})
+ @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY,
+ POLICY_TYPE_CLIPBOARD})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface DynamicPolicyType {}
@@ -230,6 +231,20 @@
@FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
public static final int POLICY_TYPE_ACTIVITY = 3;
+ /**
+ * Tells the clipboard manager whether this device's clipboard should be shared or not.
+ *
+ * <ul>
+ * <li>{@link #DEVICE_POLICY_DEFAULT}: By default the device's clipboard is its own and is
+ * not shared with other devices' clipboards, including the clipboard of the default device.
+ * <li>{@link #DEVICE_POLICY_CUSTOM}: The device's clipboard is shared with the default
+ * device's clipboard. Any clipboard operation on the virtual device is as if it was done on
+ * the default device.
+ * </ul>
+ */
+ @FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD)
+ public static final int POLICY_TYPE_CLIPBOARD = 4;
+
private final int mLockState;
@NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
@NavigationPolicy
@@ -1086,6 +1101,10 @@
}
}
+ if (!Flags.crossDeviceClipboard()) {
+ mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);
+ }
+
if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE
|| mAudioRecordingSessionId != AUDIO_SESSION_ID_GENERATE)
&& mDevicePolicies.get(POLICY_TYPE_AUDIO, DEVICE_POLICY_DEFAULT)
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 3cadb7c..cfab9eb 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -16,6 +16,13 @@
}
flag {
+ name: "cross_device_clipboard"
+ namespace: "virtual_devices"
+ description: "Enable cross-device clipboard API"
+ bug: "306622082"
+}
+
+flag {
name: "vdm_custom_home"
namespace: "virtual_devices"
description: "Enable custom home API"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ffc4805..ea54c91 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1164,6 +1164,11 @@
* numbers. Applications can <strong>dial</strong> emergency numbers using
* {@link #ACTION_DIAL}, however.
*
+ * <p>Note: This Intent can only be used to dial call forwarding MMI codes if the application
+ * using this intent is set as the default or system dialer. The system will treat any other
+ * application using this Intent for the purpose of dialing call forwarding MMI codes as if the
+ * {@link #ACTION_DIAL} Intent was used instead.
+ *
* <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
* {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
* relying on this intent.
diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivityInfo.java
similarity index 89%
rename from core/java/android/content/pm/ArchivedActivity.java
rename to core/java/android/content/pm/ArchivedActivityInfo.java
index 9e49c9e..1faa437 100644
--- a/core/java/android/content/pm/ArchivedActivity.java
+++ b/core/java/android/content/pm/ArchivedActivityInfo.java
@@ -32,9 +32,13 @@
import java.io.IOException;
import java.util.Objects;
+/**
+ * Contains fields required to show archived package in Launcher.
+ * @see ArchivedPackageInfo
+ */
@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
@FlaggedApi(Flags.FLAG_ARCHIVING)
-public final class ArchivedActivity {
+public final class ArchivedActivityInfo {
/** The label for the activity. */
private @NonNull CharSequence mLabel;
/** The component name of this activity. */
@@ -47,7 +51,7 @@
/** Monochrome icon, if defined, of the activity. */
private @Nullable Drawable mMonochromeIcon;
- public ArchivedActivity(@NonNull CharSequence label, @NonNull ComponentName componentName) {
+ public ArchivedActivityInfo(@NonNull CharSequence label, @NonNull ComponentName componentName) {
Objects.requireNonNull(label);
Objects.requireNonNull(componentName);
mLabel = label;
@@ -55,7 +59,7 @@
}
/* @hide */
- ArchivedActivity(@NonNull ArchivedActivityParcel parcel) {
+ ArchivedActivityInfo(@NonNull ArchivedActivityParcel parcel) {
mLabel = parcel.title;
mComponentName = parcel.originalComponentName;
mIcon = drawableFromCompressedBitmap(parcel.iconBitmap);
@@ -149,7 +153,7 @@
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivity.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -193,7 +197,7 @@
* The label for the activity.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedActivity setLabel(@NonNull CharSequence value) {
+ public @NonNull ArchivedActivityInfo setLabel(@NonNull CharSequence value) {
mLabel = value;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mLabel);
@@ -204,7 +208,7 @@
* The component name of this activity.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedActivity setComponentName(@NonNull ComponentName value) {
+ public @NonNull ArchivedActivityInfo setComponentName(@NonNull ComponentName value) {
mComponentName = value;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mComponentName);
@@ -216,7 +220,7 @@
* launcher.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedActivity setIcon(@NonNull Drawable value) {
+ public @NonNull ArchivedActivityInfo setIcon(@NonNull Drawable value) {
mIcon = value;
return this;
}
@@ -225,16 +229,16 @@
* Monochrome icon, if defined, of the activity.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedActivity setMonochromeIcon(@NonNull Drawable value) {
+ public @NonNull ArchivedActivityInfo setMonochromeIcon(@NonNull Drawable value) {
mMonochromeIcon = value;
return this;
}
@DataClass.Generated(
- time = 1698173429911L,
+ time = 1698789991876L,
codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivity.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivity extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+ sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/ArchivedPackage.java b/core/java/android/content/pm/ArchivedPackageInfo.java
similarity index 86%
rename from core/java/android/content/pm/ArchivedPackage.java
rename to core/java/android/content/pm/ArchivedPackageInfo.java
index 42795db..f432598 100644
--- a/core/java/android/content/pm/ArchivedPackage.java
+++ b/core/java/android/content/pm/ArchivedPackageInfo.java
@@ -27,9 +27,13 @@
import java.util.List;
import java.util.Objects;
+/**
+ * Contains fields required for archived package installation,
+ * i.e. installation without an APK.
+ */
@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
@FlaggedApi(Flags.FLAG_ARCHIVING)
-public final class ArchivedPackage {
+public final class ArchivedPackageInfo {
/** Name of the package as used to identify it in the system */
private @NonNull String mPackageName;
/** Signing certificates used to sign the package. */
@@ -74,10 +78,10 @@
* {@link Intent#CATEGORY_LAUNCHER}.
* @see LauncherApps#getActivityList
*/
- private @NonNull List<ArchivedActivity> mLauncherActivities;
+ private @NonNull List<ArchivedActivityInfo> mLauncherActivities;
- public ArchivedPackage(@NonNull String packageName, @NonNull SigningInfo signingInfo,
- @NonNull List<ArchivedActivity> launcherActivities) {
+ public ArchivedPackageInfo(@NonNull String packageName, @NonNull SigningInfo signingInfo,
+ @NonNull List<ArchivedActivityInfo> launcherActivities) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(signingInfo);
Objects.requireNonNull(launcherActivities);
@@ -90,7 +94,7 @@
* Constructs the archived package from parcel.
* @hide
*/
- public ArchivedPackage(@NonNull ArchivedPackageParcel parcel) {
+ public ArchivedPackageInfo(@NonNull ArchivedPackageParcel parcel) {
mPackageName = parcel.packageName;
mSigningInfo = new SigningInfo(parcel.signingDetails);
mVersionCode = parcel.versionCode;
@@ -102,7 +106,7 @@
mLauncherActivities = new ArrayList<>();
if (parcel.archivedActivities != null) {
for (var activityParcel : parcel.archivedActivities) {
- mLauncherActivities.add(new ArchivedActivity(activityParcel));
+ mLauncherActivities.add(new ArchivedActivityInfo(activityParcel));
}
}
}
@@ -135,7 +139,7 @@
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackage.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -224,7 +228,7 @@
* @see LauncherApps#getActivityList
*/
@DataClass.Generated.Member
- public @NonNull List<ArchivedActivity> getLauncherActivities() {
+ public @NonNull List<ArchivedActivityInfo> getLauncherActivities() {
return mLauncherActivities;
}
@@ -232,7 +236,7 @@
* Name of the package as used to identify it in the system
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setPackageName(@NonNull String value) {
+ public @NonNull ArchivedPackageInfo setPackageName(@NonNull String value) {
mPackageName = value;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
@@ -243,7 +247,7 @@
* Signing certificates used to sign the package.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setSigningInfo(@NonNull SigningInfo value) {
+ public @NonNull ArchivedPackageInfo setSigningInfo(@NonNull SigningInfo value) {
mSigningInfo = value;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSigningInfo);
@@ -255,7 +259,7 @@
* {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setVersionCode( int value) {
+ public @NonNull ArchivedPackageInfo setVersionCode( int value) {
mVersionCode = value;
return this;
}
@@ -265,7 +269,7 @@
* {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setVersionCodeMajor( int value) {
+ public @NonNull ArchivedPackageInfo setVersionCodeMajor( int value) {
mVersionCodeMajor = value;
return this;
}
@@ -276,7 +280,7 @@
* attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setTargetSdkVersion( int value) {
+ public @NonNull ArchivedPackageInfo setTargetSdkVersion( int value) {
mTargetSdkVersion = value;
return this;
}
@@ -287,7 +291,7 @@
* attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String value) {
+ public @NonNull ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String value) {
mDefaultToDeviceProtectedStorage = value;
return this;
}
@@ -299,7 +303,7 @@
* attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setRequestLegacyExternalStorage(@NonNull String value) {
+ public @NonNull ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String value) {
mRequestLegacyExternalStorage = value;
return this;
}
@@ -310,7 +314,7 @@
* {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute.
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setUserDataFragile(@NonNull String value) {
+ public @NonNull ArchivedPackageInfo setUserDataFragile(@NonNull String value) {
mUserDataFragile = value;
return this;
}
@@ -322,7 +326,7 @@
* @see LauncherApps#getActivityList
*/
@DataClass.Generated.Member
- public @NonNull ArchivedPackage setLauncherActivities(@NonNull List<ArchivedActivity> value) {
+ public @NonNull ArchivedPackageInfo setLauncherActivities(@NonNull List<ArchivedActivityInfo> value) {
mLauncherActivities = value;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mLauncherActivities);
@@ -330,10 +334,10 @@
}
@DataClass.Generated(
- time = 1697824890503L,
+ time = 1698789995536L,
codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackage.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate int mVersionCode\nprivate int mVersionCodeMajor\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivity> mLauncherActivities\n android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackage extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+ sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate int mVersionCode\nprivate int mVersionCodeMajor\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivityInfo> mLauncherActivities\n android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackageInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 9926415..babfba1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -838,4 +838,6 @@
ArchivedPackageParcel getArchivedPackage(in String packageName, int userId);
Bitmap getArchivedAppIcon(String packageName, in UserHandle user);
+
+ boolean isAppArchivable(String packageName, in UserHandle user);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 62dda6b..6681e54 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1006,7 +1006,7 @@
/**
* Install package in an archived state.
*
- * @param archivedPackage archived package data such as package name, signature etc.
+ * @param archivedPackageInfo archived package data such as package name, signature etc.
* @param sessionParams used to create an underlying installation session
* @param statusReceiver Called when the state of the session changes. Intents
* sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
@@ -1016,15 +1016,15 @@
*/
@RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
@FlaggedApi(Flags.FLAG_ARCHIVING)
- public void installPackageArchived(@NonNull ArchivedPackage archivedPackage,
+ public void installPackageArchived(@NonNull ArchivedPackageInfo archivedPackageInfo,
@NonNull SessionParams sessionParams,
@NonNull IntentSender statusReceiver) {
- Objects.requireNonNull(archivedPackage, "archivedPackage cannot be null");
+ Objects.requireNonNull(archivedPackageInfo, "archivedPackageInfo cannot be null");
Objects.requireNonNull(sessionParams, "sessionParams cannot be null");
Objects.requireNonNull(statusReceiver, "statusReceiver cannot be null");
try {
mInstaller.installPackageArchived(
- archivedPackage.getParcel(),
+ archivedPackageInfo.getParcel(),
sessionParams,
statusReceiver,
mInstallerPackageName,
@@ -3832,6 +3832,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_INSTALLED_SESSION_PATHS)
+ @FlaggedApi(Flags.FLAG_GET_RESOLVED_APK_PATH)
public @Nullable String getResolvedBaseApkPath() {
return resolvedBaseCodePath;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ad7dd51..e2a5747 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1480,6 +1480,7 @@
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
+ INSTALL_IGNORE_DEXOPT_PROFILE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1712,6 +1713,18 @@
public static final int INSTALL_ARCHIVED = 1 << 27;
/**
+ * If set, all dexopt profiles are ignored by dexopt during the installation, including the
+ * profile in the DM file and the profile embedded in the APK file. If an invalid profile is
+ * provided during installation, no warning will be reported by {@code adb install}.
+ *
+ * This option does not affect later dexopt operations (e.g., background dexopt and manual `pm
+ * compile` invocations).
+ *
+ * @hide
+ */
+ public static final int INSTALL_IGNORE_DEXOPT_PROFILE = 1 << 28;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
@@ -8910,6 +8923,20 @@
}
/**
+ * Returns true if an app is archivable.
+ *
+ * @throws NameNotFoundException if the given package name is not available to the caller.
+ * @see PackageInstaller#requestArchive(String, IntentSender)
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
+ public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException("isAppArchivable not implemented");
+ }
+
+ /**
* Attempts to clear the user data directory of an application.
* Since this may take a little while, the result will
* be posted back to the given observer. A deletion will fail if the
@@ -11032,7 +11059,7 @@
* @see PackageInstaller#installPackageArchived
*/
@FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
- public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+ public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) {
throw new UnsupportedOperationException(
"getArchivedPackage not implemented in subclass");
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index a8fe21e..814eae6 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -60,7 +60,6 @@
}
flag {
-
name: "rollback_lifetime"
namespace: "package_manager_service"
description: "Feature flag to enable custom rollback lifetime during install."
@@ -74,3 +73,10 @@
description: "Feature flag to improve install freeze time."
bug: "307561242"
}
+
+flag {
+ name: "get_resolved_apk_path"
+ namespace: "package_manager_service"
+ description: "Feature flag to retrieve resolved path of the base APK during an app install."
+ bug: "269728874"
+}
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 524afe9..ad3ccc4 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -437,7 +437,14 @@
* Returns {@code true} if the calling application provides a CredentialProviderService that is
* enabled for the current user, or {@code false} otherwise. CredentialProviderServices are
* enabled on a per-service basis so the individual component name of the service should be
- * passed in here.
+ * passed in here. <strong>Usage of this API is discouraged as it is not fully functional, and
+ * may throw a NullPointerException on certain devices and/or API versions.</strong>
+ *
+ * @throws IllegalArgumentException if the componentName package does not match the calling
+ * package name this call will throw an exception
+ *
+ * @throws NullPointerException Usage of this API is discouraged as it is not fully
+ * functional, and may throw a NullPointerException on certain devices and/or API versions
*
* @param componentName the component name to check is enabled
*/
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 943eee4..61d8702 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -18,6 +18,7 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -296,4 +297,15 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({BIOMETRIC_LOCKOUT_NONE, BIOMETRIC_LOCKOUT_TIMED, BIOMETRIC_LOCKOUT_PERMANENT})
@interface LockoutMode {}
+
+ //
+ // Other miscellaneous constants
+ //
+
+ /**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when there has
+ * been no successful authentication for the given authenticator since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ long BIOMETRIC_NO_AUTHENTICATION = -1;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 82694ee..90bbca8 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -23,6 +23,8 @@
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_BIOMETRIC_MANAGER_CAN_AUTHENTICATE;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +32,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.KeyguardManager;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -86,6 +89,17 @@
BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
/**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when no matching
+ * successful authentication has been performed since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public static final long BIOMETRIC_NO_AUTHENTICATION =
+ BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+
+ private static final int GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG;
+
+ /**
* @hide
*/
@IntDef({BIOMETRIC_SUCCESS,
@@ -637,5 +651,58 @@
}
}
+
+ /**
+ * Gets the last time the user successfully authenticated using one of the given authenticators.
+ * The returned value is time in
+ * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} (time since
+ * boot, including sleep).
+ * <p>
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} is returned in the case where there
+ * has been no successful authentication using any of the given authenticators since boot.
+ * <p>
+ * Currently, only {@link Authenticators#DEVICE_CREDENTIAL} and
+ * {@link Authenticators#BIOMETRIC_STRONG} are accepted. {@link IllegalArgumentException} will
+ * be thrown if {@code authenticators} contains other authenticator types.
+ * <p>
+ * Note that this may return successful authentication times even if the device is currently
+ * locked. You may use {@link KeyguardManager#isDeviceLocked()} to determine if the device
+ * is unlocked or not. Additionally, this method may return valid times for an authentication
+ * method that is no longer available. For instance, if the user unlocked the device with a
+ * {@link Authenticators#BIOMETRIC_STRONG} authenticator but then deleted that authenticator
+ * (e.g., fingerprint data), this method will still return the time of that unlock for
+ * {@link Authenticators#BIOMETRIC_STRONG} if it is the most recent successful event. The caveat
+ * is that {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} will be returned if the device
+ * no longer has a secure lock screen at all, even if there were successful authentications
+ * performed before the lock screen was made insecure.
+ *
+ * @param authenticators bit field consisting of constants defined in {@link Authenticators}.
+ * @return the time of last authentication or
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION}
+ * @throws IllegalArgumentException if {@code authenticators} contains values other than
+ * {@link Authenticators#DEVICE_CREDENTIAL} and {@link Authenticators#BIOMETRIC_STRONG} or is
+ * 0.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @ElapsedRealtimeLong
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public long getLastAuthenticationTime(
+ @BiometricManager.Authenticators.Types int authenticators) {
+ if (authenticators == 0
+ || (GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS & authenticators) != authenticators) {
+ throw new IllegalArgumentException(
+ "Only BIOMETRIC_STRONG and DEVICE_CREDENTIAL authenticators may be used.");
+ }
+
+ if (mService != null) {
+ try {
+ return mService.getLastAuthenticationTime(UserHandle.myUserId(), authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index c2e5c0b..5bdbe2b5 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -57,6 +57,9 @@
// Checks if biometrics can be used.
int canAuthenticate(String opPackageName, int userId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 18c8d1b..058f302 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -53,6 +53,10 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 66429e5..979f570 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.hardware.biometrics"
flag {
+ name: "last_authentication_time"
+ namespace: "wallet_integration"
+ description: "Feature flag for adding getLastAuthenticationTime API to BiometricManager"
+ bug: "301979982"
+}
+
+flag {
name: "add_key_agreement_crypto_object"
namespace: "biometrics"
description: "Feature flag for adding KeyAgreement api to CryptoObject."
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 779a8db..6f11d3a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -493,7 +493,7 @@
/**
* Check whether safe mode is enabled
*
- * @see Builder#enableSafeMode(boolean)
+ * @see Builder#setSafeModeEnabled(boolean)
*/
@FlaggedApi(FLAG_SAFE_MODE_CONFIG)
public boolean isSafeModeEnabled() {
@@ -815,8 +815,8 @@
*
* <p>If a VCN fails to provide connectivity within a system-provided timeout, it will enter
* safe mode. In safe mode, the VCN Network will be torn down and the system will restore
- * connectivity by allowing underlying cellular networks to be used as default. At the same
- * time, VCN will continue to retry until it succeeds.
+ * connectivity by allowing underlying cellular or WiFi networks to be used as default. At
+ * the same time, VCN will continue to retry until it succeeds.
*
* <p>When safe mode is disabled and VCN connection fails to provide connectivity, end users
* might not have connectivity, and may not have access to carrier-owned underlying
@@ -826,7 +826,7 @@
*/
@FlaggedApi(FLAG_SAFE_MODE_CONFIG)
@NonNull
- public Builder enableSafeMode(boolean enabled) {
+ public Builder setSafeModeEnabled(boolean enabled) {
mIsSafeModeDisabled = !enabled;
return this;
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index a07735e..8fcff78 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -117,8 +117,6 @@
private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
private static final String SYSTEM_ANGLE_STRING = "system";
- private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
-
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
@@ -620,8 +618,7 @@
}
/**
- * Attempt to set up ANGLE from system, if the apk can be found, pass ANGLE details to
- * the C++ GraphicsEnv class.
+ * Set up ANGLE from system.
*
* @param context - Context of the application.
* @param bundle - Bundle of the application.
@@ -630,14 +627,8 @@
* false: can not set up to use system ANGLE because it doesn't exist.
*/
private boolean setupAngleFromSystem(Context context, Bundle bundle, String packageName) {
- final boolean systemAngleSupported = SystemProperties
- .getBoolean(PROPERTY_RO_ANGLE_SUPPORTED, false);
- if (!systemAngleSupported) {
- return false;
- }
-
- // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
- // the application package name and ANGLE features to use.
+ // System ANGLE always exists, call nativeSetAngleInfo() with the application package
+ // name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
nativeSetAngleInfo(SYSTEM_ANGLE_STRING, false, packageName, features);
return true;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e37b2b5..677143a 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -730,13 +730,14 @@
whitelistedDataInfoMap,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
+ boolean bindMountSystemOverrides,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
- bindMountAppStorageDirs, zygoteArgs);
+ bindMountAppStorageDirs, bindMountSystemOverrides, zygoteArgs);
}
/** @hide */
@@ -753,6 +754,7 @@
@Nullable String invokeWith,
@Nullable String packageName,
@Nullable long[] disabledCompatChanges,
+ boolean bindMountSyspropOverrides,
@Nullable String[] zygoteArgs) {
// Webview zygote can't access app private data files, so doesn't need to know its data
// info.
@@ -761,7 +763,8 @@
abi, instructionSet, appDataDir, invokeWith, packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
disabledCompatChanges, /* pkgDataInfoMap */ null,
- /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
+ /* whitelistedDataInfoMap */ null, /* bindMountAppsData */ false,
+ /* bindMountAppStorageDirs */ false, bindMountSyspropOverrides, zygoteArgs);
}
/**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index a3b836a..d4688f8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -933,6 +933,12 @@
rebootWipeUserData(context, shutdown, reason, force, false /* wipeEuicc */);
}
+ /** {@hide} */
+ public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
+ boolean force, boolean wipeEuicc) throws IOException {
+ rebootWipeUserData(context, shutdown, reason, force, wipeEuicc, false /* keepMemtagMode */);
+ }
+
/**
* Reboots the device and wipes the user data and cache
* partitions. This is sometimes called a "factory reset", which
@@ -948,6 +954,7 @@
* @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction
* should be ignored
* @param wipeEuicc whether wipe the euicc data
+ * @param keepMemtagMode whether to tell recovery to keep currently configured memtag mode
*
* @throws IOException if writing the recovery command file
* fails, or if the reboot itself fails.
@@ -956,7 +963,7 @@
* @hide
*/
public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
- boolean force, boolean wipeEuicc) throws IOException {
+ boolean force, boolean wipeEuicc, boolean keepMemtagMode) throws IOException {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
throw new SecurityException("Wiping data is not allowed for this user.");
@@ -996,8 +1003,13 @@
reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
}
+ String memtagArg = null;
+ if (keepMemtagMode) {
+ memtagArg = "--keep_memtag_mode";
+ }
+
final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
- bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
+ bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg, memtagArg);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 3cb5c60..c14810b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -355,6 +355,7 @@
allowlistedDataInfoList,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
+ boolean bindOverrideSysprops,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -367,7 +368,7 @@
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,
- bindMountAppStorageDirs, zygoteArgs);
+ bindMountAppStorageDirs, bindOverrideSysprops, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -638,6 +639,7 @@
allowlistedDataInfoList,
boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
+ boolean bindMountOverrideSysprops,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<>();
@@ -753,6 +755,10 @@
argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
}
+ if (bindMountOverrideSysprops) {
+ argsForZygote.add(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1306,7 +1312,8 @@
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
null /* allowlistedDataInfoList */, true /* bindMountAppsData*/,
- /* bindMountAppStorageDirs */ false, extraArgs);
+ /* bindMountAppStorageDirs */ false, /*bindMountOverrideSysprops */ false,
+ extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c282c96..9f931b4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5914,6 +5914,7 @@
*
* @hide
*/
+ @Readable
public static final String TOUCHPAD_NATURAL_SCROLLING = "touchpad_natural_scrolling";
/**
@@ -10308,6 +10309,13 @@
@Readable
public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
+ /**
+ * 1 if it is allowed to remove the primary GAIA account. 0 by default.
+ * @hide
+ */
+ public static final String ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS =
+ "allow_primary_gaia_account_removal_for_tests";
+
/**
* List of TV inputs that are currently hidden. This is a string
* containing the IDs of all hidden TV inputs. Each ID is encoded by
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 27ad45d..bcda25a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3206,6 +3206,15 @@
public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask";
/**
+ * Indicating if the APN is used for eSIM bootsrap provisioning. The default value is 0 (Not
+ * used for eSIM bootstrap provisioning).
+ *
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String ESIM_BOOTSTRAP_PROVISIONING = "esim_bootstrap_provisioning";
+
+ /**
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 96c0be7..33a67ae 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,3 +8,4 @@
per-file Confirmation*.java = file:/keystore/OWNERS
per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
+per-file *.aconfig = victorhsieh@google.com
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 94eca3d..0133bd8 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -29,3 +29,10 @@
bug: "277916185"
is_fixed_read_only: true
}
+
+flag {
+ name: "binary_transparency_sepolicy_hash"
+ namespace: "hardware_backed_security"
+ description: "Collect sepolicy hash from sysfs"
+ bug: "308471499"
+}
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 52d4d47..2a05c84 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -4,7 +4,7 @@
name: "ranking_update_ashmem"
namespace: "systemui"
description: "This flag controls moving ranking update contents into ashmem"
- bug: "284297289"
+ bug: "249848655"
}
flag {
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index be5851c..d84cc2c 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -194,17 +194,22 @@
audioTrack.play();
}
- int count = 0;
- while (count < bytes.length) {
- // Note that we don't take bufferCopy.mOffset into account because
- // it is guaranteed to be 0.
- int written = audioTrack.write(bytes, count, bytes.length);
+ int offset = 0;
+ while (offset < bytes.length) {
+ // Although it requests to write the entire bytes at once, it might fail when the track
+ // got stopped or the thread is interrupted. In that case, it needs to carry on from
+ // last offset.
+ int sizeToWrite = bytes.length - offset;
+ int written = audioTrack.write(bytes, offset, sizeToWrite);
if (written <= 0) {
+ if (written < 0) {
+ Log.e(TAG, "An error occurred while writing to audio track: " + written);
+ }
break;
}
- count += written;
+ offset += written;
}
- return count;
+ return offset;
}
private AudioTrack createStreamingAudioTrack() {
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 43c38f3..a1885ae 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -75,3 +75,10 @@
description: "A feature flag that implements line break word style auto."
bug: "280005585"
}
+
+flag {
+ name: "inter_character_justification"
+ namespace: "text"
+ description: "A feature flag that implement inter character justification."
+ bug: "283193133"
+}
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index d545751..fd5517d 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -231,4 +231,19 @@
default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t,
@NonNull Consumer<Boolean> listener) {
}
+
+ /**
+ * Transfer the currently in progress touch gesture from the host to the requested
+ * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
+ * SurfaceControlViewHost was created with the current host's inputToken.
+ *
+ * @param surfacePackage The SurfacePackage to transfer the gesture to.
+ * @return Whether the touch stream was transferred.
+ */
+ @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED)
+ default boolean transferHostTouchGestureToEmbedded(
+ @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ throw new UnsupportedOperationException(
+ "transferHostTouchGestureToEmbedded is unimplemented");
+ }
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7acf2f8..02e97da 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -357,4 +357,6 @@
boolean cancelDraw(IWindow window);
boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
+
+ boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken);
}
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 52be8f6..127d4a7 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -337,13 +337,13 @@
@VisibleForTesting
public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) {
final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty();
- if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) {
+ if (matchCall && !sCallStackDebuggingMatchCall.contains(call.toLowerCase())) {
// Skip if target call doesn't match requested caller
return false;
}
final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
if (matchName && (name == null
- || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) {
+ || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) {
// Skip if target surface doesn't match requested surface
return false;
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 57b19a8..4056531 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -287,7 +287,8 @@
}
/**
- * Returns an input token used which can be used to request focus on the embedded surface.
+ * Returns an input token used which can be used to request focus on the embedded surface
+ * or to transfer touch gesture to the embedded surface.
*
* @hide
*/
@@ -526,7 +527,8 @@
}
/**
- * Returns an input token used which can be used to request focus on the embedded surface.
+ * Returns an input token used which can be used to request focus on the embedded surface
+ * or to transfer touch gesture to the embedded surface.
*
* @hide
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 5b69d7f..0ae14a2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -43,6 +43,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -51,6 +52,7 @@
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.window.SurfaceSyncGroup;
+import com.android.graphics.hwui.flags.Flags;
import com.android.internal.view.SurfaceCallbackHelper;
import java.lang.annotation.Retention;
@@ -326,6 +328,8 @@
}
};
+ private final boolean mRtDrivenClipping = Flags.clipSurfaceviews();
+
public SurfaceView(Context context) {
this(context, null);
}
@@ -572,6 +576,10 @@
public void setClipBounds(Rect clipBounds) {
super.setClipBounds(clipBounds);
+ if (mRtDrivenClipping && isHardwareAccelerated()) {
+ return;
+ }
+
if (!mClipSurfaceToBounds || mSurfaceControl == null) {
return;
}
@@ -915,15 +923,17 @@
}
if (sizeChanged || creating || !isHardwareAccelerated()) {
- // Set a window crop when creating the surface or changing its size to
- // crop the buffer to the surface size since the buffer producer may
- // use SCALING_MODE_SCALE and submit a larger size than the surface
- // size.
- if (mClipSurfaceToBounds && mClipBounds != null) {
- surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
- } else {
- surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
- mSurfaceHeight);
+ if (!mRtDrivenClipping || !isHardwareAccelerated()) {
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ if (mClipSurfaceToBounds && mClipBounds != null) {
+ surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+ } else {
+ surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
}
surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth,
@@ -941,7 +951,7 @@
mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
}
if (DEBUG_POSITION) {
- Log.d(TAG, String.format(
+ Log.d(TAG, TextUtils.formatSimple(
"%d performSurfaceTransaction %s "
+ "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
System.identityHashCode(this),
@@ -1453,6 +1463,7 @@
}
private final Rect mRTLastReportedPosition = new Rect();
+ private final Rect mRTLastSetCrop = new Rect();
private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
private final int mRtSurfaceWidth;
@@ -1496,6 +1507,45 @@
}
@Override
+ public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
+ int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ try {
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format(
+ "%d updateSurfacePosition RenderWorker, frameNr = %d, "
+ + "position = [%d, %d, %d, %d] clip = [%d, %d, %d, %d] "
+ + "surfaceSize = %dx%d",
+ System.identityHashCode(SurfaceView.this), frameNumber,
+ left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom,
+ mRtSurfaceWidth, mRtSurfaceHeight));
+ }
+ synchronized (mSurfaceControlLock) {
+ if (mSurfaceControl == null) return;
+
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl,
+ mRTLastReportedPosition.left /*positionLeft*/,
+ mRTLastReportedPosition.top /*positionTop*/,
+ mRTLastReportedPosition.width()
+ / (float) mRtSurfaceWidth /*postScaleX*/,
+ mRTLastReportedPosition.height()
+ / (float) mRtSurfaceHeight /*postScaleY*/);
+
+ mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
+ mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop);
+ if (mRTLastSetCrop.isEmpty()) {
+ mPositionChangedTransaction.hide(mSurfaceControl);
+ } else {
+ mPositionChangedTransaction.show(mSurfaceControl);
+ }
+ }
+ applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception from repositionChild", ex);
+ }
+ }
+
+ @Override
public void applyStretch(long frameNumber, float width, float height,
float vecX, float vecY, float maxStretchX, float maxStretchY,
float childRelativeLeft, float childRelativeTop, float childRelativeRight,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dd2a104..1ee303c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3983,8 +3983,17 @@
}
private void notifyContentCaptureEvents() {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
try {
+ if (!isContentCaptureEnabled()) {
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.d(mTag, "notifyContentCaptureEvents while disabled");
+ }
+ mAttachInfo.mContentCaptureEvents = null;
+ return;
+ }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+ }
MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
.getMainContentCaptureSession();
for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
@@ -4898,13 +4907,14 @@
if (DEBUG_CONTENT_CAPTURE) {
Log.v(mTag, "performContentCaptureInitialReport() on " + rootView);
}
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
- + getClass().getSimpleName());
- }
try {
if (!isContentCaptureEnabled()) return;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+ + getClass().getSimpleName());
+ }
+
// Initial dispatch of window bounds to content capture
if (mAttachInfo.mContentCaptureManager != null) {
MainContentCaptureSession session =
@@ -4924,13 +4934,14 @@
if (DEBUG_CONTENT_CAPTURE) {
Log.v(mTag, "handleContentCaptureFlush()");
}
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
- + getClass().getSimpleName());
- }
try {
if (!isContentCaptureEnabled()) return;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
+ + getClass().getSimpleName());
+ }
+
final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager;
if (ccm == null) {
Log.w(TAG, "No ContentCapture on AttachInfo");
@@ -11882,4 +11893,17 @@
}
Log.d(mTag, msg);
}
+
+ @Override
+ public boolean transferHostTouchGestureToEmbedded(
+ @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
+ try {
+ return realWm.transferHostTouchGestureToEmbedded(mWindow,
+ surfacePackage.getInputToken());
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return false;
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4f03ce9..cfec081 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -90,6 +90,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -5872,7 +5873,7 @@
*
* @hide
*/
- @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
@@ -5888,7 +5889,7 @@
*
* @hide
*/
- @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
+ @SuppressLint("UnflaggedApi") // The API is only used for tests.
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7c3b6ae..652fe24 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -656,6 +656,14 @@
return false;
}
+ @Override
+ public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
+ IBinder embeddedInputToken) {
+ Log.e(TAG, "Received request to transferHostTouchGestureToEmbedded on"
+ + " WindowlessWindowManager. We shouldn't get here!");
+ return false;
+ }
+
void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 970baf2..5a058ff 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -794,7 +794,9 @@
(params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
MainContentCaptureSession mainSession;
+ boolean alreadyDisabledByApp;
synchronized (mLock) {
+ alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0;
if (flagSecureEnabled) {
mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
} else {
@@ -802,7 +804,9 @@
}
mainSession = mMainSession;
}
- if (mainSession != null) {
+
+ // Prevent overriding the status of disabling by app
+ if (mainSession != null && !alreadyDisabledByApp) {
mainSession.setDisabled(flagSecureEnabled);
}
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index b44d6a4..d9b0f80 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -44,6 +44,7 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.os.Trace;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
@@ -373,12 +374,26 @@
return;
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ if (eventType == TYPE_VIEW_TREE_APPEARING) {
+ Trace.asyncTraceBegin(
+ Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+ }
+ }
+
if (isContentProtectionReceiverEnabled()) {
sendContentProtectionEvent(event);
}
if (isContentCaptureReceiverEnabled()) {
sendContentCaptureEvent(event, forceFlush);
}
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ if (eventType == TYPE_VIEW_TREE_APPEARED) {
+ Trace.asyncTraceEnd(
+ Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+ }
+ }
}
@UiThread
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index f6ee061..f3dc33c 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -20,3 +20,10 @@
description: "If true, content protection setting ui is displayed in Settings > Privacy & Security > More security & privacy."
bug: "305792348"
}
+
+flag {
+ name: "create_accessibility_overlay_app_op_enabled"
+ namespace: "content_protection"
+ description: "If true, an appop is logged on creation of accessibility overlays."
+ bug: "289081465"
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3078801..403b403 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -85,6 +85,7 @@
import android.util.LongArray;
import android.util.Pair;
import android.util.SizeF;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
@@ -367,6 +368,11 @@
@UnsupportedAppUsage
private BitmapCache mBitmapCache = new BitmapCache();
+ /**
+ * Maps Intent ID to RemoteCollectionItems to avoid duplicate items
+ */
+ private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();
+
/** Cache of ApplicationInfos used by collection items. */
private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
@@ -784,9 +790,12 @@
if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
&& itemsAction.mViewId == viewId
&& itemsAction.mServiceIntent != null) {
- mActions.set(i,
- new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId,
- itemsAction.mServiceIntent));
+ SetRemoteCollectionItemListAdapterAction newCollectionAction =
+ new SetRemoteCollectionItemListAdapterAction(
+ itemsAction.mViewId, itemsAction.mServiceIntent);
+ newCollectionAction.mIntentId = itemsAction.mIntentId;
+ newCollectionAction.mIsReplacedIntoAction = true;
+ mActions.set(i, newCollectionAction);
isActionReplaced = true;
} else if (action instanceof SetRemoteViewsAdapterIntent intentAction
&& intentAction.mViewId == viewId) {
@@ -1048,6 +1057,8 @@
@NonNull
private CompletableFuture<RemoteCollectionItems> mItemsFuture;
final Intent mServiceIntent;
+ int mIntentId = -1;
+ boolean mIsReplacedIntoAction = false;
SetRemoteCollectionItemListAdapterAction(@IdRes int id,
@NonNull RemoteCollectionItems items) {
@@ -1108,38 +1119,36 @@
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
- mItemsFuture = CompletableFuture.completedFuture(
- new RemoteCollectionItems(parcel, getHierarchyRootData()));
+ mIntentId = parcel.readInt();
+ mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
+ ? null
+ : new RemoteCollectionItems(parcel, getHierarchyRootData()));
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
}
@Override
public void setHierarchyRootData(HierarchyRootData rootData) {
- mItemsFuture = mItemsFuture
- .thenApply(rc -> {
- rc.setHierarchyRootData(rootData);
- return rc;
- });
- }
-
- private static RemoteCollectionItems getCollectionItemsFromFuture(
- CompletableFuture<RemoteCollectionItems> itemsFuture) {
- RemoteCollectionItems items;
- try {
- items = itemsFuture.get();
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error getting collection items from future", e);
- items = new RemoteCollectionItems.Builder().build();
+ if (mIntentId == -1) {
+ mItemsFuture = mItemsFuture
+ .thenApply(rc -> {
+ rc.setHierarchyRootData(rootData);
+ return rc;
+ });
+ return;
}
- return items;
+ // Set the root data for items in the cache instead
+ mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
- items.writeToParcel(dest, flags, /* attached= */ true);
+ dest.writeInt(mIntentId);
+ if (mIntentId == -1) {
+ RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
+ items.writeToParcel(dest, flags, /* attached= */ true);
+ }
dest.writeTypedObject(mServiceIntent, flags);
}
@@ -1149,7 +1158,9 @@
View target = root.findViewById(mViewId);
if (target == null) return;
- RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
+ RemoteCollectionItems items = mIntentId == -1
+ ? getCollectionItemsFromFuture(mItemsFuture)
+ : mCollectionCache.getItemsForId(mIntentId);
// Ensure that we are applying to an AppWidget root
if (!(rootParent instanceof AppWidgetHostView)) {
@@ -1210,6 +1221,153 @@
}
}
+ private static RemoteCollectionItems getCollectionItemsFromFuture(
+ CompletableFuture<RemoteCollectionItems> itemsFuture) {
+ RemoteCollectionItems items;
+ try {
+ items = itemsFuture.get();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error getting collection items from future", e);
+ items = new RemoteCollectionItems.Builder().build();
+ }
+
+ return items;
+ }
+
+ /**
+ * @hide
+ */
+ public void collectAllIntents() {
+ mCollectionCache.collectAllIntentsNoComplete(this);
+ }
+
+ private class RemoteCollectionCache {
+ private SparseArray<String> mIdToUriMapping = new SparseArray<>();
+ private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
+
+ // We don't put this into the parcel
+ private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
+ new HashMap<>();
+
+ RemoteCollectionCache() { }
+
+ RemoteCollectionCache(RemoteCollectionCache src) {
+ boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
+ for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
+ String uri = src.mIdToUriMapping.valueAt(i);
+ mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
+ if (isWaitingCache) {
+ mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
+ } else {
+ mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
+ }
+ }
+ }
+
+ RemoteCollectionCache(Parcel in) {
+ int cacheSize = in.readInt();
+ HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache,
+ this,
+ mApplicationInfoCache,
+ mClassCookies);
+ for (int i = 0; i < cacheSize; i++) {
+ int intentId = in.readInt();
+ String intentUri = in.readString8();
+ RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData);
+ mIdToUriMapping.put(intentId, intentUri);
+ mUriToCollectionMapping.put(intentUri, items);
+ }
+ }
+
+ void setHierarchyDataForId(int intentId, HierarchyRootData data) {
+ String uri = mIdToUriMapping.get(intentId);
+ if (mTempUriToFutureMapping.get(uri) != null) {
+ CompletableFuture<RemoteCollectionItems> itemsFuture =
+ mTempUriToFutureMapping.get(uri);
+ mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
+ rc.setHierarchyRootData(data);
+ return rc;
+ }));
+
+ return;
+ }
+
+ RemoteCollectionItems items = mUriToCollectionMapping.get(uri);
+ items.setHierarchyRootData(data);
+ }
+
+ RemoteCollectionItems getItemsForId(int intentId) {
+ String uri = mIdToUriMapping.get(intentId);
+ return mUriToCollectionMapping.get(uri);
+ }
+
+ void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+ if (inViews.hasSizedRemoteViews()) {
+ for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
+ remoteViews.collectAllIntents();
+ }
+ } else if (inViews.hasLandscapeAndPortraitLayouts()) {
+ inViews.mLandscape.collectAllIntents();
+ inViews.mPortrait.collectAllIntents();
+ } else if (inViews.mActions != null) {
+ for (Action action : inViews.mActions) {
+ if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
+ // Deal with the case where the intent is replaced into the action list
+ if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) {
+ continue;
+ }
+
+ if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
+ String uri = mIdToUriMapping.get(rca.mIntentId);
+ mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
+ rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ continue;
+ }
+
+ // Differentiate between the normal collection actions and the ones with
+ // intents.
+ if (rca.mServiceIntent != null) {
+ String uri = rca.mServiceIntent.toUri(0);
+ int index = mIdToUriMapping.indexOfValue(uri);
+ if (index == -1) {
+ int newIntentId = mIdToUriMapping.size();
+ rca.mIntentId = newIntentId;
+ mIdToUriMapping.put(newIntentId, uri);
+ // mUriToIntentMapping.put(uri, mServiceIntent);
+ mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
+ } else {
+ rca.mIntentId = mIdToUriMapping.keyAt(index);
+ }
+ rca.mItemsFuture = CompletableFuture.completedFuture(null);
+ } else {
+ RemoteCollectionItems items = getCollectionItemsFromFuture(
+ rca.mItemsFuture);
+ for (RemoteViews views : items.mViews) {
+ views.collectAllIntents();
+ }
+ }
+ } else if (action instanceof ViewGroupActionAdd vgaa
+ && vgaa.mNestedViews != null) {
+ vgaa.mNestedViews.collectAllIntents();
+ }
+ }
+ }
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mIdToUriMapping.size());
+ for (int i = 0; i < mIdToUriMapping.size(); i++) {
+ out.writeInt(mIdToUriMapping.keyAt(i));
+ String intentUri = mIdToUriMapping.valueAt(i);
+ out.writeString8(intentUri);
+ RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
+ ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
+ : mUriToCollectionMapping.get(intentUri);
+ items.writeToParcel(out, flags, true);
+ }
+ }
+ }
+
private class SetRemoteViewsAdapterIntent extends Action {
Intent mIntent;
boolean mIsAsync = false;
@@ -3850,9 +4008,12 @@
private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
if (hierarchyRoot == null) {
mBitmapCache = src.mBitmapCache;
+ // We need to create a new instance because we don't reconstruct collection cache
+ mCollectionCache = new RemoteCollectionCache(src.mCollectionCache);
mApplicationInfoCache = src.mApplicationInfoCache;
} else {
mBitmapCache = hierarchyRoot.mBitmapCache;
+ mCollectionCache = hierarchyRoot.mCollectionCache;
mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
}
if (hierarchyRoot == null || src.mIsRoot) {
@@ -3926,6 +4087,7 @@
mBitmapCache = new BitmapCache(parcel);
// Store the class cookies such that they are available when we clone this RemoteView.
mClassCookies = parcel.copyClassCookies();
+ mCollectionCache = new RemoteCollectionCache(parcel);
} else {
configureAsChild(rootData);
}
@@ -4087,6 +4249,7 @@
private void configureAsChild(@NonNull HierarchyRootData rootData) {
mIsRoot = false;
mBitmapCache = rootData.mBitmapCache;
+ mCollectionCache = rootData.mRemoteCollectionCache;
mApplicationInfoCache = rootData.mApplicationInfoCache;
mClassCookies = rootData.mClassCookies;
configureDescendantsAsChildren();
@@ -6357,6 +6520,7 @@
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags);
}
mApplication.writeToParcel(dest, flags);
if (mIsRoot || mIdealSize == null) {
@@ -6373,6 +6537,7 @@
dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags);
}
dest.writeInt(mSizedRemoteViews.size());
for (RemoteViews view : mSizedRemoteViews) {
@@ -6384,6 +6549,7 @@
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags);
}
mLandscape.writeToParcel(dest, flags);
// Both RemoteViews already share the same package and user
@@ -7262,19 +7428,23 @@
}
private HierarchyRootData getHierarchyRootData() {
- return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies);
+ return new HierarchyRootData(mBitmapCache, mCollectionCache,
+ mApplicationInfoCache, mClassCookies);
}
private static final class HierarchyRootData {
final BitmapCache mBitmapCache;
+ final RemoteCollectionCache mRemoteCollectionCache;
final ApplicationInfoCache mApplicationInfoCache;
final Map<Class, Object> mClassCookies;
HierarchyRootData(
BitmapCache bitmapCache,
+ RemoteCollectionCache remoteCollectionCache,
ApplicationInfoCache applicationInfoCache,
Map<Class, Object> classCookies) {
mBitmapCache = bitmapCache;
+ mRemoteCollectionCache = remoteCollectionCache;
mApplicationInfoCache = applicationInfoCache;
mClassCookies = classCookies;
}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 7b7e341..4706dfd 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -19,6 +19,7 @@
import android.os.IBinder;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
+import android.window.RemoteTransition;
import android.window.WindowContainerTransaction;
/** @hide */
@@ -65,7 +66,10 @@
/**
* Requests the server to apply the given {@link WindowContainerTransaction}.
+ *
+ * {@link RemoteTransition} can only be used by a system organizer and
+ * {@code shouldApplyIndependently} must be {@code true}. See {@link registerOrganizer}.
*/
void applyTransaction(in WindowContainerTransaction wct, int transitionType,
- boolean shouldApplyIndependently);
+ boolean shouldApplyIndependently, in RemoteTransition remoteTransition);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index a6c9cec..5c113f8 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -275,7 +275,31 @@
}
wct.setTaskFragmentOrganizer(mInterface);
try {
- getController().applyTransaction(wct, transitionType, shouldApplyIndependently);
+ getController().applyTransaction(
+ wct, transitionType, shouldApplyIndependently, null /* remoteTransition */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Applies a transaction with a {@link RemoteTransition}. Only a system organizer is allowed to
+ * use {@link RemoteTransition}. See {@link TaskFragmentOrganizer#registerOrganizer(boolean)}.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
+ public void applySystemTransaction(@NonNull WindowContainerTransaction wct,
+ @TaskFragmentTransitionType int transitionType,
+ @Nullable RemoteTransition remoteTransition) {
+ if (wct.isEmpty()) {
+ return;
+ }
+ wct.setTaskFragmentOrganizer(mInterface);
+ try {
+ getController().applyTransaction(
+ wct, transitionType, remoteTransition != null /* shouldApplyIndependently */,
+ remoteTransition);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
new file mode 100644
index 0000000..fa81ee3
--- /dev/null
+++ b/core/java/android/window/flags/OWNERS
@@ -0,0 +1 @@
+per-file responsible_apis.aconfig = file:/BAL_OWNERS
\ No newline at end of file
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
new file mode 100644
index 0000000..4bfb177
--- /dev/null
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.window.flags"
+
+flag {
+ name: "bal_require_opt_in_by_pending_intent_creator"
+ namespace: "responsible_apis"
+ description: "Require the PendingIntent creator to opt in starting with Android 15"
+ bug: "296478951"
+}
+
+flag {
+ name: "bal_dont_bring_existing_background_task_stack_to_fg"
+ namespace: "responsible_apis"
+ description: "When starting a PendingIntent with ONLY creator privileges, don't bring the existing task stack to foreground"
+ bug: "296478675"
+}
+
+flag {
+ name: "bal_show_toasts"
+ namespace: "responsible_apis"
+ description: "Enable toasts to indicate (potential) BAL blocking."
+ bug: "308059069"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
new file mode 100644
index 0000000..09be0cf
--- /dev/null
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.window.flags"
+
+flag {
+ name: "always_update_wallpaper_permission"
+ namespace: "wear_frameworks"
+ description: "Allow out of focus process to update wallpaper complications"
+ bug: "271132915"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 5ad5c79..68eddff 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -25,3 +25,10 @@
is_fixed_read_only: true
bug: "304508760"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "transfer_gesture_to_embedded"
+ description: "Enable public API for Window Surfaces"
+ bug: "287076178"
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 77e1502..df6c153 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -106,6 +106,10 @@
/** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
public static final Flag VISIT_RISKY_URIS = devFlag(
"persist.sysui.notification.visit_risky_uris");
+
+ /** b/303716154: For debugging only: use short bitmap duration. */
+ public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag(
+ "persist.sysui.notification.debug_short_bitmap_duration");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 1c5f4f0..cab84bb 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -236,6 +236,9 @@
/** Bind mount app storage dirs to lower fs not via fuse */
public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
+ /** Bind the system properties to an alternate set, for appcompat reasons */
+ public static final String BIND_MOUNT_SYSPROP_OVERRIDES = "--bind-mount-sysprop-overrides";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -353,6 +356,8 @@
* @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
+ * @param bindMountSyspropOverrides True if the zygote needs to mount the override system
+ * properties
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
@@ -361,14 +366,15 @@
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList,
- boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
+ boolean bindMountSyspropOverrides) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs,
- bindMountAppStorageDirs);
+ bindMountAppStorageDirs, bindMountSyspropOverrides);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -391,7 +397,7 @@
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
String[] allowlistedDataInfoList, boolean bindMountAppDataDirs,
- boolean bindMountAppStorageDirs);
+ boolean bindMountAppStorageDirs, boolean bindMountSyspropOverrides);
/**
* Specialize an unspecialized app process. The current VM must have been started
@@ -421,16 +427,19 @@
* @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
+ * @param bindMountSyspropOverrides True if the zygote needs to mount the override system
+ * properties
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
String[] pkgDataInfoList, String[] allowlistedDataInfoList,
- boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
+ boolean bindMountSyspropOverrides) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
pkgDataInfoList, allowlistedDataInfoList,
- bindMountAppDataDirs, bindMountAppStorageDirs);
+ bindMountAppDataDirs, bindMountAppStorageDirs, bindMountSyspropOverrides);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -455,7 +464,8 @@
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
String[] pkgDataInfoList, String[] allowlistedDataInfoList,
- boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
+ boolean bindMountSyspropOverrides);
/**
* Called to do any initialization before starting an application.
@@ -866,7 +876,8 @@
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
- args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
+ args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs,
+ args.mBindMountSyspropOverrides);
// While `specializeAppProcess` sets the thread name on the process's main thread, this
// is distinct from the app process name which appears in stack traces, as the latter is
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index ef83982..86b9a59 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -243,6 +243,11 @@
boolean mBindMountAppDataDirs;
/**
+ * @see Zygote#BIND_MOUNT_SYSPROP_OVERRIDES
+ */
+ boolean mBindMountSyspropOverrides;
+
+ /**
* Constructs instance and parses args
*
* @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count.
@@ -481,6 +486,8 @@
mBindMountAppStorageDirs = true;
} else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
mBindMountAppDataDirs = true;
+ } else if (arg.equals(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES)) {
+ mBindMountSyspropOverrides = true;
} else {
unprocessedArg = arg;
break;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 5fe086d..cbe0700 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,7 +257,8 @@
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
- parsedArgs.mBindMountAppStorageDirs);
+ parsedArgs.mBindMountAppStorageDirs,
+ parsedArgs.mBindMountSyspropOverrides);
try {
if (pid == 0) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index b12e147..9c1bea7 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -59,6 +59,8 @@
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
@@ -1691,6 +1693,131 @@
fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno)));
}
+// All public String android.os.Build constants, and the system properties they're pulled from
+std::pair<const char*, const char*> build_constants[] = {
+ std::pair("ID", "ro.build.id"),
+ std::pair("DISPLAY", "ro.build.display.id"),
+ std::pair("PRODUCT", "ro.product.name"),
+ std::pair("DEVICE", "ro.product.device"),
+ std::pair("BOARD", "ro.product.board"),
+ std::pair("MANUFACTURER", "ro.product.manufacturer"),
+ std::pair("BRAND", "ro.product.brand"),
+ std::pair("MODEL", "ro.product.model"),
+ std::pair("BOOTLOADER", "ro.bootloader"),
+ std::pair("HARDWARE", "ro.hardware"),
+ std::pair("SKU", "ro.boot.hardware.sku"),
+ std::pair("ODM_SKU", "ro.boot.product.hardware.sku"),
+ std::pair("TAGS", "ro.build.tags"),
+ std::pair("TYPE", "ro.build.type"),
+ std::pair("USER", "ro.build.user"),
+ std::pair("HOST", "ro.build.host"),
+};
+
+// All public String Build.VERSION constants, and the system properties they're pulled from
+std::pair<const char*, const char*> build_version_constants[] = {
+ std::pair("INCREMENTAL", "ro.build.version.incremental"),
+ std::pair("RELEASE", "ro.build.version.release"),
+ std::pair("RELEASE_OR_CODENAME", "ro.build.version.release_or_codename"),
+ std::pair("RELEASE_OR_PREVIEW_DISPLAY", "ro.build.version.release_or_preview_display"),
+ std::pair("BASE_OS", "ro.build.version.base_os"),
+ std::pair("SECURITY_PATCH", "ro.build.version.security_patch"),
+ std::pair("SDK", "ro.build.version.sdk"),
+ std::pair("PREVIEW_SDK_FINGERPRINT", "ro.build.version.preview_sdk_fingerprint"),
+ std::pair("CODENAME", "ro.build.version.codename"),
+};
+
+static void ReloadBuildJavaConstant(JNIEnv* env, jclass build_class, const char* field_name,
+ const char* field_signature, const char* sysprop_name) {
+ const prop_info* prop_info = __system_property_find(sysprop_name);
+ std::string new_value;
+ __system_property_read_callback(
+ prop_info,
+ [](void* cookie, const char* name, const char* value, unsigned serial) {
+ auto new_value = reinterpret_cast<std::string*>(cookie);
+ *new_value = value;
+ },
+ &new_value);
+ jfieldID fieldId = env->GetStaticFieldID(build_class, field_name, field_signature);
+ if (strcmp(field_signature, "I") == 0) {
+ env->SetStaticIntField(build_class, fieldId, jint(strtol(new_value.c_str(), nullptr, 0)));
+ } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) {
+ jstring string_val = env->NewStringUTF(new_value.c_str());
+ env->SetStaticObjectField(build_class, fieldId, string_val);
+ } else if (strcmp(field_signature, "[Ljava/lang/String;") == 0) {
+ auto stream = std::stringstream(new_value);
+ std::vector<std::string> items;
+ std::string segment;
+ while (std::getline(stream, segment, ',')) {
+ items.push_back(segment);
+ }
+ jclass string_class = env->FindClass("java/lang/String");
+ jobjectArray string_arr = env->NewObjectArray(items.size(), string_class, nullptr);
+ for (size_t i = 0; i < items.size(); i++) {
+ jstring string_arr_val = env->NewStringUTF(items.at(i).c_str());
+ env->SetObjectArrayElement(string_arr, i, string_arr_val);
+ }
+ env->SetStaticObjectField(build_class, fieldId, string_arr);
+ } else if (strcmp(field_signature, "J") == 0) {
+ env->SetStaticLongField(build_class, fieldId, jlong(strtoll(new_value.c_str(), nullptr, 0)));
+ }
+}
+
+static void ReloadBuildJavaConstants(JNIEnv* env) {
+ jclass build_cls = env->FindClass("android/os/Build");
+ size_t arr_size = sizeof(build_constants) / sizeof(build_constants[0]);
+ for (int i = 0; i < arr_size; i++) {
+ const char* field_name = build_constants[i].first;
+ const char* sysprop_name = build_constants[i].second;
+ ReloadBuildJavaConstant(env, build_cls, field_name, "Ljava/lang/String;", sysprop_name);
+ }
+ jclass build_version_cls = env->FindClass("android/os/Build$VERSION");
+ arr_size = sizeof(build_version_constants) / sizeof(build_version_constants[0]);
+ for (int i = 0; i < arr_size; i++) {
+ const char* field_name = build_version_constants[i].first;
+ const char* sysprop_name = build_version_constants[i].second;
+ ReloadBuildJavaConstant(env, build_version_cls, field_name, "Ljava/lang/String;", sysprop_name);
+ }
+
+ // Reload the public String[] constants
+ ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_ABIS", "[Ljava/lang/String;",
+ "ro.product.cpu.abilist");
+ ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_32_BIT_ABIS", "[Ljava/lang/String;",
+ "ro.product.cpu.abilist32");
+ ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_64_BIT_ABIS", "[Ljava/lang/String;",
+ "ro.product.cpu.abilist64");
+ ReloadBuildJavaConstant(env, build_version_cls, "ALL_CODENAMES", "[Ljava/lang/String;",
+ "ro.build.version.all_codenames");
+
+ // Reload the public int/long constants
+ ReloadBuildJavaConstant(env, build_cls, "TIME", "J", "ro.build.date.utc");
+ ReloadBuildJavaConstant(env, build_version_cls, "SDK_INT", "I", "ro.build.version.sdk");
+ ReloadBuildJavaConstant(env, build_version_cls, "PREVIEW_SDK_INT", "I",
+ "ro.build.version.preview_sdk");
+
+ // Re-derive the fingerprint
+ jmethodID derive_fingerprint =
+ env->GetStaticMethodID(build_cls, "deriveFingerprint", "()Ljava/lang/String;");
+ auto new_fingerprint = (jstring)(env->CallStaticObjectMethod(build_cls, derive_fingerprint));
+ jfieldID fieldId = env->GetStaticFieldID(build_cls, "FINGERPRINT", "Ljava/lang/String;");
+ env->SetStaticObjectField(build_cls, fieldId, new_fingerprint);
+}
+
+static void BindMountSyspropOverride(fail_fn_t fail_fn, JNIEnv* env) {
+ std::string source = "/dev/__properties__/appcompat_override";
+ std::string target = "/dev/__properties__";
+ if (access(source.c_str(), F_OK) != 0) {
+ fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
+ }
+ if (access(target.c_str(), F_OK) != 0) {
+ fail_fn(CREATE_ERROR("Error accessing %s: %s", target.c_str(), strerror(errno)));
+ }
+ BindMount(source, target, fail_fn);
+ // Reload the system properties file, to ensure new values are read into memory
+ __system_properties_zygote_reload();
+ // android.os.Build constants are pulled from system properties, so they must be reloaded, too
+ ReloadBuildJavaConstants(env);
+}
+
static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
const char* dir_name, const char* package, fail_fn_t fail_fn) {
bool hasSdcardFs = IsSdcardfsUsed();
@@ -1754,7 +1881,7 @@
jstring managed_instruction_set, jstring managed_app_data_dir,
bool is_top_app, jobjectArray pkg_data_info_list,
jobjectArray allowlisted_data_info_list, bool mount_data_dirs,
- bool mount_storage_dirs) {
+ bool mount_storage_dirs, bool mount_sysprop_overrides) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1807,6 +1934,10 @@
fail_fn);
}
+ if (mount_sysprop_overrides) {
+ BindMountSyspropOverride(fail_fn, env);
+ }
+
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {
@@ -2360,7 +2491,7 @@
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list,
- jboolean mount_data_dirs, jboolean mount_storage_dirs) {
+ jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2403,7 +2534,7 @@
mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
- mount_storage_dirs == JNI_TRUE);
+ mount_storage_dirs == JNI_TRUE, mount_sysprop_overrides == JNI_TRUE);
}
return pid;
}
@@ -2439,7 +2570,7 @@
effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
/* pkg_data_info_list */ nullptr,
- /* allowlisted_data_info_list */ nullptr, false, false);
+ /* allowlisted_data_info_list */ nullptr, false, false, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2591,14 +2722,14 @@
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
jboolean is_top_app, jobjectArray pkg_data_info_list,
jobjectArray allowlisted_data_info_list, jboolean mount_data_dirs,
- jboolean mount_storage_dirs) {
+ jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
- mount_storage_dirs == JNI_TRUE);
+ mount_storage_dirs == JNI_TRUE, mount_sysprop_overrides == JNI_TRUE);
}
/**
@@ -2876,7 +3007,7 @@
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
(void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
{"nativeForkSystemServer", "(II[II[[IJJ)I",
(void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2892,7 +3023,7 @@
(void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)V",
(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
{"nativeInitNativeState", "(Z)V",
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8c91be8..ec302e7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2358,6 +2358,15 @@
<permission android:name="android.permission.SUSPEND_APPS"
android:protectionLevel="signature|role" />
+ <!-- @SystemApi
+ @hide
+ @FlaggedApi("android.content.pm.quarantined_enabled")
+ Allows an application to quarantine other apps, which will prevent
+ them from running without explicit user action.
+ -->
+ <permission android:name="android.permission.QUARANTINE_APPS"
+ android:protectionLevel="internal|verifier" />
+
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
-->
@@ -4065,6 +4074,7 @@
<!-- Allow apps to always update wallpaper by sending data.
@SystemApi
@hide
+ @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission")
-->
<permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER"
android:protectionLevel="internal|role" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ab71b41..862e537 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2213,7 +2213,10 @@
<bool name="config_single_volume">false</bool>
<!-- Volume policy -->
+ <!-- Strongly consider keeping these 2 values in sync - otherwise vol up/vol down have confusing
+ asymmetric behavior, as in b/279645379 -->
<bool name="config_volume_down_to_enter_silent">false</bool>
+ <bool name="config_volume_up_to_exit_silent">false</bool>
<!-- The number of volume steps for the notification stream -->
<integer name="config_audio_notif_vol_steps">7</integer>
@@ -4174,6 +4177,10 @@
<!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
the device to be "idle" after being inactive for this long. -->
<integer name="config_jobSchedulerInactivityIdleThreshold">1860000</integer>
+ <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
+ the device to be "idle" after being inactive for this long if the device is on stable
+ power. Stable power is defined as "charging + battery not low". -->
+ <integer name="config_jobSchedulerInactivityIdleThresholdOnStablePower">1860000</integer>
<!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
<integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
@@ -4369,6 +4376,15 @@
UI is handled by ActivityManagerService -->
<bool name="config_customUserSwitchUi">false</bool>
+ <!-- Flag specifying whether the first account added can be removed or renamed. By default,
+ this ability is enabled. When false, user will not be able to remove the first account. -->
+ <bool name="config_canRemoveFirstAccount">true</bool>
+
+ <!-- Used together with config_canRemoveOrRenameFirstAccount when set to false. By default, this
+ is blank. Check if the first account is of this account type. If it is, then disable
+ remove/rename. -->
+ <string name="config_accountTypeToKeepFirstAccount"></string>
+
<!-- A array of regex to treat a SMS as VVM SMS if the message body matches.
Each item represents an entry, which consists of two parts:
a comma (,) separated list of MCCMNC the regex applies to, followed by a semicolon (;), and
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1aa1fea..e646548 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -328,6 +328,7 @@
<java-symbol type="integer" name="config_phonenumber_compare_min_match" />
<java-symbol type="bool" name="config_single_volume" />
<java-symbol type="bool" name="config_volume_down_to_enter_silent" />
+ <java-symbol type="bool" name="config_volume_up_to_exit_silent" />
<java-symbol type="bool" name="config_voice_capable" />
<java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
<java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
@@ -1722,6 +1723,8 @@
<java-symbol type="bool" name="config_startDreamImmediatelyOnDock" />
<java-symbol type="bool" name="config_carDockEnablesAccelerometer" />
<java-symbol type="bool" name="config_customUserSwitchUi" />
+ <java-symbol type="bool" name="config_canRemoveFirstAccount" />
+ <java-symbol type="string" name="config_accountTypeToKeepFirstAccount" />
<java-symbol type="bool" name="config_deskDockEnablesAccelerometer" />
<java-symbol type="bool" name="config_disableMenuKeyInLockScreen" />
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
@@ -2879,6 +2882,7 @@
<java-symbol type="integer" name="config_defaultNightMode" />
<java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
+ <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThresholdOnStablePower" />
<java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
<java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
<java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
diff --git a/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
new file mode 100644
index 0000000..839b645
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/UsageEventsQueryTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 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.usage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.usage.UsageEvents.Event;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UsageEventsQueryTest {
+ @Test
+ public void testQueryDuration() {
+ // Test with negative beginTimeMillis.
+ long beginTimeMillis = -100;
+ long endTimeMillis = 100;
+ try {
+ UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+ .build();
+ fail("beginTimeMillis should be a non-negative timestamp measured as the number of"
+ + " milliseconds since 1970-01-01T00:00:00Z.");
+ } catch (IllegalArgumentException e) {
+ // Expected, fall through;
+ }
+
+ // Test with negative endTimeMillis.
+ beginTimeMillis = 1001;
+ endTimeMillis = -1;
+ try {
+ UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+ .build();
+ fail("endTimeMillis should be a non-negative timestamp measured as the number of"
+ + " milliseconds since 1970-01-01T00:00:00Z.");
+ } catch (IllegalArgumentException e) {
+ // Expected, fall through;
+ }
+
+ // Test with beginTimeMillis < endTimeMillis;
+ beginTimeMillis = 2001;
+ endTimeMillis = 1000;
+ try {
+ UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+ .build();
+ fail("beginTimeMillis should be smaller than endTimeMillis");
+ } catch (IllegalArgumentException e) {
+ // Expected, fall through;
+ }
+
+ // Test with beginTimeMillis == endTimeMillis, valid.
+ beginTimeMillis = 1001;
+ endTimeMillis = 1001;
+ try {
+ UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+ .build();
+ assertEquals(query.getBeginTimeMillis(), query.getEndTimeMillis());
+ } catch (IllegalArgumentException e) {
+ // Not expected for valid duration.
+ fail("Valid duration for beginTimeMillis=" + beginTimeMillis
+ + ", endTimeMillis=" + endTimeMillis);
+ }
+
+ beginTimeMillis = 2001;
+ endTimeMillis = 3001;
+ try {
+ UsageEventsQuery query = new UsageEventsQuery.Builder(beginTimeMillis, endTimeMillis)
+ .build();
+ assertEquals(query.getBeginTimeMillis(), 2001);
+ assertEquals(query.getEndTimeMillis(), 3001);
+ } catch (IllegalArgumentException e) {
+ // Not expected for valid duration.
+ fail("Valid duration for beginTimeMillis=" + beginTimeMillis
+ + ", endTimeMillis=" + endTimeMillis);
+ }
+ }
+
+ @Test
+ public void testQueryEventTypes() {
+ Random rnd = new Random();
+ UsageEventsQuery.Builder queryBuilder = new UsageEventsQuery.Builder(1000, 2000);
+
+ // Test with invalid event type.
+ int eventType = Event.NONE - 1;
+ try {
+ queryBuilder.addEventTypes(eventType);
+ fail("Invalid event type: " + eventType);
+ } catch (IllegalArgumentException e) {
+ // Expected, fall through.
+ }
+
+ eventType = Event.MAX_EVENT_TYPE + 1;
+ try {
+ queryBuilder.addEventTypes(eventType);
+ fail("Invalid event type: " + eventType);
+ } catch (IllegalArgumentException e) {
+ // Expected, fall through.
+ }
+
+ // Test with valid and duplicate event types.
+ eventType = rnd.nextInt(Event.MAX_EVENT_TYPE + 1);
+ try {
+ UsageEventsQuery query = queryBuilder.addEventTypes(eventType, eventType, eventType)
+ .build();
+ Set<Integer> eventTypeSet = query.getEventTypes();
+ assertEquals(eventTypeSet.size(), 1);
+ int type = eventTypeSet.iterator().next();
+ assertEquals(type, eventType);
+ } catch (IllegalArgumentException e) {
+ fail("Valid event type: " + eventType);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 21d1dbb..5d213ca 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -19,11 +19,17 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.AndroidTestCase;
import androidx.test.InstrumentationRegistry;
@@ -31,6 +37,7 @@
import androidx.test.uiautomator.UiDevice;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,6 +71,10 @@
System.loadLibrary("powermanagertest_jni");
}
+ // Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
/**
* Setup any common data for the upcoming tests.
*/
@@ -454,4 +465,27 @@
parcelBatterySaverPolicyConfigToNativeAndVerify(bs2);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
+ public void testBatterySaverSupported_isSupported() throws RemoteException {
+ IPowerManager powerManager = mock(IPowerManager.class);
+ PowerManager pm = new PowerManager(mContext, powerManager,
+ mock(IThermalService.class),
+ Handler.createAsync(Looper.getMainLooper()));
+ when(powerManager.isBatterySaverSupported()).thenReturn(true);
+
+ assertTrue(pm.isBatterySaverSupported());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
+ public void testBatterySaverSupported_isNotSupported() throws RemoteException {
+ IPowerManager powerManager = mock(IPowerManager.class);
+ PowerManager pm = new PowerManager(mContext, powerManager,
+ mock(IThermalService.class),
+ Handler.createAsync(Looper.getMainLooper()));
+ when(powerManager.isBatterySaverSupported()).thenReturn(false);
+
+ assertFalse(pm.isBatterySaverSupported());
+ }
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 5c411d5..35ddfdb 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -23,6 +23,7 @@
import android.content.ContentCaptureOptions;
import android.content.Context;
+import android.view.WindowManager;
import com.android.internal.util.RingBuffer;
@@ -147,6 +148,52 @@
assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse();
}
+ @Test
+ public void testUpdateWindowAttribute_setFlagSecure() {
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+ // Ensure main session is created.
+ final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
+ initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+
+ manager.updateWindowAttributes(initialParam);
+
+ assertThat(manager.isContentCaptureEnabled()).isFalse();
+ }
+
+ @Test
+ public void testUpdateWindowAttribute_clearFlagSecure() {
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+ // Ensure main session is created.
+ final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
+ initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+ // Default param does not have FLAG_SECURE set.
+ final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
+
+ manager.updateWindowAttributes(initialParam);
+ manager.updateWindowAttributes(resetParam);
+
+ assertThat(manager.isContentCaptureEnabled()).isTrue();
+ }
+
+ @Test
+ public void testUpdateWindowAttribute_clearFlagSecureAfterDisabledByApp() {
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+ // Ensure main session is created.
+ final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ // Default param does not have FLAG_SECURE set.
+ final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
+
+ manager.setContentCaptureEnabled(false);
+ manager.updateWindowAttributes(resetParam);
+
+ assertThat(manager.isContentCaptureEnabled()).isFalse();
+ }
+
private ContentCaptureOptions createOptions(
ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
return new ContentCaptureOptions(
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 15d26eb..2732569 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -272,6 +272,17 @@
void positionChanged(long frameNumber, int left, int top, int right, int bottom);
/**
+ * Called by native by a Rendering Worker thread to update window position; includes
+ * the local rect that represents the clipped area of the RenderNode's bounds.
+ *
+ * @hide
+ */
+ default void positionChanged(long frameNumber, int left, int top, int right, int bottom,
+ int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ positionChanged(frameNumber, left, top, right, bottom);
+ }
+
+ /**
* Called by JNI
*
* @hide */
@@ -287,6 +298,23 @@
}
/**
+ * Called by JNI
+ *
+ * @hide */
+ static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener,
+ long frameNumber, int left, int top, int right, int bottom,
+ int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ final PositionUpdateListener listener = weakListener.get();
+ if (listener != null) {
+ listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft,
+ clipTop, clipRight, clipBottom);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Call to apply a stretch effect to any child SurfaceControl layers
*
* TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
@@ -371,6 +399,15 @@
}
@Override
+ public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
+ int clipLeft, int clipTop, int clipRight, int clipBottom) {
+ for (PositionUpdateListener pul : mListeners) {
+ pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop,
+ clipRight, clipBottom);
+ }
+ }
+
+ @Override
public void positionLost(long frameNumber) {
for (PositionUpdateListener pul : mListeners) {
pul.positionLost(frameNumber);
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 2d2dd24..b4b3e92 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -37,7 +39,10 @@
public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
- private static IKeystoreAuthorization getService() {
+ /**
+ * @return an instance of IKeystoreAuthorization
+ */
+ public static IKeystoreAuthorization getService() {
return IKeystoreAuthorization.Stub.asInterface(
ServiceManager.checkService("android.security.authorization"));
}
@@ -100,4 +105,24 @@
}
}
+ /**
+ * Gets the last authentication time of the given user and authenticators.
+ *
+ * @param userId user id
+ * @param authenticatorTypes an array of {@link HardwareAuthenticatorType}.
+ * @return the last authentication time or
+ * {@link BiometricConstants#BIOMETRIC_NO_AUTHENTICATION}.
+ */
+ public static long getLastAuthenticationTime(
+ long userId, @HardwareAuthenticatorType int[] authenticatorTypes) {
+ try {
+ return getService().getLastAuthTime(userId, authenticatorTypes);
+ } catch (RemoteException | NullPointerException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
}
diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java
index af188a9..464714f 100644
--- a/keystore/java/android/security/GateKeeper.java
+++ b/keystore/java/android/security/GateKeeper.java
@@ -45,8 +45,19 @@
@UnsupportedAppUsage
public static long getSecureUserId() throws IllegalStateException {
+ return getSecureUserId(UserHandle.myUserId());
+ }
+
+ /**
+ * Return the secure user id for a given user id
+ * @param userId the user id, e.g. 0
+ * @return the secure user id or {@link GateKeeper#INVALID_SECURE_USER_ID} if no such mapping
+ * for the given user id is found.
+ * @throws IllegalStateException if there is an error retrieving the secure user id
+ */
+ public static long getSecureUserId(int userId) throws IllegalStateException {
try {
- return getService().getSecureUserId(UserHandle.myUserId());
+ return getService().getSecureUserId(userId);
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to obtain secure user ID from gatekeeper", e);
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1ba41b1..b714035 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1596,6 +1596,8 @@
* {@link #getAttestationChallenge()} returns non-null and the spec is used to generate a
* symmetric (AES or HMAC) key, {@link javax.crypto.KeyGenerator#generateKey()} will throw
* {@link java.security.InvalidAlgorithmParameterException}.
+ *
+ * <p>The challenge may be up to 128 bytes.
*/
@NonNull
public Builder setAttestationChallenge(byte[] attestationChallenge) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index a663f9f..ed99501 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -20,6 +20,7 @@
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -40,12 +41,17 @@
*/
public class WindowExtensionsImpl implements WindowExtensions {
+ private static final String TAG = "WindowExtensionsImpl";
private final Object mLock = new Object();
private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
private volatile WindowLayoutComponentImpl mWindowLayoutComponent;
private volatile SplitController mSplitController;
private volatile WindowAreaComponent mWindowAreaComponent;
+ public WindowExtensionsImpl() {
+ Log.i(TAG, "Initializing Window Extensions.");
+ }
+
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 76f0b67..4973a4d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -156,6 +156,7 @@
public SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent,
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
+ Log.i(TAG, "Initializing Activity Embedding Controller.");
final MainThreadExecutor executor = new MainThreadExecutor();
mHandler = executor.mHandler;
mPresenter = new SplitPresenter(executor, windowLayoutComponent, this);
@@ -208,6 +209,7 @@
@Override
public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
synchronized (mLock) {
+ Log.i(TAG, "Setting embedding rules. Size: " + rules.size());
mSplitRules.clear();
mSplitRules.addAll(rules);
}
@@ -216,6 +218,7 @@
@Override
public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) {
synchronized (mLock) {
+ Log.i(TAG, "Request to pin top activity stack.");
final TaskContainer task = getTaskContainer(taskId);
if (task == null) {
Log.e(TAG, "Cannot find the task for id: " + taskId);
@@ -272,6 +275,7 @@
@Override
public void unpinTopActivityStack(int taskId){
synchronized (mLock) {
+ Log.i(TAG, "Request to unpin top activity stack.");
final TaskContainer task = getTaskContainer(taskId);
if (task == null) {
Log.e(TAG, "Cannot find the task to unpin, id: " + taskId);
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 10c9562..d8ae9c8 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -26,7 +26,8 @@
android:id="@+id/bubble_manage_menu_dismiss_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="@dimen/bubble_menu_item_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
android:paddingStart="@dimen/bubble_menu_padding"
android:paddingEnd="@dimen/bubble_menu_padding"
@@ -52,7 +53,8 @@
android:id="@+id/bubble_manage_menu_dont_bubble_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="@dimen/bubble_menu_item_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
android:paddingStart="@dimen/bubble_menu_padding"
android:paddingEnd="@dimen/bubble_menu_padding"
@@ -78,7 +80,8 @@
android:id="@+id/bubble_manage_menu_settings_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="@dimen/bubble_menu_item_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
android:paddingStart="@dimen/bubble_menu_padding"
android:paddingEnd="@dimen/bubble_menu_padding"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 4d87c95..ac75c73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -255,8 +255,13 @@
int offsetLayer = TYPE_LAYER_OFFSET;
final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
for (TransitionInfo.Change change : openingChanges) {
+ final Animation animation =
+ animationProvider.get(info, change, openingWholeScreenBounds);
+ if (animation.getDuration() == 0) {
+ continue;
+ }
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
- info, change, animationProvider, openingWholeScreenBounds);
+ info, change, animation, openingWholeScreenBounds);
if (isOpening) {
adapter.overrideLayer(offsetLayer++);
}
@@ -275,8 +280,13 @@
adapters.add(snapshotAdapter);
}
}
+ final Animation animation =
+ animationProvider.get(info, change, closingWholeScreenBounds);
+ if (animation.getDuration() == 0) {
+ continue;
+ }
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
- info, change, animationProvider, closingWholeScreenBounds);
+ info, change, animation, closingWholeScreenBounds);
if (!isOpening) {
adapter.overrideLayer(offsetLayer++);
}
@@ -353,8 +363,7 @@
@NonNull
private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter(
@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
- @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) {
- final Animation animation = animationProvider.get(info, change, wholeAnimationBounds);
+ @NonNull Animation animation, @NonNull Rect wholeAnimationBounds) {
return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
wholeAnimationBounds, TransitionUtil.getRootFor(change, info));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index cc9c2be..6cd1324 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -22,6 +22,8 @@
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.animation.AlphaAnimation;
@@ -34,8 +36,6 @@
import android.view.animation.TranslateAnimation;
import android.window.TransitionInfo;
-import androidx.annotation.NonNull;
-
import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.util.TransitionUtil;
@@ -201,11 +201,10 @@
Animation loadOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ final Animation customAnimation = loadCustomAnimation(info, isEnter);
final Animation animation;
- if (options != null && options.getType() == ANIM_CUSTOM) {
- animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
- isEnter ? options.getEnterResId() : options.getExitResId());
+ if (customAnimation != null) {
+ animation = customAnimation;
} else if (shouldShowBackdrop(info, change)) {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? com.android.internal.R.anim.task_fragment_clear_top_open_enter
@@ -229,11 +228,10 @@
Animation loadCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ final Animation customAnimation = loadCustomAnimation(info, isEnter);
final Animation animation;
- if (options != null && options.getType() == ANIM_CUSTOM) {
- animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
- isEnter ? options.getEnterResId() : options.getExitResId());
+ if (customAnimation != null) {
+ animation = customAnimation;
} else if (shouldShowBackdrop(info, change)) {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? com.android.internal.R.anim.task_fragment_clear_top_close_enter
@@ -259,4 +257,21 @@
mTransitionAnimation, false);
return a != null && a.getShowBackdrop();
}
+
+ @Nullable
+ private Animation loadCustomAnimation(@NonNull TransitionInfo info, boolean isEnter) {
+ final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ if (options == null || options.getType() != ANIM_CUSTOM) {
+ return null;
+ }
+ final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
+ isEnter ? options.getEnterResId() : options.getExitResId());
+ if (anim != null) {
+ return anim;
+ }
+ // The app may be intentional to use an invalid resource as a no-op animation.
+ // ActivityEmbeddingAnimationRunner#createOpenCloseAnimationAdapters will skip the
+ // animation with duration 0. Then it will use prepareForJumpCut for empty adapters.
+ return new AlphaAnimation(1f, 1f);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index 24479d7..a596cef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -353,7 +353,6 @@
closingLeft += mapRange(interpolatedProgress, deltaXMin, deltaXMax);
// Move the window along the Y axis.
- final float deltaYRatio = (touchY - mInitialTouchPos.y) / height;
final float closingTop = (height - closingHeight) * 0.5f;
targetRect.set(
closingLeft, closingTop, closingLeft + closingWidth, closingTop + closingHeight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 84feb03..108aa82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -129,9 +129,7 @@
@JvmStatic
fun getTaskSnapshot(taskId: Int, isLowResolution: Boolean): TaskSnapshot? {
return if (taskId <= 0) null else try {
- ActivityTaskManager.getService().getTaskSnapshot(
- taskId, isLowResolution, false /* takeSnapshotIfNeeded */
- )
+ ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution)
} catch (e: RemoteException) {
Log.e(TAG, "Failed to get task snapshot, taskId=$taskId", e)
null
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 ea7b2e9..64294c9 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
@@ -103,6 +103,7 @@
import com.android.wm.shell.taskview.TaskViewFactory;
import com.android.wm.shell.taskview.TaskViewFactoryController;
import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
@@ -613,14 +614,22 @@
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ HomeTransitionObserver homeTransitionObserver) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
pool, displayController, mainExecutor, mainHandler, animExecutor,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer, homeTransitionObserver);
+ }
+
+ @WMSingleton
+ @Provides
+ static HomeTransitionObserver provideHomeTransitionObserver(Context context,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new HomeTransitionObserver(context, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a533ca5..47769a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -78,6 +78,7 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.DefaultMixedHandler;
+import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -380,9 +381,10 @@
static RecentsTransitionHandler provideRecentsTransitionHandler(
ShellInit shellInit,
Transitions transitions,
- Optional<RecentTasksController> recentTasksController) {
+ Optional<RecentTasksController> recentTasksController,
+ HomeTransitionObserver homeTransitionObserver) {
return new RecentsTransitionHandler(shellInit, transitions,
- recentTasksController.orElse(null));
+ recentTasksController.orElse(null), homeTransitionObserver);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 1898ea7..3b48c67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -22,6 +22,8 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
@@ -29,6 +31,7 @@
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip2.phone.PipController;
+import com.android.wm.shell.pip2.phone.PipScheduler;
import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -52,9 +55,10 @@
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
- Optional<PipController> pipController) {
+ Optional<PipController> pipController,
+ @NonNull PipScheduler pipScheduler) {
return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
- pipBoundsAlgorithm);
+ pipBoundsAlgorithm, pipScheduler);
}
@WMSingleton
@@ -73,4 +77,12 @@
pipDisplayLayoutState));
}
}
+
+ @WMSingleton
+ @Provides
+ static PipScheduler providePipScheduler(Context context,
+ PipBoundsState pipBoundsState,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new PipScheduler(context, pipBoundsState, mainExecutor);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 412a5b5..8e12991 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -343,9 +343,8 @@
task.taskId
)
val wct = WindowContainerTransaction()
- wct.setWindowingMode(task.token, WINDOWING_MODE_MULTI_WINDOW)
wct.setBounds(task.token, Rect())
- wct.setDensityDpi(task.token, getDefaultDensityDpi())
+ addMoveToSplitChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
} else {
@@ -827,7 +826,9 @@
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo
) {
- wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
+ // Explicitly setting multi-window at task level interferes with animations.
+ // Let task inherit windowing mode once transition is complete instead.
+ wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED)
// The task's density may have been overridden in freeform; revert it here as we don't
// want it overridden in multi-window.
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
new file mode 100644
index 0000000..9bb383f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 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.pip2.phone;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip.PipTransitionController;
+
+/**
+ * Scheduler for Shell initiated PiP transitions and animations.
+ */
+public class PipScheduler {
+ private static final String TAG = PipScheduler.class.getSimpleName();
+ private static final String BROADCAST_FILTER = PipScheduler.class.getCanonicalName();
+
+ private final Context mContext;
+ private final PipBoundsState mPipBoundsState;
+ private final ShellExecutor mMainExecutor;
+ private PipSchedulerReceiver mSchedulerReceiver;
+ private PipTransitionController mPipTransitionController;
+
+ // pinned PiP task's WC token
+ @Nullable
+ private WindowContainerToken mPipTaskToken;
+
+ // pinned PiP task's leash
+ @Nullable
+ private SurfaceControl mPinnedTaskLeash;
+
+ // the leash of the original task of the PiP activity;
+ // used to synchronize app drawings in the multi-activity case
+ @Nullable
+ private SurfaceControl mOriginalTaskLeash;
+
+ /**
+ * A temporary broadcast receiver to initiate exit PiP via expand.
+ * This will later be modified to be triggered by the PiP menu.
+ */
+ private class PipSchedulerReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ scheduleExitPipViaExpand();
+ }
+ }
+
+ public PipScheduler(Context context, PipBoundsState pipBoundsState,
+ ShellExecutor mainExecutor) {
+ mContext = context;
+ mPipBoundsState = pipBoundsState;
+ mMainExecutor = mainExecutor;
+
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ // temporary broadcast receiver to initiate exit PiP via expand
+ mSchedulerReceiver = new PipSchedulerReceiver();
+ ContextCompat.registerReceiver(mContext, mSchedulerReceiver,
+ new IntentFilter(BROADCAST_FILTER), ContextCompat.RECEIVER_EXPORTED);
+ }
+ }
+
+ void setPipTransitionController(PipTransitionController pipTransitionController) {
+ mPipTransitionController = pipTransitionController;
+ }
+
+ void setPinnedTaskLeash(SurfaceControl pinnedTaskLeash) {
+ mPinnedTaskLeash = pinnedTaskLeash;
+ }
+
+ void setOriginalTaskLeash(SurfaceControl originalTaskLeash) {
+ mOriginalTaskLeash = originalTaskLeash;
+ }
+
+ void setPipTaskToken(@Nullable WindowContainerToken pipTaskToken) {
+ mPipTaskToken = pipTaskToken;
+ }
+
+ @Nullable
+ private WindowContainerTransaction getExitPipViaExpandTransaction() {
+ if (mPipTaskToken == null || mPinnedTaskLeash == null) {
+ return null;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ // final expanded bounds to be inherited from the parent
+ wct.setBounds(mPipTaskToken, null);
+ // if we are hitting a multi-activity case
+ // windowing mode change will reparent to original host task
+ wct.setWindowingMode(mPipTaskToken, WINDOWING_MODE_UNDEFINED);
+ return wct;
+ }
+
+ /**
+ * Schedules exit PiP via expand transition.
+ */
+ public void scheduleExitPipViaExpand() {
+ WindowContainerTransaction wct = getExitPipViaExpandTransaction();
+ if (wct != null) {
+ mMainExecutor.execute(() -> {
+ mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct,
+ null /* destinationBounds */);
+ });
+ }
+ }
+
+ void onExitPip() {
+ mPipTaskToken = null;
+ mPinnedTaskLeash = null;
+ mOriginalTaskLeash = null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index d704b09..7d3bd65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -16,8 +16,12 @@
package com.android.wm.shell.pip2.phone;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
@@ -26,6 +30,7 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
@@ -43,8 +48,15 @@
* Implementation of transitions for PiP on phone.
*/
public class PipTransition extends PipTransitionController {
+ private static final String TAG = PipTransition.class.getSimpleName();
+
+ private PipScheduler mPipScheduler;
+ @Nullable
+ private WindowContainerToken mPipTaskToken;
@Nullable
private IBinder mAutoEnterButtonNavTransition;
+ @Nullable
+ private IBinder mExitViaExpandTransition;
public PipTransition(
@NonNull ShellInit shellInit,
@@ -52,9 +64,13 @@
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm) {
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipScheduler pipScheduler) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
+
+ mPipScheduler = pipScheduler;
+ mPipScheduler.setPipTransitionController(this);
}
@Override
@@ -64,6 +80,18 @@
}
}
+ @Override
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @android.annotation.Nullable Rect destinationBounds) {
+ if (out == null) {
+ return;
+ }
+ IBinder transition = mTransitions.startTransition(type, out, this);
+ if (type == TRANSIT_EXIT_PIP) {
+ mExitViaExpandTransition = transition;
+ }
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -84,8 +112,18 @@
}
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {}
+
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
+ // cache the original task token to check for multi-activity case later
final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
@@ -93,6 +131,8 @@
// calculate the entry bounds and notify core to move task to pinned with final bounds
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ mPipBoundsState.setBounds(entryBounds);
+
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
return wct;
@@ -121,19 +161,59 @@
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition == mAutoEnterButtonNavTransition) {
+ mAutoEnterButtonNavTransition = null;
+ TransitionInfo.Change pipChange = getPipChange(info);
+ if (pipChange == null) {
+ return false;
+ }
+ mPipTaskToken = pipChange.getContainer();
+
+ // cache the PiP task token and the relevant leashes
+ mPipScheduler.setPipTaskToken(mPipTaskToken);
+ mPipScheduler.setPinnedTaskLeash(pipChange.getLeash());
+ // check if we entered PiP from a multi-activity task and set the original task leash
+ final int lastParentTaskId = pipChange.getTaskInfo().lastParentTaskIdBeforePip;
+ final boolean isSingleActivity = lastParentTaskId == INVALID_TASK_ID;
+ mPipScheduler.setOriginalTaskLeash(isSingleActivity ? null :
+ findChangeByTaskId(info, lastParentTaskId).getLeash());
+
startTransaction.apply();
finishCallback.onTransitionFinished(null);
return true;
+ } else if (transition == mExitViaExpandTransition) {
+ mExitViaExpandTransition = null;
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null);
+ onExitPip();
+ return true;
}
return false;
}
- @Override
- public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+ @Nullable
+ private TransitionInfo.Change getPipChange(TransitionInfo info) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ return change;
+ }
+ }
+ return null;
+ }
- @Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishT) {}
+ @Nullable
+ private TransitionInfo.Change findChangeByTaskId(TransitionInfo info, int taskId) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().taskId == taskId) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+ private void onExitPip() {
+ mPipTaskToken = null;
+ mPipScheduler.onExitPip();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index d277eef..c20d23e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -59,6 +59,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;
@@ -85,11 +86,15 @@
*/
private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();
+ private final HomeTransitionObserver mHomeTransitionObserver;
+
public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
- @Nullable RecentTasksController recentTasksController) {
+ @Nullable RecentTasksController recentTasksController,
+ HomeTransitionObserver homeTransitionObserver) {
mTransitions = transitions;
mExecutor = transitions.getMainExecutor();
mRecentTasksController = recentTasksController;
+ mHomeTransitionObserver = homeTransitionObserver;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
if (recentTasksController == null) return;
shellInit.addInitCallback(() -> {
@@ -911,6 +916,11 @@
Slog.e(TAG, "Duplicate call to finish");
return;
}
+ if (!toHome) {
+ // For some transitions, we may have notified home activity that it became visible.
+ // We need to notify the observer that we are no longer going home.
+ mHomeTransitionObserver.notifyHomeVisibilityChanged(false);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.finishInner: toHome=%b userLeave=%b "
+ "willFinishToHome=%b state=%d",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9cd318f..723a4a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -40,7 +40,6 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
-import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -422,11 +421,6 @@
continue;
}
- // The back gesture has animated this change before transition happen, so here we don't
- // play the animation again.
- if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
- continue;
- }
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index f561aa5..b528089d15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -38,24 +38,15 @@
*/
public class HomeTransitionObserver implements TransitionObserver,
RemoteCallable<HomeTransitionObserver> {
- private final SingleInstanceRemoteListener<HomeTransitionObserver, IHomeTransitionListener>
+ private SingleInstanceRemoteListener<HomeTransitionObserver, IHomeTransitionListener>
mListener;
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
- private @NonNull final Transitions mTransitions;
-
public HomeTransitionObserver(@NonNull Context context,
- @NonNull ShellExecutor mainExecutor,
- @NonNull Transitions transitions) {
+ @NonNull ShellExecutor mainExecutor) {
mContext = context;
mMainExecutor = mainExecutor;
- mTransitions = transitions;
-
- mListener = new SingleInstanceRemoteListener<>(this,
- c -> mTransitions.registerObserver(this),
- c -> mTransitions.unregisterObserver(this));
-
}
@Override
@@ -72,7 +63,7 @@
final int mode = change.getMode();
if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
&& TransitionUtil.isOpenOrCloseMode(mode)) {
- mListener.call(l -> l.onHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)));
+ notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode));
}
}
}
@@ -92,7 +83,14 @@
* Sets the home transition listener that receives any transitions resulting in a change of
*
*/
- public void setHomeTransitionListener(IHomeTransitionListener listener) {
+ public void setHomeTransitionListener(Transitions transitions,
+ IHomeTransitionListener listener) {
+ if (mListener == null) {
+ mListener = new SingleInstanceRemoteListener<>(this,
+ c -> transitions.registerObserver(this),
+ c -> transitions.unregisterObserver(this));
+ }
+
if (listener != null) {
mListener.register(listener);
} else {
@@ -100,6 +98,16 @@
}
}
+ /**
+ * Notifies the listener that the home visibility has changed.
+ * @param isVisible true when home activity is visible, false otherwise.
+ */
+ public void notifyHomeVisibilityChanged(boolean isVisible) {
+ if (mListener != null) {
+ mListener.call(l -> l.onHomeVisibilityChanged(isVisible));
+ }
+ }
+
@Override
public Context getContext() {
return mContext;
@@ -113,7 +121,7 @@
/**
* Invalidates this controller, preventing future calls to send updates.
*/
- public void invalidate() {
- mTransitions.unregisterObserver(this);
+ public void invalidate(Transitions transitions) {
+ transitions.unregisterObserver(this);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index b1fc16d..030f601 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -44,7 +44,7 @@
private IBinder mTransition = null;
/** The remote to delegate animation to */
- private final RemoteTransition mRemote;
+ private RemoteTransition mRemote;
public OneShotRemoteHandler(@NonNull ShellExecutor mainExecutor,
@NonNull RemoteTransition remote) {
@@ -83,6 +83,8 @@
mMainExecutor.execute(() -> {
finishCallback.onTransitionFinished(wct);
});
+ Log.d("b/302551868", "OneShotRemoteHandler#start remote anim null");
+ mRemote = null;
}
};
Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
@@ -105,6 +107,8 @@
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
finishCallback.onTransitionFinished(null /* wct */);
+ Log.d("b/302551868", "OneShotRemoteHandler#exception remote anim null");
+ mRemote = null;
}
return true;
}
@@ -123,6 +127,8 @@
// so just assume the worst-case and clear the local transaction.
t.clear();
mMainExecutor.execute(() -> finishCallback.onTransitionFinished(wct));
+ Log.d("b/302551868", "OneShotRemoteHandler#merge remote anim null");
+ mRemote = null;
}
};
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index baa9aca..ab5c063 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -191,6 +192,8 @@
private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
+ private HomeTransitionObserver mHomeTransitionObserver;
+
/** List of {@link Runnable} instances to run when the last active transition has finished. */
private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
@@ -204,12 +207,6 @@
*/
private static final int SYNC_ALLOWANCE_MS = 120;
- /**
- * Keyguard gets a more generous timeout to finish its animations, because we are always holding
- * a sleep token during occlude/unocclude transitions and we want them to finish playing cleanly
- */
- private static final int SYNC_ALLOWANCE_KEYGUARD_MS = 2000;
-
/** For testing only. Disables the force-finish timeout on sync. */
private boolean mDisableForceSync = false;
@@ -272,10 +269,11 @@
@NonNull DisplayController displayController,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
- @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor animExecutor,
+ @NonNull HomeTransitionObserver observer) {
this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
displayController, mainExecutor, mainHandler, animExecutor,
- new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit));
+ new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit), observer);
}
public Transitions(@NonNull Context context,
@@ -288,7 +286,8 @@
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
- @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
+ @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ @NonNull HomeTransitionObserver observer) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
@@ -307,6 +306,7 @@
mHandlers.add(mRemoteTransitionHandler);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
+ mHomeTransitionObserver = observer;
}
private void onInit() {
@@ -356,7 +356,7 @@
}
private ExternalInterfaceBinder createExternalInterface() {
- return new IShellTransitionsImpl(mContext, getMainExecutor(), this);
+ return new IShellTransitionsImpl(this);
}
@Override
@@ -743,6 +743,11 @@
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
}
+ // The change has already animated by back gesture, don't need to play transition
+ // animation on it.
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+ info.getChanges().remove(i);
+ }
}
// There does not need animation when:
// A. Transfer starting window. Apply transfer starting window directly if there is no other
@@ -1203,11 +1208,8 @@
if (track.mActiveTransition == playing) {
if (!mDisableForceSync) {
// Give it a short amount of time to process it before forcing.
- final int tolerance = KeyguardTransitionHandler.handles(playing.mInfo)
- ? SYNC_ALLOWANCE_KEYGUARD_MS
- : SYNC_ALLOWANCE_MS;
mMainExecutor.executeDelayed(
- () -> finishForSync(reason, trackIdx, playing), tolerance);
+ () -> finishForSync(reason, trackIdx, playing), SYNC_ALLOWANCE_MS);
}
break;
}
@@ -1400,12 +1402,9 @@
private static class IShellTransitionsImpl extends IShellTransitions.Stub
implements ExternalInterfaceBinder {
private Transitions mTransitions;
- private final HomeTransitionObserver mHomeTransitionObserver;
- IShellTransitionsImpl(Context context, ShellExecutor executor, Transitions transitions) {
+ IShellTransitionsImpl(Transitions transitions) {
mTransitions = transitions;
- mHomeTransitionObserver = new HomeTransitionObserver(context, executor,
- mTransitions);
}
/**
@@ -1413,7 +1412,7 @@
*/
@Override
public void invalidate() {
- mHomeTransitionObserver.invalidate();
+ mTransitions.mHomeTransitionObserver.invalidate(mTransitions);
mTransitions = null;
}
@@ -1443,7 +1442,8 @@
public void setHomeTransitionListener(IHomeTransitionListener listener) {
executeRemoteCallWithTaskPermission(mTransitions, "setHomeTransitionListener",
(transitions) -> {
- mHomeTransitionObserver.setHomeTransitionListener(listener);
+ transitions.mHomeTransitionObserver.setHomeTransitionListener(mTransitions,
+ listener);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 716f8b8..e206039 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -82,6 +82,7 @@
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -238,7 +239,7 @@
mSplitScreenController = splitScreenController;
mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
@Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
if (visible) {
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
if (decor != null && DesktopModeStatus.isEnabled()
@@ -391,10 +392,10 @@
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId();
if (id == R.id.close_window) {
- mTaskOperations.closeTask(mTaskToken);
if (isTaskInSplitScreen(mTaskId)) {
- RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
- mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
+ mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId);
+ } else {
+ mTaskOperations.closeTask(mTaskToken);
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
@@ -417,8 +418,12 @@
}
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
- mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
+ if (isTaskInSplitScreen(mTaskId)) {
+ mSplitScreenController.moveTaskToFullscreen(mTaskId);
+ } else {
+ mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
+ }
} else if (id == R.id.split_screen_button) {
decoration.closeHandleMenu();
mDesktopTasksController.ifPresent(c -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
index 9dc86db..b1fb0f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -42,11 +42,11 @@
}
override fun onHandleMenuOpened() {
- animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
+ animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
}
override fun onHandleMenuClosed() {
- animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
+ animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index bd2eb5b..366f7b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -28,93 +28,6 @@
srcs: ["src/com/android/wm/shell/flicker/utils/*.kt"],
}
-filegroup {
- name: "WMShellFlickerTestsBase-src",
- srcs: ["src/com/android/wm/shell/flicker/*.kt"],
-}
-
-filegroup {
- name: "WMShellFlickerTestsBubbles-src",
- srcs: ["src/com/android/wm/shell/flicker/bubble/*.kt"],
-}
-
-filegroup {
- name: "WMShellFlickerTestsPip1-src",
- srcs: [
- "src/com/android/wm/shell/flicker/pip/A*.kt",
- "src/com/android/wm/shell/flicker/pip/B*.kt",
- "src/com/android/wm/shell/flicker/pip/C*.kt",
- "src/com/android/wm/shell/flicker/pip/D*.kt",
- "src/com/android/wm/shell/flicker/pip/S*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerTestsPip2-src",
- srcs: [
- "src/com/android/wm/shell/flicker/pip/E*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerTestsPip3-src",
- srcs: ["src/com/android/wm/shell/flicker/pip/*.kt"],
-}
-
-filegroup {
- name: "WMShellFlickerTestsPipCommon-src",
- srcs: ["src/com/android/wm/shell/flicker/pip/common/*.kt"],
-}
-
-filegroup {
- name: "WMShellFlickerTestsPipApps-src",
- srcs: ["src/com/android/wm/shell/flicker/pip/apps/*.kt"],
-}
-
-filegroup {
- name: "WMShellFlickerTestsSplitScreenBase-src",
- srcs: [
- "src/com/android/wm/shell/flicker/splitscreen/benchmark/*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerTestsSplitScreenGroup1-src",
- srcs: [
- "src/com/android/wm/shell/flicker/splitscreen/A*.kt",
- "src/com/android/wm/shell/flicker/splitscreen/B*.kt",
- "src/com/android/wm/shell/flicker/splitscreen/C*.kt",
- "src/com/android/wm/shell/flicker/splitscreen/D*.kt",
- "src/com/android/wm/shell/flicker/splitscreen/E*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerTestsSplitScreenGroup2-src",
- srcs: [
- "src/com/android/wm/shell/flicker/splitscreen/*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerServicePlatinumTests-src",
- srcs: [
- "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
- "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt",
- "src/com/android/wm/shell/flicker/service/common/**/*.kt",
- ],
-}
-
-filegroup {
- name: "WMShellFlickerServiceTests-src",
- srcs: [
- "src/com/android/wm/shell/flicker/service/**/*.kt",
- ],
- exclude_srcs: [
- "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
- ],
-}
-
java_library {
name: "wm-shell-flicker-utils",
platform_apis: true,
@@ -138,23 +51,8 @@
],
}
-java_library {
- name: "wm-shell-flicker-platinum-tests",
- platform_apis: true,
- optimize: {
- enabled: false,
- },
- srcs: [
- ":WMShellFlickerServicePlatinumTests-src",
- ],
- static_libs: [
- "wm-shell-flicker-utils",
- ],
-}
-
java_defaults {
name: "WMShellFlickerTestsDefaultWithoutTemplate",
- manifest: "manifests/AndroidManifest.xml",
platform_apis: true,
certificate: "platform",
optimize: {
@@ -187,170 +85,8 @@
test_config_template: "AndroidTestTemplate.xml",
}
-android_test {
- name: "WMShellFlickerTestsOther",
+java_library {
+ name: "WMShellFlickerTestsBase",
defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestOther.xml"],
- package_name: "com.android.wm.shell.flicker",
- instrumentation_target_package: "com.android.wm.shell.flicker",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- exclude_srcs: [
- ":WMShellFlickerTestsBubbles-src",
- ":WMShellFlickerTestsPip1-src",
- ":WMShellFlickerTestsPip2-src",
- ":WMShellFlickerTestsPip3-src",
- ":WMShellFlickerTestsPipCommon-src",
- ":WMShellFlickerTestsPipApps-src",
- ":WMShellFlickerTestsSplitScreenGroup1-src",
- ":WMShellFlickerTestsSplitScreenGroup2-src",
- ":WMShellFlickerTestsSplitScreenBase-src",
- ":WMShellFlickerServiceTests-src",
- ":WMShellFlickerServicePlatinumTests-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsBubbles",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestBubbles.xml"],
- package_name: "com.android.wm.shell.flicker.bubbles",
- instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsBubbles-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsPip1",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPip1-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsPip2",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPip2-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsPip3",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPip3-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
- exclude_srcs: [
- ":WMShellFlickerTestsPip1-src",
- ":WMShellFlickerTestsPip2-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsPipApps",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip.apps",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPipApps-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsPipAppsCSuite",
- defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
- additional_manifests: ["manifests/AndroidManifestPip.xml"],
- package_name: "com.android.wm.shell.flicker.pip.apps",
- instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPipApps-src",
- ":WMShellFlickerTestsPipCommon-src",
- ],
- test_suites: [
- "device-tests",
- "csuite",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsSplitScreenGroup1",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
- package_name: "com.android.wm.shell.flicker.splitscreen",
- instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsSplitScreenBase-src",
- ":WMShellFlickerTestsSplitScreenGroup1-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerTestsSplitScreenGroup2",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
- package_name: "com.android.wm.shell.flicker.splitscreen",
- instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsSplitScreenBase-src",
- ":WMShellFlickerTestsSplitScreenGroup2-src",
- ],
- exclude_srcs: [
- ":WMShellFlickerTestsSplitScreenGroup1-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerServiceTests",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestService.xml"],
- package_name: "com.android.wm.shell.flicker.service",
- instrumentation_target_package: "com.android.wm.shell.flicker.service",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerServiceTests-src",
- ],
-}
-
-android_test {
- name: "WMShellFlickerServicePlatinumTests",
- defaults: ["WMShellFlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestService.xml"],
- package_name: "com.android.wm.shell.flicker.service",
- instrumentation_target_package: "com.android.wm.shell.flicker.service",
- srcs: [
- ":WMShellFlickerTestsBase-src",
- ":WMShellFlickerServicePlatinumTests-src",
- ],
-}
-
-csuite_test {
- name: "csuite-1p3p-pip-flickers",
- test_config_template: "csuiteDefaultTemplate.xml",
+ srcs: ["src/com/android/wm/shell/flicker/*.kt"],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
new file mode 100644
index 0000000..bae701f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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 {
+ // 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"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsAppCompat-src",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsOther",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker",
+ instrumentation_target_package: "com.android.wm.shell.flicker",
+ srcs: [":WMShellFlickerTestsAppCompat-src"],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
similarity index 77%
rename from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
index ae130b8..2af1e74 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
@@ -1,18 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
@@ -69,4 +69,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
index ba2b3e7..446aad8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.appcompat
import android.os.Build
-import android.tools.common.datatypes.Rect
import android.platform.test.annotations.Postsubmit
import android.system.helpers.CommandsHelper
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -65,10 +65,12 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
- private val immersiveApp = LetterboxAppHelper(instrumentation,
+ private val immersiveApp =
+ LetterboxAppHelper(
+ instrumentation,
launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL,
- component =
- ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent())
+ component = ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent()
+ )
private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) }
@@ -84,8 +86,8 @@
setStartRotation()
immersiveApp.launchViaIntent(wmHelper)
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds
- ?: error("Display not found")
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Display not found")
}
transitions {
if (isCuttlefishDevice) {
@@ -96,12 +98,10 @@
val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion")
uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT)
uiDevice.findObject(rotationButtonSelector)
- ?: error("rotation button not found")
+ ?: error("rotation button not found")
}
}
- teardown {
- immersiveApp.exit(wmHelper)
- }
+ teardown { immersiveApp.exit(wmHelper) }
}
@Before
@@ -112,48 +112,40 @@
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun taskBarLayerIsVisibleAtStartAndEnd() {
- }
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun navBarLayerIsVisibleAtStartAndEnd() {
- }
+ override fun navBarLayerIsVisibleAtStartAndEnd() {}
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun statusBarLayerIsVisibleAtStartAndEnd() {
- }
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun taskBarWindowIsAlwaysVisible() {
- }
+ override fun taskBarWindowIsAlwaysVisible() {}
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun navBarWindowIsAlwaysVisible() {
- }
+ override fun navBarWindowIsAlwaysVisible() {}
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun statusBarWindowIsAlwaysVisible() {
- }
+ override fun statusBarWindowIsAlwaysVisible() {}
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun statusBarLayerPositionAtStartAndEnd() {
- }
+ override fun statusBarLayerPositionAtStartAndEnd() {}
@Test
@Ignore("Not applicable to this CUJ. App is in immersive mode.")
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- }
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {}
/** Test that app is fullscreen by checking status bar and task bar visibility. */
@Postsubmit
@@ -161,8 +153,9 @@
fun appWindowFullScreen() {
flicker.assertWmEnd {
this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR)
- .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
- .visibleRegion(immersiveApp).coversExactly(startDisplayBounds)
+ .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
+ .visibleRegion(immersiveApp)
+ .coversExactly(startDisplayBounds)
}
}
@@ -170,9 +163,7 @@
@Postsubmit
@Test
fun appInOriginalRotation() {
- flicker.assertWmEnd {
- this.hasRotation(Rotation.ROTATION_90)
- }
+ flicker.assertWmEnd { this.hasRotation(Rotation.ROTATION_90) }
}
companion object {
@@ -189,10 +180,10 @@
@JvmStatic
fun getParams(): Collection<FlickerTest> {
return LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_90),
- // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
-
+ supportedRotations = listOf(Rotation.ROTATION_90),
+ // TODO(b/292403378): 3 button mode not added as rotation button is hidden in
+ // taskbar
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
new file mode 100644
index 0000000..c4e9a84
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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 {
+ // 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"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsBubbles",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.bubbles",
+ instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
+ srcs: ["src/**/*.kt"],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
similarity index 76%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
index ae130b8..e6e6f1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
+ package="com.android.wm.shell.flicker.bubble">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.bubble"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/bubble/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/bubble/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index bc095bb..0c36e29 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -58,8 +58,9 @@
return {
setup {
MultiWindowUtils.executeShellCommand(
- instrumentation,
- "settings put secure force_hide_bubbles_user_education 1")
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 1"
+ )
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
@@ -72,8 +73,9 @@
teardown {
MultiWindowUtils.executeShellCommand(
- instrumentation,
- "settings put secure force_hide_bubbles_user_education 0")
+ instrumentation,
+ "settings put secure force_hide_bubbles_user_education 0"
+ )
notifyManager.setBubblesAllowed(
testApp.packageName,
uid,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
deleted file mode 100644
index 437871f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.bubble">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.bubble"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
deleted file mode 100644
index cf642f6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
deleted file mode 100644
index fa42a45..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.pip">
-
- <!-- Enable mocking GPS location -->
- <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.pip"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml
deleted file mode 100644
index c7aca1a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.service">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.service"
- android:label="WindowManager Flicker Service Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
deleted file mode 100644
index 887d8db..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.splitscreen">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker.splitscreen"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
new file mode 100644
index 0000000..386983c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -0,0 +1,136 @@
+//
+// 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 {
+ // 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"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip1-src",
+ srcs: [
+ "src/**/A*.kt",
+ "src/**/B*.kt",
+ "src/**/C*.kt",
+ "src/**/D*.kt",
+ "src/**/S*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip2-src",
+ srcs: [
+ "src/**/E*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip3-src",
+ srcs: ["src/**/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPipCommon-src",
+ srcs: ["src/**/common/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPipApps-src",
+ srcs: ["src/**/apps/*.kt"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip1",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsPip1-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip2",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsPip2-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip3",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsPip3-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ exclude_srcs: [
+ ":WMShellFlickerTestsPip1-src",
+ ":WMShellFlickerTestsPip2-src",
+ ":WMShellFlickerTestsPipApps-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPipApps",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.pip.apps",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+ srcs: [
+ ":WMShellFlickerTestsPipApps-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPipAppsCSuite",
+ defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
+ additional_manifests: ["AndroidManifest.xml"],
+ package_name: "com.android.wm.shell.flicker.pip.apps",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+ srcs: [
+ ":WMShellFlickerTestsPipApps-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+ test_suites: [
+ "device-tests",
+ "csuite",
+ ],
+}
+
+csuite_test {
+ name: "csuite-1p3p-pip-flickers",
+ test_config_template: "csuiteDefaultTemplate.xml",
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
similarity index 74%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
index ae130b8..6d5423b 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
+ package="com.android.wm.shell.flicker.pip">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -69,4 +69,12 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <!-- Enable mocking GPS location -->
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.pip"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/pip/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/pip/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml
rename to libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
rename to libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index 943b16c..a5c2c89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -81,8 +81,13 @@
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
SplitScreenUtils.enterSplit(
- wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
- flicker.scenario.startRotation)
+ wmHelper,
+ tapl,
+ device,
+ pipApp,
+ secondAppForSplitScreen,
+ flicker.scenario.startRotation
+ )
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -132,9 +137,7 @@
if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
flicker.assertWmVisibleRegion(pipApp) {
// first check against landscape bounds then against portrait bounds
- coversAtMost(displayBounds).then().coversAtMost(
- portraitDisplayBounds
- )
+ coversAtMost(displayBounds).then().coversAtMost(portraitDisplayBounds)
}
} else {
// always check against the display bounds which do not change during transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 94e3959..af2db12 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -55,8 +55,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
- EnterPipTransition(flicker) {
+open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
@@ -66,11 +65,7 @@
}
}
- override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- pipApp.exit(wmHelper)
- }
- }
+ override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
@FlakyTest(bugId = 293133362)
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 820af93..4cc9547 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -53,11 +53,7 @@
}
}
- override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- pipApp.exit(wmHelper)
- }
- }
+ override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 4f27ced..36047cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -35,9 +35,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions {
- pipApp.changeAspectRatio()
- }
+ transitions { pipApp.changeAspectRatio() }
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
similarity index 98%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 0fd1b2c..381f947 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -42,8 +42,8 @@
}
/**
- * Checks that the visible region area of [pipApp] decreases
- * and then increases during the animation.
+ * Checks that the visible region area of [pipApp] decreases and then increases during the
+ * animation.
*/
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
similarity index 91%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index c9a98c7..bd8b005 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -20,6 +20,8 @@
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +31,15 @@
abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
protected abstract val standardAppHelper: StandardAppHelper
+ @FlickerBuilderProvider
+ override fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withoutScreenRecorder()
+ setup { flicker.scenario.setIsTablet(tapl.isTablet) }
+ transition()
+ }
+ }
+
/** Checks [standardAppHelper] window remains visible throughout the animation */
@Postsubmit
@Test
@@ -81,9 +92,8 @@
@Test
override fun pipLayerReduces() {
flicker.assertLayers {
- val pipLayerList = this.layers {
- standardAppHelper.layerMatchesAnyOf(it) && it.isVisible
- }
+ val pipLayerList =
+ this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
}
@@ -194,7 +204,8 @@
* transition
*/
@Postsubmit
- @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
/**
* Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
@@ -215,9 +226,7 @@
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
/** Checks that all parts of the screen are covered during the transition */
- @Postsubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index d7ba3d5..4da52ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -69,20 +69,21 @@
val mainHandler = Handler(Looper.getMainLooper())
var mockLocationEnabled = false
- val updateLocation = object : Runnable {
- override fun run() {
- // early bail out if mocking location is not enabled
- if (!mockLocationEnabled) return
- val location = Location("Googleplex")
- location.latitude = 37.42243438411294
- location.longitude = -122.08426281892311
- location.time = System.currentTimeMillis()
- location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
- location.accuracy = 100f
- locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
- mainHandler.postDelayed(this, 5)
+ val updateLocation =
+ object : Runnable {
+ override fun run() {
+ // early bail out if mocking location is not enabled
+ if (!mockLocationEnabled) return
+ val location = Location("Googleplex")
+ location.latitude = 37.42243438411294
+ location.longitude = -122.08426281892311
+ location.time = System.currentTimeMillis()
+ location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
+ location.accuracy = 100f
+ locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
+ mainHandler.postDelayed(this, 5)
+ }
}
- }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
@@ -129,9 +130,7 @@
}
}
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 5965805..5498e8c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -67,25 +67,18 @@
standardAppHelper.launchViaIntent(
wmHelper,
NetflixAppHelper.getNetflixWatchVideoIntent("70184207"),
- ComponentNameMatcher(
- NetflixAppHelper.PACKAGE_NAME,
- NetflixAppHelper.WATCH_ACTIVITY
- )
+ ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY)
)
standardAppHelper.waitForVideoPlaying()
}
}
override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- standardAppHelper.exit(wmHelper)
- }
+ teardown { standardAppHelper.exit(wmHelper) }
}
override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions {
- tapl.goHomeFromImmersiveFullscreenApp()
- }
+ transitions { tapl.goHomeFromImmersiveFullscreenApp() }
}
@Postsubmit
@@ -133,7 +126,8 @@
}
@Postsubmit
- @Test override fun statusBarWindowIsAlwaysVisible() {
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {
// Netflix plays in immersive fullscreen mode, so taskbar will be gone at some point
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index c370d91..d8afc25 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -70,14 +70,10 @@
}
override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- standardAppHelper.exit(wmHelper)
- }
+ teardown { standardAppHelper.exit(wmHelper) }
}
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
new file mode 100644
index 0000000..9b8cd94
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
@@ -0,0 +1,67 @@
+//
+// 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 {
+ // 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"],
+}
+
+filegroup {
+ name: "WMShellFlickerServicePlatinumTests-src",
+ srcs: [
+ "src/**/platinum/*.kt",
+ "src/**/scenarios/*.kt",
+ "src/**/common/*.kt",
+ ],
+}
+
+java_library {
+ name: "wm-shell-flicker-platinum-tests",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: [
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+ static_libs: [
+ "wm-shell-flicker-utils",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerServiceTests",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.service",
+ instrumentation_target_package: "com.android.wm.shell.flicker.service",
+ srcs: ["src/**/*.kt"],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerServicePlatinumTests",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.service",
+ instrumentation_target_package: "com.android.wm.shell.flicker.service",
+ srcs: [":WMShellFlickerServicePlatinumTests-src"],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
similarity index 75%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
index ae130b8..d54b694 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
+ package="com.android.wm.shell.flicker.service">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.service"
+ android:label="WindowManager Flicker Service Tests">
+ </instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
index 5f15785..4bd7954 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
@@ -36,9 +36,7 @@
fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
return RuleChain.outerRule(ArtifactSaverRule())
.around(UnlockScreenRule())
- .around(
- NavigationModeRule(navigationMode.value, false)
- )
+ .around(NavigationModeRule(navigationMode.value, false))
.around(
LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
new file mode 100644
index 0000000..4629c53
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -0,0 +1,77 @@
+//
+// 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 {
+ // 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"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenBase-src",
+ srcs: [
+ "src/**/benchmark/*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroup1-src",
+ srcs: [
+ "src/**/A*.kt",
+ "src/**/B*.kt",
+ "src/**/C*.kt",
+ "src/**/D*.kt",
+ "src/**/E*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroup2-src",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroup1",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroup2",
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ":WMShellFlickerTestsSplitScreenGroup2-src",
+ ],
+ exclude_srcs: [
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ],
+ static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
similarity index 75%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
index ae130b8..9ff2161 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.wm.shell.flicker">
+ package="com.android.wm.shell.flicker.splitscreen">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.splitscreen"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ </target_preparer>
+
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index 1387536..715a533 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -62,10 +62,22 @@
get() = {
setup {
tapl.goHome()
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
- flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ thirdApp,
+ pipApp,
+ flicker.scenario.startRotation
+ )
pipApp.enableAutoEnterForPipActivity()
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 3b9e53f..df1c9a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -39,8 +39,16 @@
protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- textEditApp, flicker.scenario.startRotation) }
+ setup {
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ textEditApp,
+ flicker.scenario.startRotation
+ )
+ }
transitions {
SplitScreenUtils.copyContentInSplit(
instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 5fdde3a..d01eab0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -35,8 +35,16 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation) }
+ setup {
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ }
transitions {
if (tapl.isTablet) {
SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index b7f6bfe..e36bd33 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -36,8 +36,14 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
- flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
}
transitions {
tapl.goHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index bb2a7aa..050d389 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -37,8 +37,16 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation) }
+ setup {
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ }
transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 46b0bd2..e39c3c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -39,8 +39,16 @@
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation) }
+ setup {
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ }
transitions {
SplitScreenUtils.doubleTapDividerToSwitch(device)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index baf7693..32284ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -39,8 +39,14 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
similarity index 89%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index 33b55f1..a926ec9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -37,8 +37,14 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
similarity index 89%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index b79dfb5..d2e1d52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -37,8 +37,14 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
tapl.goHome()
wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
similarity index 82%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 0204d75..9d6b251 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -39,10 +39,22 @@
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
- secondaryApp, flicker.scenario.startRotation)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
- flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ thirdApp,
+ fourthApp,
+ flicker.scenario.startRotation
+ )
SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
index 0f3e0f5..e9363f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
@@ -38,7 +38,7 @@
* executions
*/
@FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
+ open fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup { flicker.scenario.setIsTablet(tapl.isTablet) }
transition()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index 735fbfb..568650d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 6b3cfaf..3244ebc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -24,6 +24,7 @@
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
import android.view.InputDevice
@@ -42,7 +43,6 @@
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
-import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
@@ -135,33 +135,38 @@
// second task to split.
val home = tapl.workspace.switchToOverview()
ChangeDisplayOrientationRule.setRotation(rotation)
- home.overviewActions.clickSplit()
+ val isGridOnlyOverviewEnabled = tapl.isGridOnlyOverviewEnabled
+ if (isGridOnlyOverviewEnabled) {
+ home.currentTask.tapMenu().tapSplitMenuItem()
+ } else {
+ home.overviewActions.clickSplit()
+ }
val snapshots = device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
}
- // Find the second task in the upper right corner in split select mode by sorting
- // 'left' in descending order and 'top' in ascending order.
+ // Find the second task in the upper (or bottom for grid only Overview) right corner in
+ // split select mode by sorting 'left' in descending order and 'top' in ascending (or
+ // descending for grid only Overview) order.
snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
t2.getVisibleBounds().left - t1.getVisibleBounds().left
}
snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ if (isGridOnlyOverviewEnabled) {
+ t2.getVisibleBounds().top - t1.getVisibleBounds().top
+ } else {
+ t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ }
}
snapshots[0].click()
} else {
val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled()
tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview
- val home = tapl.workspace
- .switchToOverview()
+ val home = tapl.workspace.switchToOverview()
tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks
ChangeDisplayOrientationRule.setRotation(rotation)
- home.currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
+ home.currentTask.tapMenu().tapSplitMenuItem().currentTask.open()
}
SystemClock.sleep(TIMEOUT_MS)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 02c9d30..2ac72af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -98,4 +98,21 @@
// The animation should be empty when it is behind starting window.
assertEquals(0, animator.getDuration());
}
+
+ @Test
+ public void testInvalidCustomAnimation() {
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
+ .build();
+ info.setAnimationOptions(TransitionInfo.AnimationOptions
+ .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
+ 0 /* backgroundColor */, false /* overrideTaskTransition */));
+ final Animator animator = mAnimRunner.createAnimator(
+ info, mStartTransaction, mFinishTransaction,
+ () -> mFinishCallback.onTransitionFinished(null /* wct */),
+ new ArrayList<>());
+
+ // An invalid custom animation is equivalent to jump-cut.
+ assertEquals(0, animator.getDuration());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index cc4db22..fff65f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -74,6 +74,7 @@
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -373,7 +374,7 @@
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
shellInit.init();
return t;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 7a8a2a9..ea7c0d9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -75,24 +75,24 @@
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final DisplayController mDisplayController = mock(DisplayController.class);
+ private IHomeTransitionListener mListener;
private Transitions mTransition;
+ private HomeTransitionObserver mHomeTransitionObserver;
@Before
public void setUp() {
+ mListener = mock(IHomeTransitionListener.class);
+ when(mListener.asBinder()).thenReturn(mock(IBinder.class));
+
+ mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor);
mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class),
mOrganizer, mTransactionPool, mDisplayController, mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mHomeTransitionObserver);
+ mHomeTransitionObserver.setHomeTransitionListener(mTransition, mListener);
}
@Test
public void testHomeActivityWithOpenModeNotifiesHomeIsVisible() throws RemoteException {
- IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
- when(listener.asBinder()).thenReturn(mock(IBinder.class));
-
- HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
- mTransition);
- observer.setHomeTransitionListener(listener);
-
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
@@ -101,23 +101,16 @@
setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN);
- observer.onTransitionReady(mock(IBinder.class),
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
info,
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class));
- verify(listener, times(1)).onHomeVisibilityChanged(true);
+ verify(mListener, times(1)).onHomeVisibilityChanged(true);
}
@Test
public void testHomeActivityWithCloseModeNotifiesHomeIsNotVisible() throws RemoteException {
- IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
- when(listener.asBinder()).thenReturn(mock(IBinder.class));
-
- HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
- mTransition);
- observer.setHomeTransitionListener(listener);
-
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
@@ -126,38 +119,30 @@
setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_TO_BACK);
- observer.onTransitionReady(mock(IBinder.class),
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
info,
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class));
- verify(listener, times(1)).onHomeVisibilityChanged(false);
+ verify(mListener, times(1)).onHomeVisibilityChanged(false);
}
@Test
public void testNonHomeActivityDoesNotTriggerCallback() throws RemoteException {
- IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
- when(listener.asBinder()).thenReturn(mock(IBinder.class));
-
- HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
- mTransition);
- observer.setHomeTransitionListener(listener);
-
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
when(change.getTaskInfo()).thenReturn(taskInfo);
when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
-
setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_UNDEFINED, TRANSIT_TO_BACK);
- observer.onTransitionReady(mock(IBinder.class),
+ mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
info,
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class));
- verify(listener, times(0)).onHomeVisibilityChanged(anyBoolean());
+ verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
}
/**
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index da83d4c0..4e300d9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -146,7 +146,7 @@
ShellInit shellInit = mock(ShellInit.class);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
// One from Transitions, one from RootTaskDisplayAreaOrganizer
verify(shellInit).addInitCallback(any(), eq(t));
verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class));
@@ -158,7 +158,7 @@
ShellController shellController = mock(ShellController.class);
final Transitions t = new Transitions(mContext, shellInit, shellController,
mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
shellInit.init();
verify(shellController, times(1)).addExternalInterface(
eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any());
@@ -1075,10 +1075,10 @@
final Transitions transitions =
new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer,
mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
final RecentsTransitionHandler recentsHandler =
new RecentsTransitionHandler(shellInit, transitions,
- mock(RecentTasksController.class));
+ mock(RecentTasksController.class), mock(HomeTransitionObserver.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
@@ -1623,7 +1623,7 @@
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor);
+ mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
shellInit.init();
return t;
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ff1eedb..da728f9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -144,6 +144,7 @@
"libsync",
"libui",
"aconfig_text_flags_c_lib",
+ "server_configurable_flags",
],
static_libs: [
"libEGL_blobCache",
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index a8d170d..fd27641 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -242,6 +242,47 @@
}
}
+SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
+ const DirtyStack* frame = mHead;
+ Matrix4 transform;
+ SkRect pretransformResult = bounds;
+ while (true) {
+ SkRect currentBounds = pretransformResult;
+ pretransformResult.setEmpty();
+ switch (frame->type) {
+ case TransformRenderNode: {
+ const RenderProperties& props = frame->renderNode->properties();
+ // Perform clipping
+ if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) {
+ if (!currentBounds.intersect(
+ SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
+ currentBounds.setEmpty();
+ }
+ }
+
+ // apply all transforms
+ mapRect(props, currentBounds, &pretransformResult);
+ frame->renderNode->applyViewPropertyTransforms(transform);
+ } break;
+ case TransformMatrix4:
+ mapRect(frame->matrix4, currentBounds, &pretransformResult);
+ transform.multiply(*frame->matrix4);
+ break;
+ default:
+ pretransformResult = currentBounds;
+ break;
+ }
+ if (frame->prev == frame) break;
+ frame = frame->prev;
+ }
+ SkRect result;
+ Matrix4 globalToLocal;
+ globalToLocal.loadInverse(transform);
+ mapRect(&globalToLocal, pretransformResult, &result);
+ *outMatrix = transform;
+ return result;
+}
+
void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
mHead->pendingDirty.join({left, top, right, bottom});
}
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index c4249af..30bf706 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -61,6 +61,8 @@
void computeCurrentTransform(Matrix4* outMatrix) const;
+ SkRect computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const;
+
void finish(SkRect* totalDirty);
struct StretchResult {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5e5eb4a..ad600d0 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -20,15 +20,26 @@
#ifdef __ANDROID__
#include "HWUIProperties.sysprop.h"
#endif
-#include "src/core/SkTraceEventCommon.h"
+#include <android-base/properties.h>
+#include <cutils/compiler.h>
+#include <log/log.h>
#include <algorithm>
#include <cstdlib>
#include <optional>
-#include <android-base/properties.h>
-#include <cutils/compiler.h>
-#include <log/log.h>
+#include "src/core/SkTraceEventCommon.h"
+
+#ifdef __ANDROID__
+#include <com_android_graphics_hwui_flags.h>
+namespace hwui_flags = com::android::graphics::hwui::flags;
+#else
+namespace hwui_flags {
+constexpr bool clip_surfaceviews() {
+ return false;
+}
+} // namespace hwui_flags
+#endif
namespace android {
namespace uirenderer {
@@ -92,6 +103,8 @@
float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number
+bool Properties::clipSurfaceViews = false;
+
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
@@ -159,6 +172,9 @@
// call isDrawingEnabled to force loading of the property
isDrawingEnabled();
+ clipSurfaceViews =
+ base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index bb47744..bca57e9 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -325,6 +325,8 @@
static float maxHdrHeadroomOn8bit;
+ static bool clipSurfaceViews;
+
static StretchEffectBehavior getStretchEffectBehavior() {
return stretchEffectBehavior;
}
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index caffdfc..ef4dce5 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,18 +17,18 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
-#include "Typeface.h"
-
-#include <cutils/compiler.h>
-
#include <SkFont.h>
#include <SkPaint.h>
#include <SkSamplingOptions.h>
+#include <cutils/compiler.h>
+#include <minikin/FamilyVariant.h>
+#include <minikin/FontFamily.h>
+#include <minikin/FontFeature.h>
+#include <minikin/Hyphenator.h>
+
#include <string>
-#include <minikin/FontFamily.h>
-#include <minikin/FamilyVariant.h>
-#include <minikin/Hyphenator.h>
+#include "Typeface.h"
namespace android {
@@ -82,11 +82,15 @@
float getWordSpacing() const { return mWordSpacing; }
- void setFontFeatureSettings(const std::string& fontFeatureSettings) {
- mFontFeatureSettings = fontFeatureSettings;
+ void setFontFeatureSettings(std::string_view fontFeatures) {
+ mFontFeatureSettings = minikin::FontFeature::parse(fontFeatures);
}
- std::string getFontFeatureSettings() const { return mFontFeatureSettings; }
+ void resetFontFeatures() { mFontFeatureSettings.clear(); }
+
+ const std::vector<minikin::FontFeature>& getFontFeatureSettings() const {
+ return mFontFeatureSettings;
+ }
void setMinikinLocaleListId(uint32_t minikinLocaleListId) {
mMinikinLocaleListId = minikinLocaleListId;
@@ -170,7 +174,7 @@
float mLetterSpacing = 0;
float mWordSpacing = 0;
- std::string mFontFeatureSettings;
+ std::vector<minikin::FontFeature> mFontFeatureSettings;
uint32_t mMinikinLocaleListId;
std::optional<minikin::FamilyVariant> mFamilyVariant;
uint32_t mHyphenEdit = 0;
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 8c71d6f..d84b73d 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -33,6 +33,7 @@
#include <cassert>
#include <cstring>
#include <memory>
+#include <string_view>
#include <vector>
#include "ColorFilter.h"
@@ -690,10 +691,11 @@
jstring settings) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (!settings) {
- paint->setFontFeatureSettings(std::string());
+ paint->resetFontFeatures();
} else {
ScopedUtfChars settingsChars(env, settings);
- paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ paint->setFontFeatureSettings(
+ std::string_view(settingsChars.c_str(), settingsChars.size()));
}
}
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 2a218a2..a1b05c1 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -568,6 +568,7 @@
struct {
jclass clazz;
jmethodID callPositionChanged;
+ jmethodID callPositionChanged2;
jmethodID callApplyStretch;
jmethodID callPositionLost;
} gPositionListener;
@@ -589,14 +590,25 @@
virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
if (CC_UNLIKELY(!mListener || !info.updateWindowPositions)) return;
- Matrix4 transform;
- info.damageAccumulator->computeCurrentTransform(&transform);
const RenderProperties& props = node.properties();
+ const bool enableClip = Properties::clipSurfaceViews;
- uirenderer::Rect bounds(props.getWidth(), props.getHeight());
+ Matrix4 transform;
+ SkIRect clipBounds;
+ if (enableClip) {
+ uirenderer::Rect initialClipBounds;
+ props.getClippingRectForFlags(props.getClippingFlags(), &initialClipBounds);
+ clipBounds =
+ info.damageAccumulator
+ ->computeClipAndTransform(initialClipBounds.toSkRect(), &transform)
+ .roundOut();
+ } else {
+ info.damageAccumulator->computeCurrentTransform(&transform);
+ }
bool useStretchShader =
Properties::getStretchEffectBehavior() != StretchEffectBehavior::UniformScale;
// Compute the transform bounds first before calculating the stretch
+ uirenderer::Rect bounds(props.getWidth(), props.getHeight());
transform.mapRect(bounds);
bool hasStretch = useStretchShader && info.stretchEffectCount;
@@ -614,10 +626,11 @@
bounds.roundOut();
}
- if (mPreviousPosition == bounds) {
+ if (mPreviousPosition == bounds && mPreviousClip == clipBounds) {
return;
}
mPreviousPosition = bounds;
+ mPreviousClip = clipBounds;
ATRACE_NAME("Update SurfaceView position");
@@ -629,11 +642,23 @@
// In particular if the app removes a view from the view tree before
// this callback is dispatched, then we lose the position
// information for this frame.
- jboolean keepListening = env->CallStaticBooleanMethod(
- gPositionListener.clazz, gPositionListener.callPositionChanged, mListener,
- static_cast<jlong>(info.canvasContext.getFrameNumber()),
- static_cast<jint>(bounds.left), static_cast<jint>(bounds.top),
- static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom));
+ jboolean keepListening;
+ if (!enableClip) {
+ keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionChanged, mListener,
+ static_cast<jlong>(info.canvasContext.getFrameNumber()),
+ static_cast<jint>(bounds.left), static_cast<jint>(bounds.top),
+ static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom));
+ } else {
+ keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionChanged2, mListener,
+ static_cast<jlong>(info.canvasContext.getFrameNumber()),
+ static_cast<jint>(bounds.left), static_cast<jint>(bounds.top),
+ static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom),
+ static_cast<jint>(clipBounds.fLeft), static_cast<jint>(clipBounds.fTop),
+ static_cast<jint>(clipBounds.fRight),
+ static_cast<jint>(clipBounds.fBottom));
+ }
if (!keepListening) {
env->DeleteGlobalRef(mListener);
mListener = nullptr;
@@ -738,6 +763,7 @@
JavaVM* mVm;
jobject mListener;
uirenderer::Rect mPreviousPosition;
+ uirenderer::Rect mPreviousClip;
};
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -866,6 +892,8 @@
gPositionListener.clazz = MakeGlobalRefOrDie(env, clazz);
gPositionListener.callPositionChanged = GetStaticMethodIDOrDie(
env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z");
+ gPositionListener.callPositionChanged2 = GetStaticMethodIDOrDie(
+ env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIII)Z");
gPositionListener.callApplyStretch = GetStaticMethodIDOrDie(
env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z");
gPositionListener.callPositionLost = GetStaticMethodIDOrDie(
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a4890ed..ad963dd 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,6 +19,8 @@
#include "DeferredLayerUpdater.h"
#include "hwui/Paint.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
#include <minikin/Layout.h>
#include <pipeline/skia/SkiaOpenGLPipeline.h>
#include <pipeline/skia/SkiaVulkanPipeline.h>
@@ -179,5 +181,13 @@
return outlineInLocalCoord;
}
+SkFont TestUtils::defaultFont() {
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont =
+ Typeface::resolveDefault(nullptr)->fFontCollection->getFamilyAt(0)->getFont(0)->baseTypeface();
+ SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(minikinFont.get())->GetSkTypeface();
+ LOG_ALWAYS_FATAL_IF(skTypeface == nullptr);
+ return SkFont(sk_ref_sp(skTypeface));
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ffc664c..0ede902 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -30,6 +30,7 @@
#include <SkBitmap.h>
#include <SkColor.h>
+#include <SkFont.h>
#include <SkImageInfo.h>
#include <SkRefCnt.h>
@@ -353,6 +354,8 @@
static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+ static SkFont defaultFont();
+
private:
static std::unordered_map<int, CallCounts> sMockFunctorCounts;
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 4a5d946..97d4c82 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -57,7 +57,7 @@
128 * 3;
paint.setColor(bgDark ? Color::White : Color::Grey_700);
- SkFont font;
+ SkFont font = TestUtils::defaultFont();
font.setSize(size / 2);
char charToShow = 'A' + (rand() % 26);
const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index bb95490..159541c 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -102,7 +102,7 @@
128 * 3;
paint.setColor(bgDark ? Color::White : Color::Grey_700);
- SkFont font;
+ SkFont font = TestUtils::defaultFont();
font.setSize(size / 2);
char charToShow = 'A' + (rand() % 26);
const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index eea6357..5d211f4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,8 +20,8 @@
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
-import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
-import static com.android.media.audio.flags.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -686,6 +686,7 @@
FLAG_ABSOLUTE_VOLUME,
})
@Retention(RetentionPolicy.SOURCE)
+ // TODO(308698465) remove due to potential conflict with the new flags class
public @interface Flags {}
/**
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 937151b..8ad3587 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -19,6 +19,7 @@
import static android.media.MediaRouter2Utils.toUniqueId;
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
+import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -306,6 +307,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_TABLET = 1004;
/**
@@ -316,6 +318,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_TABLET_DOCKED = 1005;
/**
@@ -326,6 +329,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_COMPUTER = 1006;
/**
@@ -336,6 +340,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_GAME_CONSOLE = 1007;
/**
@@ -346,6 +351,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_CAR = 1008;
/**
@@ -356,6 +362,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_SMARTWATCH = 1009;
/**
@@ -366,6 +373,7 @@
*
* @see #getType
*/
+ @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES)
public static final int TYPE_REMOTE_SMARTPHONE = 1010;
/**
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
index b74b6a3..b7c3721 100644
--- a/media/java/android/media/RingtoneSelection.java
+++ b/media/java/android/media/RingtoneSelection.java
@@ -642,6 +642,7 @@
* allowing the user to configure their selection. Once a selection is stored as a Uri, then
* the RingtoneSelection can be loaded directly using {@link RingtoneSelection#fromUri}.
*/
+ @FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
public static final class Builder {
private Uri mSoundUri;
private Uri mVibrationUri;
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 9ced2a4..e168498 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -49,7 +49,6 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.media.audio.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 386534b..dd1df47 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -15,29 +15,36 @@
}
flag {
- namespace: "media_solutions"
name: "enable_audio_policies_device_and_bluetooth_controller"
+ namespace: "media_solutions"
description: "Use Audio Policies implementation for device and Bluetooth route controllers."
bug: "280576228"
}
flag {
- namespace: "media_solutions"
name: "disable_screen_off_broadcast_receiver"
+ namespace: "media_solutions"
description: "Disables the broadcast receiver that prevents scanning when the screen is off."
bug: "304234628"
}
flag {
- namespace: "media_solutions"
name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
+ namespace: "media_solutions"
description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
bug: "293743975"
}
flag {
- namespace: "media_solutions"
name: "enable_waiting_state_for_system_session_creation_request"
+ namespace: "media_solutions"
description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
bug: "307723189"
}
+
+flag {
+ name: "enable_new_media_route_2_info_types"
+ namespace: "media_solutions"
+ description: "Enables the following type constants in MediaRoute2Info: CAR, COMPUTER, GAME_CONSOLE, SMARTPHONE, SMARTWATCH, TABLET, TABLET_DOCKED. Note that this doesn't gate any behavior. It only guards some API int symbols."
+ bug: "301713440"
+}
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index c54bfce..bbbef47 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -16,8 +16,6 @@
package android.media.midi;
-import static com.android.media.midi.flags.Flags.FLAG_VIRTUAL_UMP;
-
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -57,11 +55,11 @@
* android:resource="@xml/device_info" />
* </service></pre>
*/
-@FlaggedApi(FLAG_VIRTUAL_UMP)
+@FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public abstract class MidiUmpDeviceService extends Service {
private static final String TAG = "MidiUmpDeviceService";
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
private IMidiManager mMidiManager;
@@ -80,7 +78,7 @@
}
};
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
@Override
public void onCreate() {
mMidiManager = IMidiManager.Stub.asInterface(
@@ -118,7 +116,7 @@
* The number of input and output ports must be equal and non-zero.
* @return list of MidiReceivers
*/
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
/**
@@ -127,7 +125,7 @@
* The number of input and output ports must be equal and non-zero.
* @return the list of MidiReceivers
*/
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
if (mServer == null) {
return new ArrayList<MidiReceiver>();
@@ -140,7 +138,7 @@
* Returns the {@link MidiDeviceInfo} instance for this service
* @return the MidiDeviceInfo of the virtual MIDI device if it was successfully created
*/
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public final @Nullable MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
@@ -149,7 +147,7 @@
* Called to notify when the {@link MidiDeviceStatus} has changed
* @param status the current status of the MIDI device
*/
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public void onDeviceStatusChanged(@NonNull MidiDeviceStatus status) {
}
@@ -157,11 +155,11 @@
* Called to notify when the virtual MIDI device running in this service has been closed by
* all its clients
*/
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
public void onClose() {
}
- @FlaggedApi(FLAG_VIRTUAL_UMP)
+ @FlaggedApi(Flags.FLAG_VIRTUAL_UMP)
@Override
public @Nullable IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
new file mode 100644
index 0000000..a73d1ff
--- /dev/null
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -0,0 +1,8 @@
+package: "android.media.tv.flags"
+
+flag {
+ name: "broadcast_visibility_types"
+ namespace: "media_tv"
+ description: "Constants for standardizing broadcast visibility types."
+ bug: "222402395"
+}
\ No newline at end of file
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ed1072c..6031ef7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -81,6 +81,7 @@
"libhidlallocatorutils",
"libhidlbase",
"libsonivox",
+ "server_configurable_flags",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.3",
@@ -99,6 +100,7 @@
static_libs: [
"libgrallocusage",
"libmedia_midiiowrapper",
+ "android.media.playback.flags-aconfig-cc",
],
include_dirs: [
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 1458758..2a10fa7 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -35,7 +35,9 @@
#include "android_media_MediaDataSource.h"
#include "android_media_Streams.h"
#include "android_util_Binder.h"
+#include <com_android_media_playback_flags.h>
+namespace playback_flags = com::android::media::playback::flags;
using namespace android;
struct fields_t {
@@ -374,9 +376,12 @@
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
- // For getFrameAtTime family of calls, default to ANDROID_BITMAP_FORMAT_RGB_565
- // to keep the behavior consistent with older releases
- AndroidBitmapFormat colorFormat = getColorFormat(env, params, ANDROID_BITMAP_FORMAT_RGB_565);
+
+ AndroidBitmapFormat defaultColorFormat =
+ playback_flags::mediametadataretriever_default_rgba8888()
+ ? ANDROID_BITMAP_FORMAT_RGBA_8888
+ : ANDROID_BITMAP_FORMAT_RGB_565;
+ AndroidBitmapFormat colorFormat = getColorFormat(env, params, defaultColorFormat);
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
diff --git a/media/jni/playback_flags.aconfig b/media/jni/playback_flags.aconfig
new file mode 100644
index 0000000..2bb0ec5
--- /dev/null
+++ b/media/jni/playback_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.media.playback.flags"
+
+flag {
+ name: "mediametadataretriever_default_rgba8888"
+ namespace: "media_solutions"
+ description: "Change MediaMetadataRetriever to use RGBA8888 for bitmap handling by default."
+ bug: "298965955"
+}
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index bdd7afe..7a329bc 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -20,6 +20,8 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"android-ex-camera2",
+ "android.media.playback.flags-aconfig-java",
+ "flag-junit",
"testables",
"testng",
"truth",
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index f70d2d1..e3d3897 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -16,19 +16,27 @@
package com.android.mediaframeworktest.unit;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import com.android.media.playback.flags.Flags;
import com.android.mediaframeworktest.MediaNames;
import com.android.mediaframeworktest.MediaProfileReader;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +48,9 @@
private static final String TAG = "MediaMetadataRetrieverTest";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Test album art extraction.
@MediumTest
@Test
@@ -284,6 +295,34 @@
assertTrue(!hasFailed);
}
+ /** Test the thumbnail is generated when the default is set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.enableFlags
+ @RequiresFlagsEnabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Set() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig());
+ }
+ }
+
+ /** Test the thumbnail is generated when the default is not set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.disableFlags
+ @RequiresFlagsDisabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Unset() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.RGB_565, bitmap.getConfig());
+ }
+ }
+
// TODO:
// Encode and test for the correct mix of metadata elements on a per-file basis?
// We should be able to compare the actual returned metadata with the expected metadata
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 1088ace..4992ef1 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -290,7 +290,14 @@
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- session.commit(pendingIntent.getIntentSender());
+ try {
+ session.commit(pendingIntent.getIntentSender());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Cannot install package: ", e);
+ launchFailure(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ return;
+ }
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
index 5d6aa03..d763f77 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -25,7 +25,6 @@
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.getIntArg
import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
@@ -110,7 +109,7 @@
fun genStringParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
override val title = STRING_PARAM_TITLE
- override val summary = stateOf(stringParam!!)
+ override val summary = { stringParam!! }
}
}
@@ -118,7 +117,7 @@
fun genIntParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
override val title = INT_PARAM_TITLE
- override val summary = stateOf(intParam!!.toString())
+ override val summary = { intParam!!.toString() }
}
}
@@ -130,7 +129,7 @@
)
return object : PreferenceModel {
override val title = PAGE_TITLE
- override val summary = stateOf(summaryArray.joinToString(", "))
+ override val summary = { summaryArray.joinToString(", ") }
override val onClick = navigator(
SettingsPageProviderEnum.ARGUMENT.name + parameter.navLink(arguments)
)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPageProvider.kt
index 50c0eb7..345b47a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPageProvider.kt
@@ -26,7 +26,6 @@
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.widget.preference.Preference
@@ -50,7 +49,7 @@
Preference(remember {
object : PreferenceModel {
override val title = "Some Preference"
- override val summary = stateOf("Some summary")
+ override val summary = { "Some summary" }
}
})
}.build()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
index 43b6d0b..d7de9b4 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
@@ -99,7 +99,7 @@
ListPreference(remember {
object : ListPreferenceModel {
override val title = "Preferred network type"
- override val enabled = enabled
+ override val enabled = { enabled.value }
override val options = listOf(
ListPreferenceOption(id = 1, text = "5G (recommended)"),
ListPreferenceOption(id = 2, text = "LTE"),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
similarity index 94%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
index 238204a..96de1a7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -34,7 +34,6 @@
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.createIntent
import com.android.settingslib.spa.gallery.R
@@ -136,8 +135,8 @@
Preference(
object : PreferenceModel {
override val title = ASYNC_PREFERENCE_TITLE
- override val summary = model.asyncSummary
- override val enabled = model.asyncEnable
+ override val summary = { model.asyncSummary.value }
+ override val enabled = { model.asyncEnable.value }
}
)
}
@@ -170,7 +169,7 @@
Preference(
object : PreferenceModel {
override val title = MANUAL_UPDATE_PREFERENCE_TITLE
- override val summary = manualUpdaterSummary
+ override val summary = { manualUpdaterSummary.value }
override val onClick = { model.manualUpdaterOnClick() }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.TouchApp)
@@ -205,11 +204,13 @@
createEntry(EntryEnum.AUTO_UPDATE_PREFERENCE)
.setUiLayoutFn {
val model = PreferencePageModel.create()
- val autoUpdaterSummary = remember { model.getAutoUpdaterSummary() }
+ val autoUpdaterSummary = remember {
+ model.getAutoUpdaterSummary()
+ }.observeAsState(" ")
Preference(
object : PreferenceModel {
override val title = AUTO_UPDATE_PREFERENCE_TITLE
- override val summary = autoUpdaterSummary.observeAsState(" ")
+ override val summary = { autoUpdaterSummary.value }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Autorenew)
}
@@ -250,12 +251,12 @@
private fun singleLineSummaryEntry() = createEntry(EntryEnum.SINGLE_LINE_SUMMARY_PREFERENCE)
.setUiLayoutFn {
+ val summary = stringResource(R.string.single_line_summary_preference_summary)
Preference(
model = object : PreferenceModel {
override val title: String =
stringResource(R.string.single_line_summary_preference_title)
- override val summary =
- stringResource(R.string.single_line_summary_preference_summary).toState()
+ override val summary = { summary }
},
singleLineSummary = true,
)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
similarity index 96%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
index b67e066..ce0ee18 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -114,7 +114,7 @@
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val summary = stateOf("With summary")
+ override val summary = { "With summary" }
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
}
@@ -131,7 +131,7 @@
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val summary = summary
+ override val summary = { summary.value }
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
}
@@ -144,7 +144,7 @@
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val summary = stateOf("Not changeable")
+ override val summary = { "Not changeable" }
override val changeable = stateOf(false)
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
similarity index 95%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
index a2cd283..fc50745 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -105,7 +105,7 @@
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val summary = stateOf("With summary")
+ override val summary = { "With summary" }
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
}
@@ -122,7 +122,7 @@
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val summary = summary
+ override val summary = { summary.value }
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
}
@@ -135,7 +135,7 @@
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val summary = stateOf("Not changeable")
+ override val summary = { "Not changeable" }
override val changeable = stateOf(false)
override val checked = checked
override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPageProvider.kt
similarity index 87%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPageProvider.kt
index aeba6ea..5c5c504 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/SpinnerPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -18,10 +18,8 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
@@ -58,7 +56,7 @@
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
- var selectedId by rememberSaveable { mutableStateOf(1) }
+ var selectedId by rememberSaveable { mutableIntStateOf(1) }
Spinner(
options = (1..3).map { SpinnerOption(id = it, text = "Option $it") },
selectedId = selectedId,
@@ -66,9 +64,7 @@
)
Preference(object : PreferenceModel {
override val title = "Selected id"
- override val summary = remember {
- derivedStateOf { selectedId.toString() }
- }
+ override val summary = { selectedId.toString() }
})
}
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/Bitmap.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/Bitmap.kt
index 814d4a1..fb65d65 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/Bitmap.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/Bitmap.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.util
import android.graphics.Bitmap
import android.graphics.Canvas
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/DefaultDeviceEmulationSpec.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/DefaultDeviceEmulationSpec.kt
deleted file mode 100644
index d7f42b3..0000000
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/DefaultDeviceEmulationSpec.kt
+++ /dev/null
@@ -1,66 +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.settingslib.spa.screenshot
-
-import platform.test.screenshot.DeviceEmulationSpec
-import platform.test.screenshot.DisplaySpec
-
-/**
- * The emulations specs for all 8 permutations of:
- * - phone or tablet.
- * - dark of light mode.
- * - portrait or landscape.
- */
-val DeviceEmulationSpec.Companion.PhoneAndTabletFull
- get() = PhoneAndTabletFullSpec
-
-private val PhoneAndTabletFullSpec =
- DeviceEmulationSpec.forDisplays(Displays.Phone, Displays.Tablet)
-
-/**
- * The emulations specs of:
- * - phone + light mode + portrait.
- * - phone + light mode + landscape.
- * - tablet + dark mode + portrait.
- *
- * This allows to test the most important permutations of a screen/layout with only 3
- * configurations.
- */
-val DeviceEmulationSpec.Companion.PhoneAndTabletMinimal
- get() = PhoneAndTabletMinimalSpec
-
-private val PhoneAndTabletMinimalSpec =
- DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false) +
- DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = true, isLandscape = false)
-
-object Displays {
- val Phone =
- DisplaySpec(
- "phone",
- width = 1440,
- height = 3120,
- densityDpi = 560,
- )
-
- val Tablet =
- DisplaySpec(
- "tablet",
- width = 2560,
- height = 1600,
- densityDpi = 320,
- )
-}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsGoldenImagePathManager.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsGoldenImagePathManager.kt
index 25bc098..f5fba7f 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsGoldenImagePathManager.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsGoldenImagePathManager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.util
import androidx.test.platform.app.InstrumentationRegistry
import platform.test.screenshot.GoldenImagePathManager
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
index 7a7cf31..3dcefe9 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/util/SettingsScreenshotTestRule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.util
import androidx.activity.ComponentActivity
import androidx.compose.material3.MaterialTheme
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
index b2e0b18..b74a243 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/button/ActionButtonsScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,12 +14,13 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.button
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.material.icons.outlined.WarningAmber
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.button.ActionButton
import com.android.settingslib.spa.widget.button.ActionButtons
import org.junit.Rule
@@ -27,6 +28,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -48,7 +50,7 @@
fun test() {
screenshotRule.screenshotTest("actionButtons") {
val actionButtons = listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {},
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
index e6decb1..051ef77 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/BarChartScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,9 +14,10 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.chart
import androidx.compose.material3.MaterialTheme
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.BarChart
import com.android.settingslib.spa.widget.chart.BarChartData
import com.android.settingslib.spa.widget.chart.BarChartModel
@@ -26,6 +27,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -61,7 +63,7 @@
override val colors = listOf(color)
override val xValueFormatter =
IAxisValueFormatter { value, _ ->
- "${WeekDay.values()[value.toInt()]}"
+ "${WeekDay.entries[value.toInt()]}"
}
override val yValueFormatter =
IAxisValueFormatter { value, _ ->
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
index f9d93f8..3822571 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/LineChartScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,8 +14,9 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.chart
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.LineChart
import com.android.settingslib.spa.widget.chart.LineChartData
import com.android.settingslib.spa.widget.chart.LineChartModel
@@ -26,6 +27,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -59,7 +61,7 @@
)
override val xValueFormatter =
IAxisValueFormatter { value, _ ->
- "${WeekDay.values()[value.toInt()]}"
+ "${WeekDay.entries[value.toInt()]}"
}
override val yValueFormatter =
IAxisValueFormatter { value, _ ->
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
index 34ded3c..6dd62ec 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/chart/PieChartScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,8 +14,9 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.chart
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.chart.PieChart
import com.android.settingslib.spa.widget.chart.PieChartData
import com.android.settingslib.spa.widget.chart.PieChartModel
@@ -24,6 +25,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -56,4 +58,4 @@
)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
index 91aca05..0ccfc0b 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/illustration/ImageIllustrationScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,8 +14,10 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.illustration
+import com.android.settingslib.spa.screenshot.R
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.illustration.Illustration
import com.android.settingslib.spa.widget.illustration.IllustrationModel
import com.android.settingslib.spa.widget.illustration.ResourceType
@@ -24,6 +26,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
index a366b9e..c1d7188 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,10 +14,11 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import org.junit.Rule
@@ -25,6 +26,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
index d72152c..dd6b553 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/PreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,14 +14,14 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Autorenew
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsIcon
@@ -30,6 +30,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -61,18 +62,18 @@
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = SUMMARY.toState()
+ override val summary = { SUMMARY }
})
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = LONG_SUMMARY.toState()
+ override val summary = { LONG_SUMMARY }
})
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = SUMMARY.toState()
- override val enabled = false.toState()
+ override val summary = { SUMMARY }
+ override val enabled = { false }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.DisabledByDefault)
}
@@ -80,7 +81,7 @@
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = SUMMARY.toState()
+ override val summary = { SUMMARY }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.Autorenew)
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
index 5fcaf85..357d815 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/ProgressBarPreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,13 +14,14 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.SystemUpdate
import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.ProgressBarPreference
import com.android.settingslib.spa.widget.preference.ProgressBarPreferenceModel
import com.android.settingslib.spa.widget.preference.ProgressBarWithDataPreference
@@ -30,6 +31,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
index 48c922d..fdee7ee 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SliderPreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,11 +14,12 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessAlarm
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SliderPreference
import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
import org.junit.Rule
@@ -26,6 +27,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
index 2c84a8e..a688e11 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,13 +14,14 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.ui.SettingsIcon
@@ -29,6 +30,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -72,7 +74,7 @@
private fun SampleSwitchPreferenceWithSummary() {
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val summary = stateOf("With summary")
+ override val summary = { "With summary" }
override val checked = stateOf(true)
override val onCheckedChange = null
})
@@ -82,7 +84,7 @@
private fun SampleNotChangeableSwitchPreference() {
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val summary = stateOf("Not changeable")
+ override val summary = { "Not changeable" }
override val changeable = stateOf(false)
override val checked = stateOf(true)
override val onCheckedChange = null
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
index 2c37212..8f0abc0 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,19 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
import com.android.settingslib.spa.framework.compose.stateOf
-import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
@@ -54,7 +56,7 @@
TwoTargetSwitchPreference(object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val summary = stateOf("With summary")
+ override val summary = { "With summary" }
override val checked = stateOf(true)
override val onCheckedChange = null
}) {}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
index 0a0faf6..fb01f77 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/FooterScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,14 +14,16 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.ui
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.ui.Footer
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
index 0b4d5e4..2867741 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/ui/SpinnerScreenshotTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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,8 +14,9 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.screenshot
+package com.android.settingslib.spa.screenshot.widget.ui
+import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.ui.Spinner
import com.android.settingslib.spa.widget.ui.SpinnerOption
import org.junit.Rule
@@ -23,6 +24,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.PhoneAndTabletMinimal
/** A screenshot test for ExampleFeature. */
@RunWith(Parameterized::class)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
index 078c925..14af508 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -37,7 +37,6 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.SESSION_BROWSE
import com.android.settingslib.spa.framework.util.SESSION_SEARCH
@@ -137,7 +136,7 @@
val page = pageWithEntry.page
Preference(object : PreferenceModel {
override val title = "${page.debugBrief()} (${pageWithEntry.entries.size})"
- override val summary = page.debugArguments().toState()
+ override val summary = { page.debugArguments() }
override val onClick = navigator(route = ROUTE_PAGE + "/${page.id}")
})
}
@@ -179,8 +178,9 @@
Text(text = "Entry size: ${pageWithEntry.entries.size}")
Preference(model = object : PreferenceModel {
override val title = "open page"
- override val enabled = (spaEnvironment.browseActivityClass != null &&
- page.isBrowsable()).toState()
+ override val enabled = {
+ spaEnvironment.browseActivityClass != null && page.isBrowsable()
+ }
override val onClick = openPage(page)
})
EntryList(pageWithEntry.entries)
@@ -196,9 +196,10 @@
RegularScaffold(title = "Entry - ${entry.debugBrief()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
- override val enabled = (spaEnvironment.browseActivityClass != null &&
- entry.containerPage().isBrowsable())
- .toState()
+ override val enabled = {
+ spaEnvironment.browseActivityClass != null &&
+ entry.containerPage().isBrowsable()
+ }
override val onClick = openEntry(entry)
})
Text(text = entryContent)
@@ -210,8 +211,9 @@
for (entry in entries) {
Preference(object : PreferenceModel {
override val title = entry.debugBrief()
- override val summary =
- "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}".toState()
+ override val summary = {
+ "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}"
+ }
override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id}")
})
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
index 74f9c9d..a0149da 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -27,8 +27,6 @@
import androidx.compose.material3.RadioButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.IntState
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -37,7 +35,6 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.SettingsDialog
import com.android.settingslib.spa.widget.ui.SettingsDialogItem
@@ -69,8 +66,8 @@
*
* Disabled [ListPreference] will be displayed in disabled style.
*/
- val enabled: State<Boolean>
- get() = stateOf(true)
+ val enabled: () -> Boolean
+ get() = { true }
val options: List<ListPreferenceOption>
@@ -89,7 +86,7 @@
) {
Column(modifier = Modifier.selectableGroup()) {
for (option in model.options) {
- Radio(option, model.selectedId.intValue, model.enabled.value) {
+ Radio(option, model.selectedId.intValue, model.enabled()) {
dialogOpened = false
model.onIdSelected(it)
}
@@ -100,7 +97,7 @@
Preference(model = remember(model) {
object : PreferenceModel {
override val title = model.title
- override val summary = derivedStateOf {
+ override val summary = {
model.options.find { it.id == model.selectedId.intValue }?.text ?: ""
}
override val icon = model.icon
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index 7ecbec7..bb7e857 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -18,14 +18,12 @@
import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.android.settingslib.spa.framework.common.EntryMacro
import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.EntryHighlight
import com.android.settingslib.spa.framework.util.wrapOnClickWithLog
import com.android.settingslib.spa.widget.ui.createSettingsIcon
@@ -42,9 +40,9 @@
override fun UiLayout() {
Preference(model = object : PreferenceModel {
override val title: String = this@SimplePreferenceMacro.title
- override val summary = stateOf(this@SimplePreferenceMacro.summary ?: "")
+ override val summary = { this@SimplePreferenceMacro.summary ?: "" }
override val icon = createSettingsIcon(this@SimplePreferenceMacro.icon)
- override val enabled = stateOf(!this@SimplePreferenceMacro.disabled)
+ override val enabled = { !disabled }
override val onClick = navigator(clickRoute)
})
}
@@ -69,8 +67,8 @@
/**
* The summary of this [Preference].
*/
- val summary: State<String>
- get() = stateOf("")
+ val summary: () -> String
+ get() = { "" }
/**
* The icon of this [Preference].
@@ -85,8 +83,8 @@
*
* Disabled [Preference] will be displayed in disabled style.
*/
- val enabled: State<Boolean>
- get() = stateOf(true)
+ val enabled: () -> Boolean
+ get() = { true }
/**
* The on click handler of this [Preference].
@@ -108,10 +106,11 @@
singleLineSummary: Boolean = false,
) {
val onClickWithLog = wrapOnClickWithLog(model.onClick)
- val modifier = remember(model.enabled.value) {
+ val enabled = model.enabled()
+ val modifier = remember(enabled) {
if (onClickWithLog != null) {
Modifier.clickable(
- enabled = model.enabled.value,
+ enabled = enabled,
onClick = onClickWithLog
)
} else Modifier
@@ -119,11 +118,11 @@
EntryHighlight {
BasePreference(
title = model.title,
- summary = { model.summary.value },
+ summary = model.summary,
singleLineSummary = singleLineSummary,
modifier = modifier,
icon = model.icon,
- enabled = { model.enabled.value },
+ enabled = model.enabled,
)
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
index f14f68c..12afe92 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -51,8 +51,8 @@
/**
* The summary of this [SwitchPreference].
*/
- val summary: State<String>
- get() = stateOf("")
+ val summary: () -> String
+ get() = { "" }
/**
* The icon of this [Preference].
@@ -95,7 +95,7 @@
EntryHighlight {
InternalSwitchPreference(
title = model.title,
- summary = { model.summary.value },
+ summary = model.summary,
icon = model.icon,
checked = model.checked.value,
changeable = model.changeable.value,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt
index b8db63c..9866023 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,32 +16,32 @@
package com.android.settingslib.spa.widget.preference
-import com.android.settingslib.spa.framework.util.EntryHighlight
+import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.runtime.State
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.material3.Icon
+import com.android.settingslib.spa.framework.util.EntryHighlight
@Composable
fun TwoTargetButtonPreference(
- title: String,
- summary: State<String>,
- icon: @Composable (() -> Unit)? = null,
- onClick: () -> Unit,
- buttonIcon: ImageVector,
- buttonIconDescription: String,
- onButtonClick: () -> Unit
+ title: String,
+ summary: () -> String,
+ icon: @Composable (() -> Unit)? = null,
+ onClick: () -> Unit,
+ buttonIcon: ImageVector,
+ buttonIconDescription: String,
+ onButtonClick: () -> Unit
) {
EntryHighlight {
TwoTargetPreference(
- title = title,
- summary = summary,
- onClick = onClick,
- icon = icon) {
+ title = title,
+ summary = summary,
+ onClick = onClick,
+ icon = icon,
+ ) {
IconButton(onClick = onButtonClick) {
Icon(imageVector = buttonIcon, contentDescription = buttonIconDescription)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index 5663610..e36572f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -24,7 +24,6 @@
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -35,7 +34,7 @@
@Composable
internal fun TwoTargetPreference(
title: String,
- summary: State<String>,
+ summary: () -> String,
onClick: () -> Unit,
icon: @Composable (() -> Unit)? = null,
widget: @Composable () -> Unit,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index 9f7f040..aa148b0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -21,7 +21,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.material3.PrimaryTabRow
+import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -43,7 +43,7 @@
val coroutineScope = rememberCoroutineScope()
val pagerState = rememberPagerState { titles.size }
- PrimaryTabRow(
+ TabRow(
selectedTabIndex = pagerState.currentPage,
modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd),
containerColor = Color.Transparent,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
index 997a023..796ac48 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -25,7 +25,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.onDialogText
import org.junit.Rule
import org.junit.Test
@@ -92,7 +91,7 @@
ListPreference(remember {
object : ListPreferenceModel {
override val title = TITLE
- override val enabled = stateOf(false)
+ override val enabled = { false }
override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
override val selectedId = mutableIntStateOf(1)
override val onIdSelected: (Int) -> Unit = {}
@@ -154,7 +153,7 @@
ListPreference(remember {
object : ListPreferenceModel {
override val title = TITLE
- override val enabled = enabledState
+ override val enabled = { enabledState.value }
override val options = listOf(
ListPreferenceOption(id = 1, text = "A"),
ListPreferenceOption(id = 2, text = "B"),
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
index 06936e1..8c363db 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -19,7 +19,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -34,7 +33,6 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.toState
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Rule
@@ -65,7 +63,7 @@
Box(Modifier.width(BOX_WIDTH)) {
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = LONG_SUMMARY.toState()
+ override val summary = { LONG_SUMMARY }
})
}
lineHeightDp = with(LocalDensity.current) {
@@ -85,7 +83,7 @@
Preference(
model = object : PreferenceModel {
override val title = TITLE
- override val summary = LONG_SUMMARY.toState()
+ override val summary = { LONG_SUMMARY }
},
singleLineSummary = true,
)
@@ -113,7 +111,7 @@
var count by remember { mutableStateOf(0) }
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = derivedStateOf { count.toString() }
+ override val summary = { count.toString() }
override val onClick: (() -> Unit) = { count++ }
})
}
@@ -128,8 +126,8 @@
var count by remember { mutableStateOf(0) }
Preference(object : PreferenceModel {
override val title = TITLE
- override val summary = derivedStateOf { count.toString() }
- override val enabled = false.toState()
+ override val summary = { count.toString() }
+ override val enabled = { false }
override val onClick: (() -> Unit) = { count++ }
})
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ProgressBarPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ProgressBarPreferenceTest.kt
index 2140c07..e6d2401 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ProgressBarPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ProgressBarPreferenceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -17,8 +17,7 @@
package com.android.settingslib.spa.widget.preference
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Launch
-import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsProperties.ProgressBarRangeInfo
import androidx.compose.ui.test.SemanticsMatcher
@@ -49,11 +48,14 @@
@Test
fun data_displayed() {
composeTestRule.setContent {
- ProgressBarWithDataPreference(model = object : ProgressBarPreferenceModel {
- override val title = "Title"
- override val progress = 0.2f
- override val icon: ImageVector = Icons.Outlined.Launch
- }, data = "Data")
+ ProgressBarWithDataPreference(
+ model = object : ProgressBarPreferenceModel {
+ override val title = "Title"
+ override val progress = 0.2f
+ override val icon = Icons.AutoMirrored.Outlined.Launch
+ },
+ data = "Data",
+ )
}
composeTestRule.onNodeWithText("Title").assertIsDisplayed()
composeTestRule.onNodeWithText("Data").assertIsDisplayed()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt
index 3a2b445..6de1933 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.widget.preference
import androidx.compose.material.icons.Icons
@@ -9,7 +25,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.toState
import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.Test
@@ -64,10 +79,10 @@
) {
TwoTargetButtonPreference(
title = TEST_MODEL_TITLE,
- summary = TEST_MODEL_SUMMARY.toState(),
+ summary = { TEST_MODEL_SUMMARY },
onClick = onClick,
buttonIcon = TEST_BUTTON_ICON,
buttonIconDescription = TEST_BUTTON_ICON_DESCRIPTION,
onButtonClick = onButtonClick
)
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
index 16e09ee..09a6e6d 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -21,7 +21,6 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import org.junit.Rule
@@ -50,7 +49,7 @@
Preference(remember {
object : PreferenceModel {
override val title = "Some Preference"
- override val summary = stateOf("Some summary")
+ override val summary = { "Some summary" }
}
})
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
index f3ab80c7..9eee6ad 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
@@ -19,7 +19,6 @@
import android.content.pm.ApplicationInfo
import android.icu.text.CollationKey
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.template.app.AppListItem
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
@@ -89,7 +88,7 @@
* @return null if no summary should be displayed.
*/
@Composable
- fun getSummary(option: Int, record: T): State<String>? = null
+ fun getSummary(option: Int, record: T): (() -> String)? = null
@Composable
fun AppListItemModel<T>.AppItem() {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 066db34..7c45b64 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -36,7 +36,6 @@
import com.android.settingslib.spa.framework.compose.LogCompositions
import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
import com.android.settingslib.spa.framework.compose.rememberLazyListStateAndHideKeyboardWhenStartScroll
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.widget.ui.CategoryTitle
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
import com.android.settingslib.spa.widget.ui.Spinner
@@ -150,7 +149,7 @@
?.let { group -> CategoryTitle(title = group) }
val appEntry = list[it]
- val summary = getSummary(option, appEntry.record) ?: "".toState()
+ val summary = getSummary(option, appEntry.record) ?: { "" }
remember(appEntry) {
AppListItemModel(appEntry.record, appEntry.label, summary)
}.AppItem()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItemModel.kt
similarity index 89%
rename from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt
rename to packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItemModel.kt
index 6d0d7d6..a7c5036 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItemModel.kt
@@ -17,11 +17,9 @@
package com.android.settingslib.spaprivileged.template.app
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
@@ -31,7 +29,7 @@
data class AppListItemModel<T : AppRecord>(
val record: T,
val label: String,
- val summary: State<String>,
+ val summary: () -> String,
)
@Composable
@@ -55,6 +53,6 @@
val record = object : AppRecord {
override val app = LocalContext.current.applicationInfo
}
- AppListItemModel<AppRecord>(record, "Chrome", "Allowed".toState()).AppListItem {}
+ AppListItemModel<AppRecord>(record, "Chrome", { "Allowed" }).AppListItem {}
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 17e9708..3ab27367 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -20,8 +20,7 @@
import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -149,33 +148,27 @@
override fun getSummary(option: Int, record: T) = getSummary(record)
@Composable
- fun getSummary(record: T): State<String> {
+ fun getSummary(record: T): () -> String {
val restrictions = remember(record.app.userId) {
Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
)
}
- val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions)
- val allowed = listModel.isAllowed(record)
- return remember {
- derivedStateOf {
- RestrictedSwitchPreference.getSummary(
- context = context,
- restrictedMode = restrictedMode.value,
- summaryIfNoRestricted = getSummaryIfNoRestricted(allowed),
- checked = allowed,
- ).value
- }
- }
+ val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
+ val allowed by listModel.isAllowed(record)
+ return RestrictedSwitchPreference.getSummary(
+ context = context,
+ restrictedModeSupplier = { restrictedMode },
+ summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed) },
+ checked = { allowed },
+ )
}
- private fun getSummaryIfNoRestricted(allowed: State<Boolean?>) = derivedStateOf {
- when (allowed.value) {
- true -> context.getString(R.string.app_permission_summary_allowed)
- false -> context.getString(R.string.app_permission_summary_not_allowed)
- null -> context.getPlaceholder()
- }
+ private fun getSummaryIfNoRestricted(allowed: Boolean?): String = when (allowed) {
+ true -> context.getString(R.string.app_permission_summary_allowed)
+ false -> context.getString(R.string.app_permission_summary_not_allowed)
+ null -> context.getPlaceholder()
}
@Composable
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
index 50490c0..ac85dd4 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedPreference.kt
@@ -23,7 +23,6 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
@@ -73,7 +72,7 @@
override val enabled = when (restrictedMode) {
NoRestricted -> model.enabled
- else -> stateOf(false)
+ else -> ({ false })
}
override val onClick = when (restrictedMode) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index 2129403..d17e0c7 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -21,8 +21,6 @@
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -75,17 +73,16 @@
internal object RestrictedSwitchPreference {
fun getSummary(
context: Context,
- restrictedMode: RestrictedMode?,
- summaryIfNoRestricted: State<String>,
- checked: State<Boolean?>,
- ): State<String> = when (restrictedMode) {
- is NoRestricted -> summaryIfNoRestricted
- is BaseUserRestricted -> stateOf(
- context.getString(com.android.settingslib.R.string.disabled)
- )
-
- is BlockedByAdmin -> derivedStateOf { restrictedMode.getSummary(checked.value) }
- null -> stateOf(context.getPlaceholder())
+ restrictedModeSupplier: () -> RestrictedMode?,
+ summaryIfNoRestricted: () -> String,
+ checked: () -> Boolean?,
+ ): () -> String = {
+ when (val restrictedMode = restrictedModeSupplier()) {
+ is NoRestricted -> summaryIfNoRestricted()
+ is BaseUserRestricted -> context.getString(com.android.settingslib.R.string.disabled)
+ is BlockedByAdmin -> restrictedMode.getSummary(checked())
+ null -> context.getPlaceholder()
+ }
}
}
@@ -98,9 +95,9 @@
override val summary = RestrictedSwitchPreference.getSummary(
context = context,
- restrictedMode = restrictedMode,
+ restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = model.summary,
- checked = model.checked,
+ checked = { model.checked.value },
)
override val checked = when (restrictedMode) {
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
index 2fd1b10..c29d7c2 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
@@ -158,7 +158,7 @@
override val app = APP
},
label = LABEL,
- summary = stateOf(SUMMARY),
+ summary = { SUMMARY },
)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
index 6e7fc8e..644a2d7 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
@@ -183,7 +183,7 @@
override val app = APP
},
label = LABEL,
- summary = stateOf(SUMMARY),
+ summary = { SUMMARY },
)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
index 457b810..bf0ad0b 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.State
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
@@ -27,7 +26,6 @@
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
@@ -72,10 +70,9 @@
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
- val summaryState = getSummary(listModel)
+ val summary = getSummary(listModel)
- assertThat(summaryState.value)
- .isEqualTo(context.getString(R.string.app_permission_summary_allowed))
+ assertThat(summary).isEqualTo(context.getString(R.string.app_permission_summary_allowed))
}
@Test
@@ -83,9 +80,9 @@
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
- val summaryState = getSummary(listModel)
+ val summary = getSummary(listModel)
- assertThat(summaryState.value)
+ assertThat(summary)
.isEqualTo(context.getString(R.string.app_permission_summary_not_allowed))
}
@@ -94,9 +91,9 @@
fakeRestrictionsProvider.restrictedMode = NoRestricted
val listModel = TestTogglePermissionAppListModel(isAllowed = null)
- val summaryState = getSummary(listModel)
+ val summary = getSummary(listModel)
- assertThat(summaryState.value).isEqualTo(context.getPlaceholder())
+ assertThat(summary).isEqualTo(context.getPlaceholder())
}
@Test
@@ -108,7 +105,7 @@
AppListItemModel(
record = listModel.transformItem(APP),
label = LABEL,
- summary = stateOf(SUMMARY),
+ summary = { SUMMARY },
).AppItem()
}
}
@@ -152,12 +149,12 @@
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
)
- private fun getSummary(listModel: TestTogglePermissionAppListModel): State<String> {
- lateinit var summary: State<String>
+ private fun getSummary(listModel: TestTogglePermissionAppListModel): String {
+ lateinit var summary: () -> String
composeTestRule.setContent {
summary = createInternalAppListModel(listModel).getSummary(record = TestAppRecord(APP))
}
- return summary
+ return summary()
}
private companion object {
diff --git a/packages/SettingsLib/res/drawable/ic_hdmi.xml b/packages/SettingsLib/res/drawable/ic_hdmi.xml
new file mode 100644
index 0000000..c7a553b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hdmi.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 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="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M320,880L320,760L200,521L200,280L240,280L240,140Q240,117 258.5,98.5Q277,80 300,80L660,80Q683,80 701.5,98.5Q720,117 720,140L720,280L760,280L760,521L640,760L640,880L320,880ZM300,280L398,280L398,198L432,198L432,280L528,280L528,198L562,198L562,280L660,280L660,140Q660,140 660,140Q660,140 660,140L300,140Q300,140 300,140Q300,140 300,140L300,280ZM378,743L378,743L582,743L582,743L582,743L378,743L378,743ZM378,743L582,743L700,504L700,340L260,340L260,504L378,743Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_tv.xml b/packages/SettingsLib/res/drawable/ic_tv.xml
new file mode 100644
index 0000000..87abaf4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_tv.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 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="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M320,840L320,760L160,760Q127,760 103.5,736.5Q80,713 80,680L80,200Q80,167 103.5,143.5Q127,120 160,120L800,120Q833,120 856.5,143.5Q880,167 880,200L880,680Q880,713 856.5,736.5Q833,760 800,760L640,760L640,840L320,840ZM160,680L800,680Q800,680 800,680Q800,680 800,680L800,200Q800,200 800,200Q800,200 800,200L160,200Q160,200 160,200Q160,200 160,200L160,680Q160,680 160,680Q160,680 160,680ZM160,680Q160,680 160,680Q160,680 160,680L160,200Q160,200 160,200Q160,200 160,200L160,200Q160,200 160,200Q160,200 160,200L160,680Q160,680 160,680Q160,680 160,680L160,680Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_usb.xml b/packages/SettingsLib/res/drawable/ic_usb.xml
new file mode 100644
index 0000000..b5f15ea
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_usb.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 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="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,880Q448,880 428,860Q408,840 408,808Q408,786 419,768Q430,750 450,739L450,628L302,628Q278,628 260,610Q242,592 242,568L242,459Q222,450 211,432.39Q200,414.78 200,392.28Q200,360 220,340Q240,320 272,320Q304,320 324,340Q344,360 344,392.41Q344,415 333,432.5Q322,450 302,459L302,568Q302,568 302,568Q302,568 302,568L450,568L450,228L370,228L480,79L590,228L510,228L510,568L658,568Q658,568 658,568Q658,568 658,568L658,464L616,464L616,320L760,320L760,464L718,464L718,568Q718,592 700,610Q682,628 658,628L510,628L510,739Q529.95,749.65 540.97,768.83Q552,788 552,808Q552,840 532,860Q512,880 480,880Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_wired_device.xml b/packages/SettingsLib/res/drawable/ic_wired_device.xml
new file mode 100644
index 0000000..7964c9f
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_wired_device.xml
@@ -0,0 +1,25 @@
+<!--
+ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M123,920L123,750Q89,737 64.5,707.5Q40,678 40,643L40,245L123,245L123,70Q123,56 131,48Q139,40 153,40Q167,40 175,48Q183,56 183,70L183,245L266,245L266,643Q266,678 242,707.5Q218,737 183,750L183,920L123,920ZM450,920L450,750Q416,737 391.5,707.5Q367,678 367,643L367,245L450,245L450,70Q450,56 458,48Q466,40 480,40Q494,40 502,48Q510,56 510,70L510,245L593,245L593,643Q593,678 569,707.5Q545,737 510,750L510,920L450,920ZM777,920L777,750Q743,737 718.5,707.5Q694,678 694,643L694,245L777,245L777,70Q777,56 785,48Q793,40 807,40Q821,40 829,48Q837,56 837,70L837,245L920,245L920,643Q920,678 895.5,707.5Q871,737 837,750L837,920L777,920ZM100,489L206,489L206,305L100,305L100,489ZM427,489L533,489L533,305L427,305L427,489ZM754,489L860,489L860,305L754,305L754,489ZM153,489L153,489L153,489L153,489L153,489ZM480,489L480,489L480,489L480,489L480,489ZM807,489L807,489L807,489L807,489L807,489ZM100,489L100,489L206,489L206,489L100,489ZM427,489L427,489L533,489L533,489L427,489ZM754,489L754,489L860,489L860,489L754,489Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 22131df..1a5acf6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1027,6 +1027,13 @@
<!-- Settings item title to select whether to disable cache for transcoding. [CHAR LIMIT=85] -->
<string name="transcode_disable_cache">Disable transcoding cache</string>
+ <!-- Developer settings title: widevine settings screen. [CHAR LIMIT=50] -->
+ <string name="widevine_settings_title">Widevine settings</string>
+ <!-- Developer settings title: select whether to enable Force L3 fallback. [CHAR LIMIT=50] -->
+ <string name="force_l3_fallback_title">Force L3 fallback</string>
+ <!-- Developer settings summary: select whether to enable Force L3 fallback.[CHAR LIMIT=NONE] -->
+ <string name="force_l3_fallback_summary">Select to force L3 fallback</string>
+
<!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
<string name="runningservices_settings_title">Running services</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
@@ -1327,6 +1334,8 @@
<string name="media_transfer_this_device_name" product="default">This phone</string>
<!-- Name of the tablet device. [CHAR LIMIT=30] -->
<string name="media_transfer_this_device_name" product="tablet">This tablet</string>
+ <!-- Name of the default media output of the TV. [CHAR LIMIT=30] -->
+ <string name="media_transfer_this_device_name" product="tv">@string/tv_media_transfer_default</string>
<!-- Name of the dock device. [CHAR LIMIT=30] -->
<string name="media_transfer_dock_speaker_device_name">Dock speaker</string>
<!-- Default name of the external device. [CHAR LIMIT=30] -->
@@ -1350,6 +1359,26 @@
<!-- Sub status indicates the device does not support the current media track. [CHAR LIMIT=NONE] -->
<string name="media_output_status_track_unsupported">Can\’t play this media here</string>
+ <!-- Media output switcher. Default subtitle for any output option that is connected if no more information is known [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_connected">Connected</string>
+
+ <!-- TV media output switcher. Title for devices connected through HDMI ARC if no device name is available. [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_arc_fallback_title">HDMI ARC</string>
+ <!-- TV media output switcher. Title for devices connected through HDMI EARC if no device name is available. [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_earc_fallback_title">HDMI eARC</string>
+
+ <!-- TV media output switcher. Subtitle for devices connected through HDMI ARC if a device name is available. [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_arc_subtitle">Connected via ARC</string>
+ <!-- Media output switcher. Subtitle for devices connected through HDMI EARC if a device name is available. [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_earc_subtitle">Connected via eARC</string>
+
+ <!-- TV media output switcher. Title for the default audio output of the device [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_default">TV Default</string>
+ <!-- TV media output switcher. Subtitle for default audio output which is HDMI, e.g. TV dongle [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_hdmi">HDMI Output</string>
+ <!-- TV media output switcher. Subtitle for default audio output which is internal speaker, i.e. panel VTs [CHAR LIMIT=NONE] -->
+ <string name="tv_media_transfer_internal_speakers">Internal Speakers</string>
+
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java
deleted file mode 100644
index 117b48f..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2017 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.settingslib.inputmethod;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceScreen;
-import androidx.preference.TwoStatePreference;
-
-import com.android.settingslib.R;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-public class InputMethodAndSubtypeEnablerManager implements Preference.OnPreferenceChangeListener {
-
- private final PreferenceFragment mFragment;
-
- private boolean mHaveHardKeyboard;
- private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
- new HashMap<>();
- private final HashMap<String, TwoStatePreference> mAutoSelectionPrefsMap = new HashMap<>();
- private InputMethodManager mImm;
- // TODO: Change mInputMethodInfoList to Map
- private List<InputMethodInfo> mInputMethodInfoList;
- private final Collator mCollator = Collator.getInstance();
-
- public InputMethodAndSubtypeEnablerManager(PreferenceFragment fragment) {
- mFragment = fragment;
- mImm = fragment.getContext().getSystemService(InputMethodManager.class);
-
- mInputMethodInfoList = mImm.getInputMethodList();
- }
-
- public void init(PreferenceFragment fragment, String targetImi, PreferenceScreen root) {
- final Configuration config = fragment.getResources().getConfiguration();
- mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
-
- for (final InputMethodInfo imi : mInputMethodInfoList) {
- // Add subtype preferences of this IME when it is specified or no IME is specified.
- if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) {
- addInputMethodSubtypePreferences(fragment, imi, root);
- }
- }
- }
-
- public void refresh(Context context, PreferenceFragment fragment) {
- // Refresh internal states in mInputMethodSettingValues to keep the latest
- // "InputMethodInfo"s and "InputMethodSubtype"s
- InputMethodSettingValuesWrapper
- .getInstance(context).refreshAllInputMethodAndSubtypes();
- InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(fragment, context.getContentResolver(),
- mInputMethodInfoList, mInputMethodAndSubtypePrefsMap);
- updateAutoSelectionPreferences();
- }
-
- public void save(Context context, PreferenceFragment fragment) {
- InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(fragment, context.getContentResolver(),
- mInputMethodInfoList, mHaveHardKeyboard);
- }
-
- @Override
- public boolean onPreferenceChange(final Preference pref, final Object newValue) {
- if (!(newValue instanceof Boolean)) {
- return true; // Invoke default behavior.
- }
- final boolean isChecking = (Boolean) newValue;
- for (final String imiId : mAutoSelectionPrefsMap.keySet()) {
- // An auto select subtype preference is changing.
- if (mAutoSelectionPrefsMap.get(imiId) == pref) {
- final TwoStatePreference autoSelectionPref = (TwoStatePreference) pref;
- autoSelectionPref.setChecked(isChecking);
- // Enable or disable subtypes depending on the auto selection preference.
- setAutoSelectionSubtypesEnabled(imiId, autoSelectionPref.isChecked());
- return false;
- }
- }
- // A subtype preference is changing.
- if (pref instanceof InputMethodSubtypePreference) {
- final InputMethodSubtypePreference subtypePref = (InputMethodSubtypePreference) pref;
- subtypePref.setChecked(isChecking);
- if (!subtypePref.isChecked()) {
- // It takes care of the case where no subtypes are explicitly enabled then the auto
- // selection preference is going to be checked.
- updateAutoSelectionPreferences();
- }
- return false;
- }
- return true; // Invoke default behavior.
- }
-
- private void addInputMethodSubtypePreferences(PreferenceFragment fragment, InputMethodInfo imi,
- final PreferenceScreen root) {
- Context prefContext = fragment.getPreferenceManager().getContext();
-
- final int subtypeCount = imi.getSubtypeCount();
- if (subtypeCount <= 1) {
- return;
- }
- final String imiId = imi.getId();
- final PreferenceCategory keyboardSettingsCategory =
- new PreferenceCategory(prefContext);
- root.addPreference(keyboardSettingsCategory);
- final PackageManager pm = prefContext.getPackageManager();
- final CharSequence label = imi.loadLabel(pm);
-
- keyboardSettingsCategory.setTitle(label);
- keyboardSettingsCategory.setKey(imiId);
- // TODO: Use toggle Preference if images are ready.
- final TwoStatePreference autoSelectionPref =
- new SwitchWithNoTextPreference(prefContext);
- mAutoSelectionPrefsMap.put(imiId, autoSelectionPref);
- keyboardSettingsCategory.addPreference(autoSelectionPref);
- autoSelectionPref.setOnPreferenceChangeListener(this);
-
- final PreferenceCategory activeInputMethodsCategory =
- new PreferenceCategory(prefContext);
- activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
- root.addPreference(activeInputMethodsCategory);
-
- CharSequence autoSubtypeLabel = null;
- final ArrayList<Preference> subtypePreferences = new ArrayList<>();
- for (int index = 0; index < subtypeCount; ++index) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(index);
- if (subtype.overridesImplicitlyEnabledSubtype()) {
- if (autoSubtypeLabel == null) {
- autoSubtypeLabel = InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
- subtype, prefContext, imi);
- }
- } else {
- final Preference subtypePref = new InputMethodSubtypePreference(
- prefContext, subtype, imi);
- subtypePreferences.add(subtypePref);
- }
- }
- subtypePreferences.sort((lhs, rhs) -> {
- if (lhs instanceof InputMethodSubtypePreference) {
- return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator);
- }
- return lhs.compareTo(rhs);
- });
- for (final Preference pref : subtypePreferences) {
- activeInputMethodsCategory.addPreference(pref);
- pref.setOnPreferenceChangeListener(this);
- InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
- }
- mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
- if (TextUtils.isEmpty(autoSubtypeLabel)) {
- autoSelectionPref.setTitle(
- R.string.use_system_language_to_select_input_method_subtypes);
- } else {
- autoSelectionPref.setTitle(autoSubtypeLabel);
- }
- }
-
- private boolean isNoSubtypesExplicitlySelected(final String imiId) {
- final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
- for (final Preference pref : subtypePrefs) {
- if (pref instanceof TwoStatePreference && ((TwoStatePreference) pref).isChecked()) {
- return false;
- }
- }
- return true;
- }
-
- private void setAutoSelectionSubtypesEnabled(final String imiId,
- final boolean autoSelectionEnabled) {
- final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
- if (autoSelectionPref == null) {
- return;
- }
- autoSelectionPref.setChecked(autoSelectionEnabled);
- final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
- for (final Preference pref : subtypePrefs) {
- if (pref instanceof TwoStatePreference) {
- // When autoSelectionEnabled is true, all subtype prefs need to be disabled with
- // implicitly checked subtypes. In case of false, all subtype prefs need to be
- // enabled.
- pref.setEnabled(!autoSelectionEnabled);
- if (autoSelectionEnabled) {
- ((TwoStatePreference) pref).setChecked(false);
- }
- }
- }
- if (autoSelectionEnabled) {
- InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
- mFragment, mFragment.getContext().getContentResolver(),
- mInputMethodInfoList, mHaveHardKeyboard);
- updateImplicitlyEnabledSubtypes(imiId);
- }
- }
-
- private void updateImplicitlyEnabledSubtypes(final String targetImiId) {
- // When targetImiId is null, apply to all subtypes of all IMEs
- for (final InputMethodInfo imi : mInputMethodInfoList) {
- final String imiId = imi.getId();
- final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
- // No need to update implicitly enabled subtypes when the user has unchecked the
- // "subtype auto selection".
- if (autoSelectionPref == null || !autoSelectionPref.isChecked()) {
- continue;
- }
- if (imiId.equals(targetImiId) || targetImiId == null) {
- updateImplicitlyEnabledSubtypesOf(imi);
- }
- }
- }
-
- private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi) {
- final String imiId = imi.getId();
- final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
- final List<InputMethodSubtype> implicitlyEnabledSubtypes =
- mImm.getEnabledInputMethodSubtypeList(imi, true);
- if (subtypePrefs == null || implicitlyEnabledSubtypes == null) {
- return;
- }
- for (final Preference pref : subtypePrefs) {
- if (!(pref instanceof TwoStatePreference)) {
- continue;
- }
- final TwoStatePreference subtypePref = (TwoStatePreference) pref;
- subtypePref.setChecked(false);
- for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) {
- final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
- if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
- subtypePref.setChecked(true);
- break;
- }
- }
- }
- }
-
- private void updateAutoSelectionPreferences() {
- for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) {
- setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
- }
- updateImplicitlyEnabledSubtypes(null /* targetImiId */ /* check */);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
index 2a28417..cf224dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
@@ -18,11 +18,13 @@
import android.annotation.DrawableRes;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
import com.android.settingslib.R;
+import com.android.settingslib.media.flags.Flags;
import java.util.Arrays;
import java.util.HashMap;
@@ -31,16 +33,25 @@
/** A util class to get the appropriate icon for different device types. */
public class DeviceIconUtil {
+
+ // A default icon to use if the type is not present in the map.
+ @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;
+ @DrawableRes private static final int DEFAULT_ICON_TV = R.drawable.ic_media_speaker_device;
+
// A map from a @AudioDeviceInfo.AudioDeviceType to full device information.
private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>();
// A map from a @MediaRoute2Info.Type to full device information.
private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>();
- // A default icon to use if the type is not present in the map.
- @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;
- public DeviceIconUtil() {
- List<Device> deviceList =
- Arrays.asList(
+ private final boolean mIsTv;
+
+ public DeviceIconUtil(Context context) {
+ this(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ }
+
+ public DeviceIconUtil(boolean isTv) {
+ mIsTv = isTv && Flags.enableTvMediaOutputDialog();
+ List<Device> deviceList = Arrays.asList(
new Device(
AudioDeviceInfo.TYPE_USB_DEVICE,
MediaRoute2Info.TYPE_USB_DEVICE,
@@ -52,7 +63,7 @@
new Device(
AudioDeviceInfo.TYPE_USB_ACCESSORY,
MediaRoute2Info.TYPE_USB_ACCESSORY,
- R.drawable.ic_headphone),
+ mIsTv ? R.drawable.ic_usb : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_DOCK,
MediaRoute2Info.TYPE_DOCK,
@@ -60,29 +71,27 @@
new Device(
AudioDeviceInfo.TYPE_HDMI,
MediaRoute2Info.TYPE_HDMI,
- R.drawable.ic_headphone),
- // TODO: b/306359110 - Put proper iconography for HDMI_ARC type.
+ mIsTv ? R.drawable.ic_tv : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_HDMI_ARC,
MediaRoute2Info.TYPE_HDMI_ARC,
- R.drawable.ic_headphone),
- // TODO: b/306359110 - Put proper iconography for HDMI_EARC type.
+ mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_HDMI_EARC,
MediaRoute2Info.TYPE_HDMI_EARC,
- R.drawable.ic_headphone),
+ mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_WIRED_HEADSET,
MediaRoute2Info.TYPE_WIRED_HEADSET,
- R.drawable.ic_headphone),
+ mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
MediaRoute2Info.TYPE_WIRED_HEADPHONES,
- R.drawable.ic_headphone),
+ mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
new Device(
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
- R.drawable.ic_smartphone));
+ mIsTv ? R.drawable.ic_tv : R.drawable.ic_smartphone));
for (int i = 0; i < deviceList.size(); i++) {
Device device = deviceList.get(i);
mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device);
@@ -90,6 +99,10 @@
}
}
+ private int getDefaultIcon() {
+ return mIsTv ? DEFAULT_ICON_TV : DEFAULT_ICON;
+ }
+
/** Returns a drawable for an icon representing the given audioDeviceType. */
public Drawable getIconFromAudioDeviceType(
@AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) {
@@ -103,7 +116,7 @@
if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) {
return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes;
}
- return DEFAULT_ICON;
+ return getDefaultIcon();
}
/** Returns a drawable res ID for an icon representing the given mediaRouteType. */
@@ -113,7 +126,7 @@
if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) {
return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes;
}
- return DEFAULT_ICON;
+ return getDefaultIcon();
}
private static class Device {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index c44f66e..80eeab5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -28,15 +28,24 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+import android.Manifest;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.media.flags.Flags;
+
+import java.util.List;
/**
* PhoneMediaDevice extends MediaDevice to represents Phone device.
@@ -58,6 +67,7 @@
public static String getSystemRouteNameFromType(
@NonNull Context context, @NonNull MediaRoute2Info routeInfo) {
CharSequence name;
+ boolean isTv = isTv(context);
switch (routeInfo.getType()) {
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
@@ -73,9 +83,32 @@
name = context.getString(R.string.media_transfer_this_device_name);
break;
case TYPE_HDMI:
+ name = context.getString(isTv ? R.string.tv_media_transfer_default :
+ R.string.media_transfer_external_device_name);
+ break;
case TYPE_HDMI_ARC:
+ if (isTv) {
+ String deviceName = getHdmiOutDeviceName(context);
+ if (deviceName != null) {
+ name = deviceName;
+ } else {
+ name = context.getString(R.string.tv_media_transfer_arc_fallback_title);
+ }
+ } else {
+ name = context.getString(R.string.media_transfer_external_device_name);
+ }
+ break;
case TYPE_HDMI_EARC:
- name = context.getString(R.string.media_transfer_external_device_name);
+ if (isTv) {
+ String deviceName = getHdmiOutDeviceName(context);
+ if (deviceName != null) {
+ name = deviceName;
+ } else {
+ name = context.getString(R.string.tv_media_transfer_arc_fallback_title);
+ }
+ } else {
+ name = context.getString(R.string.media_transfer_external_device_name);
+ }
break;
default:
name = context.getString(R.string.media_transfer_default_device_name);
@@ -94,10 +127,15 @@
String packageName,
RouteListingPreference.Item item) {
super(context, info, packageName, item);
- mDeviceIconUtil = new DeviceIconUtil();
+ mDeviceIconUtil = new DeviceIconUtil(mContext);
initDeviceRecord();
}
+ static boolean isTv(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && Flags.enableTvMediaOutputDialog();
+ }
+
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
@Override
@@ -111,9 +149,64 @@
return SELECTION_BEHAVIOR_TRANSFER;
}
+ private static String getHdmiOutDeviceName(Context context) {
+ HdmiControlManager hdmiControlManager;
+ if (context.checkCallingOrSelfPermission(Manifest.permission.HDMI_CEC)
+ == PackageManager.PERMISSION_GRANTED) {
+ hdmiControlManager = context.getSystemService(HdmiControlManager.class);
+ } else {
+ Log.w(TAG, "Could not get HDMI device name, android.permission.HDMI_CEC denied");
+ return null;
+ }
+
+ HdmiPortInfo hdmiOutputPortInfo = null;
+ for (HdmiPortInfo hdmiPortInfo : hdmiControlManager.getPortInfo()) {
+ if (hdmiPortInfo.getType() == HdmiPortInfo.PORT_OUTPUT) {
+ hdmiOutputPortInfo = hdmiPortInfo;
+ break;
+ }
+ }
+ if (hdmiOutputPortInfo == null) {
+ return null;
+ }
+ List<HdmiDeviceInfo> connectedDevices = hdmiControlManager.getConnectedDevices();
+ for (HdmiDeviceInfo deviceInfo : connectedDevices) {
+ if (deviceInfo.getPortId() == hdmiOutputPortInfo.getId()) {
+ String deviceName = deviceInfo.getDisplayName();
+ if (deviceName != null && !deviceName.isEmpty()) {
+ return deviceName;
+ }
+ }
+ }
+ return null;
+ }
+
@Override
public String getSummary() {
- return mSummary;
+ if (!isTv(mContext)) {
+ return mSummary;
+ }
+ switch (mRouteInfo.getType()) {
+ case TYPE_BUILTIN_SPEAKER:
+ return mContext.getString(R.string.tv_media_transfer_internal_speakers);
+ case TYPE_HDMI:
+ return mContext.getString(R.string.tv_media_transfer_hdmi);
+ case TYPE_HDMI_ARC:
+ if (getHdmiOutDeviceName(mContext) == null) {
+ // Connection type is already part of the title.
+ return mContext.getString(R.string.tv_media_transfer_connected);
+ }
+ return mContext.getString(R.string.tv_media_transfer_arc_subtitle);
+ case TYPE_HDMI_EARC:
+ if (getHdmiOutDeviceName(mContext) == null) {
+ // Connection type is already part of the title.
+ return mContext.getString(R.string.tv_media_transfer_connected);
+ }
+ return mContext.getString(R.string.tv_media_transfer_earc_subtitle);
+ default:
+ return null;
+ }
+
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
index 72dfc17..5669276 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
@@ -20,131 +20,309 @@
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
+import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settingslib.R;
+import com.android.settingslib.media.flags.Flags;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class DeviceIconUtilTest {
- private final DeviceIconUtil mDeviceIconUtil = new DeviceIconUtil();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setup() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+ }
@Test
public void getIconResIdFromMediaRouteType_usbDevice_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
+ .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
public void getIconResIdFromMediaRouteType_usbHeadset_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
public void getIconResIdFromMediaRouteType_usbAccessory_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
- public void getIconResIdFromMediaRouteType_dock_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
- .isEqualTo(R.drawable.ic_headphone);
+ public void getIconResIdFromMediaRouteType_tv_usbAccessory_isUsb() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
+ .isEqualTo(R.drawable.ic_usb);
}
@Test
- public void getIconResIdFromMediaRouteType_hdmi_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
- .isEqualTo(R.drawable.ic_headphone);
+ public void getIconResIdFromMediaRouteType_dock_isDock() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
+ .isEqualTo(R.drawable.ic_dock_device);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
+ .isEqualTo(R.drawable.ic_dock_device);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_hdmi() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_hdmi_isTv() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
+ .isEqualTo(R.drawable.ic_tv);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_hdmiArc_isHeadphone() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_hdmiArc_isHdmi() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC))
+ .isEqualTo(R.drawable.ic_hdmi);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_hdmiEarc_isHeadphone() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_hdmiEarc_isHdmi() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC))
+ .isEqualTo(R.drawable.ic_hdmi);
}
@Test
public void getIconResIdFromMediaRouteType_wiredHeadset_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_wiredHeadset_isWiredDevice() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
+ .isEqualTo(R.drawable.ic_wired_device);
}
@Test
public void getIconResIdFromMediaRouteType_wiredHeadphones_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_wiredHeadphones_isWiredDevice() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
+ .isEqualTo(R.drawable.ic_wired_device);
}
@Test
public void getIconResIdFromMediaRouteType_builtinSpeaker_isSmartphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
- .isEqualTo(R.drawable.ic_smartphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_smartphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_builtinSpeaker_isTv() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_tv);
}
@Test
public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
- .isEqualTo(R.drawable.ic_smartphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_smartphone);
+ }
+
+ @Test
+ public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_media_speaker_device);
}
@Test
public void getIconResIdFromAudioDeviceType_usbDevice_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
+ .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
public void getIconResIdFromAudioDeviceType_usbHeadset_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
public void getIconResIdFromAudioDeviceType_usbAccessory_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
+ .isEqualTo(R.drawable.ic_headphone);
}
@Test
- public void getIconResIdFromAudioDeviceType_dock_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
- .isEqualTo(R.drawable.ic_headphone);
+ public void getIconResIdFromAudioDeviceType_tv_usbAccessory_isUsb() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
+ .isEqualTo(R.drawable.ic_usb);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_dock_isDock() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
+ .isEqualTo(R.drawable.ic_dock_device);
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
+ .isEqualTo(R.drawable.ic_dock_device);
}
@Test
public void getIconResIdFromAudioDeviceType_hdmi_isHeadphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_hdmi_isTv() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
+ .isEqualTo(R.drawable.ic_tv);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_hdmiArc_isHeadphone() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_hdmiArc_isHdmi() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC))
+ .isEqualTo(R.drawable.ic_hdmi);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_hdmiEarc_isHeadphone() {
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_hdmiEarc() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC))
+ .isEqualTo(R.drawable.ic_hdmi);
}
@Test
public void getIconResIdFromAudioDeviceType_wiredHeadset_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_wiredHeadset_isWiredDevice() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
+ .isEqualTo(R.drawable.ic_wired_device);
}
@Test
public void getIconResIdFromAudioDeviceType_wiredHeadphones_isHeadphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
- .isEqualTo(R.drawable.ic_headphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
+ .isEqualTo(R.drawable.ic_headphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_wiredHeadphones_isWiredDevice() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
+ .isEqualTo(R.drawable.ic_wired_device);
}
@Test
public void getIconResIdFromAudioDeviceType_builtinSpeaker_isSmartphone() {
- assertThat(
- mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
- .isEqualTo(R.drawable.ic_smartphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_smartphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_builtinSpeaker_isTv() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_tv);
}
@Test
public void getIconResIdFromAudioDeviceType_unsupportedType_isSmartphone() {
- assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
- .isEqualTo(R.drawable.ic_smartphone);
+ assertThat(new DeviceIconUtil(/* isTv */ false)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_smartphone);
+ }
+
+ @Test
+ public void getIconResIdFromAudioDeviceType_tv_unsupportedType_isSpeaker() {
+ assertThat(new DeviceIconUtil(/* isTv */ true)
+ .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_media_speaker_device);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 976ba21..6bc2716 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -68,7 +68,7 @@
private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
"/system/etc/aconfig_flags.pb",
"/system_ext/etc/aconfig_flags.pb",
- "/system_ext/etc/aconfig_flags.pb",
+ "/product/etc/aconfig_flags.pb",
"/vendor/etc/aconfig_flags.pb");
private static final List<String> PRIVATE_NAMESPACES = List.of(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 8c8a71c..785003a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3030,11 +3030,25 @@
}
@GuardedBy("mLock")
- public void ensureSettingsForUserLocked(int userId) {
+ @Nullable
+ private SettingsState getOrCreateSettingsStateLocked(int key) {
+ SettingsState settingsState = mSettingsStates.get(key);
+ if (settingsState != null) {
+ return settingsState;
+ }
+
+ if (!ensureSettingsForUserLocked(getUserIdFromKey(key))) {
+ return null;
+ }
+ return mSettingsStates.get(key);
+ }
+
+ @GuardedBy("mLock")
+ public boolean ensureSettingsForUserLocked(int userId) {
// First make sure this user actually exists.
if (mUserManager.getUserInfo(userId) == null) {
Slog.wtf(LOG_TAG, "Requested user " + userId + " does not exist");
- return;
+ return false;
}
// Migrate the setting for this user if needed.
@@ -3072,6 +3086,7 @@
// Upgrade the settings to the latest version.
UpgradeController upgrader = new UpgradeController(userId);
upgrader.upgradeIfNeededLocked();
+ return true;
}
@GuardedBy("mLock")
@@ -3149,7 +3164,7 @@
boolean success = false;
boolean wasUnsetNonPredefinedSetting = false;
- SettingsState settingsState = mSettingsStates.get(key);
+ SettingsState settingsState = getOrCreateSettingsStateLocked(key);
if (settingsState != null) {
if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
wasUnsetNonPredefinedSetting = true;
@@ -3184,7 +3199,7 @@
@GuardedBy("mLock")
public boolean setConfigSettingsLocked(int key, String prefix,
Map<String, String> keyValues, String packageName) {
- SettingsState settingsState = mSettingsStates.get(key);
+ SettingsState settingsState = getOrCreateSettingsStateLocked(key);
if (settingsState != null) {
if (settingsState.isNewConfigBannedLocked(prefix, keyValues)) {
return false;
@@ -3207,7 +3222,7 @@
final int key = makeKey(type, userId);
boolean success = false;
- SettingsState settingsState = mSettingsStates.get(key);
+ SettingsState settingsState = getOrCreateSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.deleteSettingLocked(name);
}
@@ -3232,7 +3247,7 @@
final int key = makeKey(type, userId);
boolean success = false;
- SettingsState settingsState = mSettingsStates.get(key);
+ SettingsState settingsState = getOrCreateSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.updateSettingLocked(name, value, tag,
makeDefault, packageName);
@@ -3284,7 +3299,7 @@
public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
- SettingsState settingsState = mSettingsStates.get(key);
+ SettingsState settingsState = getOrCreateSettingsStateLocked(key);
if (settingsState == null) {
return false;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9dacade..1f171ba 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -691,6 +691,7 @@
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
+ Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS,
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
Settings.Secure.ALWAYS_ON_VPN_APP,
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 17cc9f8..88abf69 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,7 +60,6 @@
// except for SystemUI-core.
// Copied from compose/features/Android.bp.
static_libs: [
- "CommunalLayoutLib",
"PlatformComposeCore",
"PlatformComposeSceneTransitionLayout",
@@ -166,6 +165,7 @@
"SystemUI-statsd",
"SettingsLib",
"com_android_systemui_flags_lib",
+ "com_android_systemui_shared_flags_lib",
"androidx.core_core-ktx",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
@@ -366,6 +366,7 @@
"tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt",
"tests/src/com/android/systemui/qs/tiles/base/**/*.kt",
"tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt",
+ "tests/src/com/android/systemui/qs/tiles/impl/**/*.kt",
/* Authentication */
"tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt",
@@ -443,6 +444,7 @@
"SystemUI-statsd",
"SettingsLib",
"com_android_systemui_flags_lib",
+ "com_android_systemui_shared_flags_lib",
"flag-junit-base",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index dc4208e..e842967 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -1,3 +1,31 @@
+//
+// Copyright (C) 2023 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.
+//
+
+/**
+ * These flags are meant only for internal use in SystemUI and its variants.
+ * For shared, cross-process flags, see //frameworks/libs/systemui/aconfig
+ */
+
+package {
+ default_visibility: [
+ "//visibility:override",
+ "//frameworks/base/packages/SystemUI:__subpackages__",
+ ],
+}
+
aconfig_declarations {
name: "com_android_systemui_flags",
package: "com.android.systemui",
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 9e06872..21263a9 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -6,7 +6,7 @@
name: "floating_menu_animated_tuck"
namespace: "accessibility"
description: "Sets up animations for tucking/untucking and adjusts clipbounds."
- bug: "24592044"
+ bug: "297556899"
}
flag {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c26d5f5..c274911 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -51,3 +51,18 @@
description: "Enables the scene container framework go/flexiglass."
bug: "283121968"
}
+
+flag {
+ name: "visual_interruptions_refactor"
+ namespace: "systemui"
+ description: "Enables the refactored version of the code to decide when notifications "
+ "HUN, bubble, pulse, or FSI."
+ bug: "261728888"
+}
+
+flag {
+ name: "haptic_brightness_slider"
+ namespace: "systemui"
+ description: "Adds haptic feedback to the brightness slider."
+ bug: "296467915"
+}
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 ab4db45..f7d9056c3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -114,7 +114,13 @@
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
/** The time we wait before timing out the remote animation after starting the intent. */
- private const val LAUNCH_TIMEOUT = 1000L
+ private const val LAUNCH_TIMEOUT = 1_000L
+
+ /**
+ * The time we wait before we Log.wtf because the remote animation was neither started or
+ * cancelled by WM.
+ */
+ private const val LONG_LAUNCH_TIMEOUT = 5_000L
private fun createPositionXInterpolator(): Interpolator {
val path =
@@ -247,7 +253,7 @@
// If we expect an animation, post a timeout to cancel it in case the remote animation is
// never started.
if (willAnimate) {
- runnerDelegate.postTimeout()
+ runnerDelegate.postTimeouts()
// Hide the keyguard using the launch animation instead of the default unlock animation.
if (hideKeyguardWithAnimation) {
@@ -578,21 +584,41 @@
private var cancelled = false
private var animation: LaunchAnimator.Animation? = null
- // A timeout to cancel the remote animation if it is not started within X milliseconds after
- // the intent was started.
- //
- // Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
- // it will be automatically converted when posted and we wouldn't be able to remove it after
- // posting it.
+ /**
+ * A timeout to cancel the launch animation if the remote animation is not started or
+ * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started.
+ *
+ * Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
+ * it will be automatically converted when posted and we wouldn't be able to remove it after
+ * posting it.
+ */
private var onTimeout = Runnable { onAnimationTimedOut() }
- @UiThread
- internal fun postTimeout() {
- timeoutHandler?.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+ /**
+ * A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't
+ * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was
+ * started.
+ */
+ private var onLongTimeout = Runnable {
+ Log.wtf(
+ TAG,
+ "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT"
+ )
}
- private fun removeTimeout() {
- timeoutHandler?.removeCallbacks(onTimeout)
+ @UiThread
+ internal fun postTimeouts() {
+ if (timeoutHandler != null) {
+ timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+ timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT)
+ }
+ }
+
+ private fun removeTimeouts() {
+ if (timeoutHandler != null) {
+ timeoutHandler.removeCallbacks(onTimeout)
+ timeoutHandler.removeCallbacks(onLongTimeout)
+ }
}
@UiThread
@@ -603,7 +629,7 @@
nonApps: Array<out RemoteAnimationTarget>?,
callback: IRemoteAnimationFinishedCallback?
) {
- removeTimeout()
+ removeTimeouts()
// The animation was started too late and we already notified the controller that it
// timed out.
@@ -653,7 +679,6 @@
val window = findRootTaskIfPossible(apps)
if (window == null) {
Log.i(TAG, "Aborting the animation as no window is opening")
- removeTimeout()
iCallback?.invoke()
if (DEBUG_LAUNCH_ANIMATION) {
@@ -890,11 +915,13 @@
}
private fun onAnimationTimedOut() {
+ // The remote animation was cancelled by WM, so we already cancelled the launch
+ // animation.
if (cancelled) {
return
}
- Log.wtf(TAG, "Remote animation timed out")
+ Log.w(TAG, "Remote animation timed out")
timedOut = true
if (DEBUG_LAUNCH_ANIMATION) {
@@ -906,13 +933,15 @@
@UiThread
override fun onAnimationCancelled() {
+ removeTimeouts()
+
+ // The short timeout happened, so we already cancelled the launch animation.
if (timedOut) {
return
}
Log.i(TAG, "Remote animation was cancelled")
cancelled = true
- removeTimeout()
animation?.cancel()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 46d418a..3780468 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -14,6 +14,7 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -29,12 +30,9 @@
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.transitions
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-
-object Scenes {
- val Blank = SceneKey(name = "blank")
- val Communal = SceneKey(name = "communal")
-}
+import kotlinx.coroutines.flow.transform
object Communal {
object Elements {
@@ -43,7 +41,7 @@
}
val sceneTransitions = transitions {
- from(Scenes.Blank, to = Scenes.Communal) {
+ from(TransitionSceneKey.Blank, to = TransitionSceneKey.Communal) {
spec = tween(durationMillis = 500)
translate(Communal.Elements.Content, Edge.Right)
@@ -58,8 +56,14 @@
* handling and transitions before the full Flexiglass layout is ready.
*/
@Composable
-fun CommunalContainer(modifier: Modifier = Modifier, viewModel: CommunalViewModel) {
- val (currentScene, setCurrentScene) = remember { mutableStateOf(Scenes.Blank) }
+fun CommunalContainer(
+ modifier: Modifier = Modifier,
+ viewModel: CommunalViewModel,
+) {
+ val currentScene: SceneKey by
+ viewModel.currentScene
+ .transform<CommunalSceneKey, SceneKey> { value -> value.toTransitionSceneKey() }
+ .collectAsState(TransitionSceneKey.Blank)
// Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
var showSceneTransitionLayout by remember { mutableStateOf(true) }
@@ -70,16 +74,19 @@
SceneTransitionLayout(
modifier = modifier.fillMaxSize(),
currentScene = currentScene,
- onChangeScene = setCurrentScene,
+ onChangeScene = { sceneKey -> viewModel.onSceneChanged(sceneKey.toCommunalSceneKey()) },
transitions = sceneTransitions,
) {
- scene(Scenes.Blank, userActions = mapOf(Swipe.Left to Scenes.Communal)) {
+ scene(
+ TransitionSceneKey.Blank,
+ userActions = mapOf(Swipe.Left to TransitionSceneKey.Communal)
+ ) {
BlankScene { showSceneTransitionLayout = false }
}
scene(
- Scenes.Communal,
- userActions = mapOf(Swipe.Right to Scenes.Blank),
+ TransitionSceneKey.Communal,
+ userActions = mapOf(Swipe.Right to TransitionSceneKey.Blank),
) {
CommunalScene(viewModel, modifier = modifier)
}
@@ -121,3 +128,17 @@
) {
Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
}
+
+// TODO(b/293899074): Remove these conversions once Compose can be used throughout SysUI.
+object TransitionSceneKey {
+ val Blank = CommunalSceneKey.Blank.toTransitionSceneKey()
+ val Communal = CommunalSceneKey.Communal.toTransitionSceneKey()
+}
+
+fun CommunalSceneKey.toTransitionSceneKey(): SceneKey {
+ return SceneKey(name = toString(), identity = this)
+}
+
+fun SceneKey.toCommunalSceneKey(): CommunalSceneKey {
+ return this.identity as CommunalSceneKey
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index b8fb264..87a8c35 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -4,8 +4,14 @@
import android.os.Bundle
import android.util.SizeF
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.material3.Card
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -13,16 +19,12 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.integerResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
-import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.res.R
@Composable
fun CommunalHub(
@@ -34,68 +36,91 @@
Box(
modifier = modifier.fillMaxSize().background(Color.White),
) {
- CommunalGridLayout(
- modifier = Modifier.align(Alignment.CenterStart),
- layoutConfig =
- CommunalGridLayoutConfig(
- gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size),
- gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size),
- gridHeight = dimensionResource(R.dimen.communal_grid_height),
- gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card),
- ),
- communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard),
- )
+ LazyHorizontalGrid(
+ modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
+ rows = GridCells.Fixed(CommunalContentSize.FULL.span),
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+ verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+ ) {
+ if (showTutorial) {
+ items(
+ count = tutorialContentSizes.size,
+ // TODO(b/308148193): a more scalable solution for unique ids.
+ key = { index -> "tutorial_$index" },
+ span = { index -> GridItemSpan(tutorialContentSizes[index].span) },
+ ) { index ->
+ TutorialCard(
+ modifier =
+ Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()),
+ )
+ }
+ } else {
+ items(
+ count = widgetContent.size,
+ key = { index -> widgetContent[index].id },
+ span = { index -> GridItemSpan(widgetContent[index].size.span) },
+ ) { index ->
+ val widget = widgetContent[index]
+ ContentCard(
+ modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()),
+ model = widget,
+ )
+ }
+ }
+ }
}
}
-private val tutorialContent =
+// A placeholder for tutorial content.
+@Composable
+private fun TutorialCard(modifier: Modifier = Modifier) {
+ Card(modifier = modifier, content = {})
+}
+
+@Composable
+private fun ContentCard(
+ model: CommunalContentUiModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ modifier = modifier,
+ factory = {
+ model.view.apply {
+ if (this is AppWidgetHostView) {
+ val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value)
+ updateAppWidgetSize(Bundle.EMPTY, listOf(size))
+ }
+ }
+ },
+ )
+}
+
+private fun CommunalContentSize.dp(): Dp {
+ return when (this) {
+ CommunalContentSize.FULL -> Dimensions.CardHeightFull
+ CommunalContentSize.HALF -> Dimensions.CardHeightHalf
+ CommunalContentSize.THIRD -> Dimensions.CardHeightThird
+ }
+}
+
+// Sizes for the tutorial placeholders.
+private val tutorialContentSizes =
listOf(
- tutorialCard(CommunalGridLayoutCard.Size.FULL),
- tutorialCard(CommunalGridLayoutCard.Size.THIRD),
- tutorialCard(CommunalGridLayoutCard.Size.THIRD),
- tutorialCard(CommunalGridLayoutCard.Size.THIRD),
- tutorialCard(CommunalGridLayoutCard.Size.HALF),
- tutorialCard(CommunalGridLayoutCard.Size.HALF),
- tutorialCard(CommunalGridLayoutCard.Size.HALF),
- tutorialCard(CommunalGridLayoutCard.Size.HALF),
+ CommunalContentSize.FULL,
+ CommunalContentSize.THIRD,
+ CommunalContentSize.THIRD,
+ CommunalContentSize.THIRD,
+ CommunalContentSize.HALF,
+ CommunalContentSize.HALF,
+ CommunalContentSize.HALF,
+ CommunalContentSize.HALF,
)
-private fun tutorialCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard {
- return object : CommunalGridLayoutCard() {
- override val supportedSizes = listOf(size)
-
- @Composable
- override fun Content(modifier: Modifier, size: SizeF) {
- Card(modifier = modifier, content = {})
- }
- }
-}
-
-private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard {
- return object : CommunalGridLayoutCard() {
- override val supportedSizes = listOf(convertToCardSize(model.size))
- override val priority = model.priority
-
- @Composable
- override fun Content(modifier: Modifier, size: SizeF) {
- AndroidView(
- modifier = modifier,
- factory = {
- model.view.apply {
- if (this is AppWidgetHostView) {
- updateAppWidgetSize(Bundle(), listOf(size))
- }
- }
- },
- )
- }
- }
-}
-
-private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size {
- return when (size) {
- CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL
- CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF
- CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD
- }
+private object Dimensions {
+ val CardWidth = 464.dp
+ val CardHeightFull = 630.dp
+ val CardHeightHalf = 307.dp
+ val CardHeightThird = 199.dp
+ val GridHeight = CardHeightFull
+ val Spacing = 16.dp
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
new file mode 100644
index 0000000..82d4239
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.compose.animation.scene
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+
+interface EdgeDetector {
+ /**
+ * Return the [Edge] associated to [position] inside a layout of size [layoutSize], given
+ * [density] and [orientation].
+ */
+ fun edge(
+ layoutSize: IntSize,
+ position: IntOffset,
+ density: Density,
+ orientation: Orientation,
+ ): Edge?
+}
+
+val DefaultEdgeDetector = FixedSizeEdgeDetector(40.dp)
+
+/** An [EdgeDetector] that detects edges assuming a fixed edge size of [size]. */
+class FixedSizeEdgeDetector(val size: Dp) : EdgeDetector {
+ override fun edge(
+ layoutSize: IntSize,
+ position: IntOffset,
+ density: Density,
+ orientation: Orientation,
+ ): Edge? {
+ val axisSize: Int
+ val axisPosition: Int
+ val topOrLeft: Edge
+ val bottomOrRight: Edge
+ when (orientation) {
+ Orientation.Horizontal -> {
+ axisSize = layoutSize.width
+ axisPosition = position.x
+ topOrLeft = Edge.Left
+ bottomOrRight = Edge.Right
+ }
+ Orientation.Vertical -> {
+ axisSize = layoutSize.height
+ axisPosition = position.y
+ topOrLeft = Edge.Top
+ bottomOrRight = Edge.Bottom
+ }
+ }
+
+ val sizePx = with(density) { size.toPx() }
+ return when {
+ axisPosition <= sizePx -> topOrLeft
+ axisPosition >= axisSize - sizePx -> bottomOrRight
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
index d005413..ae7d8f5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/GestureHandler.kt
@@ -2,7 +2,6 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import kotlinx.coroutines.CoroutineScope
interface GestureHandler {
val draggable: DraggableHandler
@@ -10,9 +9,9 @@
}
interface DraggableHandler {
- suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset)
+ fun onDragStarted(startedPosition: Offset, pointersDown: Int = 1)
fun onDelta(pixels: Float)
- suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float)
+ fun onDragStopped(velocity: Float)
}
interface NestedScrollHandler {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
new file mode 100644
index 0000000..97d3fff
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 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.compose.animation.scene
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.horizontalDrag
+import androidx.compose.foundation.gestures.verticalDrag
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerId
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.util.VelocityTracker
+import androidx.compose.ui.input.pointer.util.addPointerInputChange
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.util.fastForEach
+
+/**
+ * Make an element draggable in the given [orientation].
+ *
+ * The main difference with [multiPointerDraggable] and
+ * [androidx.compose.foundation.gestures.draggable] is that [onDragStarted] also receives the number
+ * of pointers that are down when the drag is started. If you don't need this information, you
+ * should use `draggable` instead.
+ *
+ * Note that the current implementation is trivial: we wait for the touch slope on the *first* down
+ * pointer, then we count the number of distinct pointers that are down right before calling
+ * [onDragStarted]. This means that the drag won't start when a first pointer is down (but not
+ * dragged) and a second pointer is down and dragged. This is an implementation detail that might
+ * change in the future.
+ */
+// TODO(b/291055080): Migrate to the Modifier.Node API.
+@Composable
+internal fun Modifier.multiPointerDraggable(
+ orientation: Orientation,
+ enabled: Boolean,
+ startDragImmediately: Boolean,
+ onDragStarted: (startedPosition: Offset, pointersDown: Int) -> Unit,
+ onDragDelta: (Float) -> Unit,
+ onDragStopped: (velocity: Float) -> Unit,
+): Modifier {
+ val onDragStarted by rememberUpdatedState(onDragStarted)
+ val onDragStopped by rememberUpdatedState(onDragStopped)
+ val onDragDelta by rememberUpdatedState(onDragDelta)
+ val startDragImmediately by rememberUpdatedState(startDragImmediately)
+
+ val velocityTracker = remember { VelocityTracker() }
+ val maxFlingVelocity =
+ LocalViewConfiguration.current.maximumFlingVelocity.let { max ->
+ val maxF = max.toFloat()
+ Velocity(maxF, maxF)
+ }
+
+ return this.pointerInput(enabled, orientation, maxFlingVelocity) {
+ if (!enabled) {
+ return@pointerInput
+ }
+
+ val onDragStart: (Offset, Int) -> Unit = { startedPosition, pointersDown ->
+ velocityTracker.resetTracking()
+ onDragStarted(startedPosition, pointersDown)
+ }
+
+ val onDragCancel: () -> Unit = { onDragStopped(/* velocity= */ 0f) }
+
+ val onDragEnd: () -> Unit = {
+ val velocity = velocityTracker.calculateVelocity(maxFlingVelocity)
+ onDragStopped(
+ when (orientation) {
+ Orientation.Horizontal -> velocity.x
+ Orientation.Vertical -> velocity.y
+ }
+ )
+ }
+
+ val onDrag: (change: PointerInputChange, dragAmount: Float) -> Unit = { change, amount ->
+ velocityTracker.addPointerInputChange(change)
+ onDragDelta(amount)
+ }
+
+ detectDragGestures(
+ orientation = orientation,
+ startDragImmediately = { startDragImmediately },
+ onDragStart = onDragStart,
+ onDragEnd = onDragEnd,
+ onDragCancel = onDragCancel,
+ onDrag = onDrag,
+ )
+ }
+}
+
+/**
+ * Detect drag gestures in the given [orientation].
+ *
+ * This function is a mix of [androidx.compose.foundation.gestures.awaitDownAndSlop] and
+ * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for:
+ * 1) starting the gesture immediately without requiring a drag >= touch slope;
+ * 2) passing the number of pointers down to [onDragStart].
+ */
+private suspend fun PointerInputScope.detectDragGestures(
+ orientation: Orientation,
+ startDragImmediately: () -> Boolean,
+ onDragStart: (startedPosition: Offset, pointersDown: Int) -> Unit,
+ onDragEnd: () -> Unit,
+ onDragCancel: () -> Unit,
+ onDrag: (change: PointerInputChange, dragAmount: Float) -> Unit,
+) {
+ awaitEachGesture {
+ val initialDown = awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial)
+ var overSlop = 0f
+ val drag =
+ if (startDragImmediately()) {
+ initialDown.consume()
+ initialDown
+ } else {
+ val down = awaitFirstDown(requireUnconsumed = false)
+ val onSlopReached = { change: PointerInputChange, over: Float ->
+ change.consume()
+ overSlop = over
+ }
+
+ // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once
+ // it is public.
+ when (orientation) {
+ Orientation.Horizontal ->
+ awaitHorizontalTouchSlopOrCancellation(down.id, onSlopReached)
+ Orientation.Vertical ->
+ awaitVerticalTouchSlopOrCancellation(down.id, onSlopReached)
+ }
+ }
+
+ if (drag != null) {
+ // Count the number of pressed pointers.
+ val pressed = mutableSetOf<PointerId>()
+ currentEvent.changes.fastForEach { change ->
+ if (change.pressed) {
+ pressed.add(change.id)
+ }
+ }
+
+ onDragStart(drag.position, pressed.size)
+ onDrag(drag, overSlop)
+
+ val successful =
+ when (orientation) {
+ Orientation.Horizontal ->
+ horizontalDrag(drag.id) {
+ onDrag(it, it.positionChange().x)
+ it.consume()
+ }
+ Orientation.Vertical ->
+ verticalDrag(drag.id) {
+ onDrag(it, it.positionChange().y)
+ it.consume()
+ }
+ }
+
+ if (successful) {
+ onDragEnd()
+ } else {
+ onDragCancel()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 9c799b28..3fd6828 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -16,7 +16,6 @@
package com.android.compose.animation.scene
-import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
@@ -101,19 +100,3 @@
MovableElement(layoutImpl, scene, key, modifier, content)
}
}
-
-/** The destination scene when swiping up or left from [upOrLeft]. */
-internal fun Scene.upOrLeft(orientation: Orientation): SceneKey? {
- return when (orientation) {
- Orientation.Vertical -> userActions[Swipe.Up]
- Orientation.Horizontal -> userActions[Swipe.Left]
- }
-}
-
-/** The destination scene when swiping down or right from [downOrRight]. */
-internal fun Scene.downOrRight(orientation: Orientation): SceneKey? {
- return when (orientation) {
- Orientation.Vertical -> userActions[Swipe.Down]
- Orientation.Horizontal -> userActions[Swipe.Right]
- }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 74e66d2..1f38e70 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
@@ -37,6 +38,7 @@
* instance by triggering back navigation or by swiping to a new scene.
* @param transitions the definition of the transitions used to animate a change of scene.
* @param state the observable state of this layout.
+ * @param edgeDetector the edge detector used to detect which edge a swipe is started from, if any.
* @param scenes the configuration of the different scenes of this layout.
*/
@Composable
@@ -46,6 +48,7 @@
transitions: SceneTransitions,
modifier: Modifier = Modifier,
state: SceneTransitionLayoutState = remember { SceneTransitionLayoutState(currentScene) },
+ edgeDetector: EdgeDetector = DefaultEdgeDetector,
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
val density = LocalDensity.current
@@ -56,15 +59,17 @@
transitions,
state,
density,
+ edgeDetector,
)
}
layoutImpl.onChangeScene = onChangeScene
layoutImpl.transitions = transitions
layoutImpl.density = density
+ layoutImpl.edgeDetector = edgeDetector
+
layoutImpl.setScenes(scenes)
layoutImpl.setCurrentScene(currentScene)
-
layoutImpl.Content(modifier)
}
@@ -191,9 +196,9 @@
}
}
-enum class SwipeDirection {
- Up,
- Down,
- Left,
- Right,
+enum class SwipeDirection(val orientation: Orientation) {
+ Up(Orientation.Vertical),
+ Down(Orientation.Vertical),
+ Left(Orientation.Horizontal),
+ Right(Orientation.Horizontal),
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index a40b299..fd62659 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -37,7 +37,7 @@
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
-import com.android.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEach
import kotlinx.coroutines.channels.Channel
@VisibleForTesting
@@ -47,6 +47,7 @@
transitions: SceneTransitions,
internal val state: SceneTransitionLayoutState,
density: Density,
+ edgeDetector: EdgeDetector,
) {
internal val scenes = SnapshotStateMap<SceneKey, Scene>()
internal val elements = SnapshotStateMap<ElementKey, Element>()
@@ -57,6 +58,7 @@
internal var onChangeScene by mutableStateOf(onChangeScene)
internal var transitions by mutableStateOf(transitions)
internal var density: Density by mutableStateOf(density)
+ internal var edgeDetector by mutableStateOf(edgeDetector)
/**
* The size of this layout. Note that this could be [IntSize.Zero] if this layour does not have
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 75dcb2e..b163a2a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -21,6 +21,8 @@
import androidx.compose.animation.core.snap
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastMap
import com.android.compose.animation.scene.transformation.AnchoredSize
import com.android.compose.animation.scene.transformation.AnchoredTranslate
import com.android.compose.animation.scene.transformation.EdgeTranslate
@@ -32,8 +34,6 @@
import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.Translate
-import com.android.compose.ui.util.fastForEach
-import com.android.compose.ui.util.fastMap
/** The transitions configuration of a [SceneTransitionLayout]. */
class SceneTransitions(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 2dc53ab..8b79c28 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -22,8 +22,6 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.draggable
-import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
@@ -37,6 +35,7 @@
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
@@ -55,7 +54,7 @@
/** Whether swipe should be enabled in the given [orientation]. */
fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean =
- upOrLeft(orientation) != null || downOrRight(orientation) != null
+ userActions.keys.any { it is Swipe && it.direction.orientation == orientation }
val currentScene = gestureHandler.currentScene
val canSwipe = currentScene.shouldEnableSwipes(orientation)
@@ -68,8 +67,7 @@
)
return nestedScroll(connection = gestureHandler.nestedScroll.connection)
- .draggable(
- state = rememberDraggableState(onDelta = gestureHandler.draggable::onDelta),
+ .multiPointerDraggable(
orientation = orientation,
enabled = gestureHandler.isDrivingTransition || canSwipe,
// Immediately start the drag if this our [transition] is currently animating to a scene
@@ -80,6 +78,7 @@
gestureHandler.isAnimatingOffset &&
!canOppositeSwipe,
onDragStarted = gestureHandler.draggable::onDragStarted,
+ onDragDelta = gestureHandler.draggable::onDelta,
onDragStopped = gestureHandler.draggable::onDragStopped,
)
}
@@ -159,7 +158,7 @@
internal var gestureWithPriority: Any? = null
- internal fun onDragStarted() {
+ internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?) {
if (isDrivingTransition) {
// This [transition] was already driving the animation: simply take over it.
// Stop animating and start from where the current offset.
@@ -199,6 +198,48 @@
Orientation.Vertical -> layoutImpl.size.height
}.toFloat()
+ val fromEdge =
+ startedPosition?.let { position ->
+ layoutImpl.edgeDetector.edge(
+ layoutImpl.size,
+ position.round(),
+ layoutImpl.density,
+ orientation,
+ )
+ }
+
+ swipeTransition.actionUpOrLeft =
+ Swipe(
+ direction =
+ when (orientation) {
+ Orientation.Horizontal -> SwipeDirection.Left
+ Orientation.Vertical -> SwipeDirection.Up
+ },
+ pointerCount = pointersDown,
+ fromEdge = fromEdge,
+ )
+
+ swipeTransition.actionDownOrRight =
+ Swipe(
+ direction =
+ when (orientation) {
+ Orientation.Horizontal -> SwipeDirection.Right
+ Orientation.Vertical -> SwipeDirection.Down
+ },
+ pointerCount = pointersDown,
+ fromEdge = fromEdge,
+ )
+
+ if (fromEdge == null) {
+ swipeTransition.actionUpOrLeftNoEdge = null
+ swipeTransition.actionDownOrRightNoEdge = null
+ } else {
+ swipeTransition.actionUpOrLeftNoEdge =
+ (swipeTransition.actionUpOrLeft as Swipe).copy(fromEdge = null)
+ swipeTransition.actionDownOrRightNoEdge =
+ (swipeTransition.actionDownOrRight as Swipe).copy(fromEdge = null)
+ }
+
if (swipeTransition.absoluteDistance > 0f) {
transitionState = swipeTransition
}
@@ -246,11 +287,11 @@
// to the next screen or go back to the previous one.
val offset = swipeTransition.dragOffset
val absoluteDistance = swipeTransition.absoluteDistance
- if (offset <= -absoluteDistance && fromScene.upOrLeft(orientation) == toScene.key) {
+ if (offset <= -absoluteDistance && swipeTransition.upOrLeft(fromScene) == toScene.key) {
swipeTransition.dragOffset += absoluteDistance
swipeTransition._fromScene = toScene
} else if (
- offset >= absoluteDistance && fromScene.downOrRight(orientation) == toScene.key
+ offset >= absoluteDistance && swipeTransition.downOrRight(fromScene) == toScene.key
) {
swipeTransition.dragOffset -= absoluteDistance
swipeTransition._fromScene = toScene
@@ -266,27 +307,21 @@
)
private fun Scene.findTargetSceneAndDistance(directionOffset: Float): TargetScene {
- val maxDistance =
- when (orientation) {
- Orientation.Horizontal -> layoutImpl.size.width
- Orientation.Vertical -> layoutImpl.size.height
- }.toFloat()
-
- val upOrLeft = upOrLeft(orientation)
- val downOrRight = downOrRight(orientation)
+ val upOrLeft = swipeTransition.upOrLeft(this)
+ val downOrRight = swipeTransition.downOrRight(this)
// Compute the target scene depending on the current offset.
return when {
directionOffset < 0f && upOrLeft != null -> {
TargetScene(
sceneKey = upOrLeft,
- distance = -maxDistance,
+ distance = -swipeTransition.absoluteDistance,
)
}
directionOffset > 0f && downOrRight != null -> {
TargetScene(
sceneKey = downOrRight,
- distance = maxDistance,
+ distance = swipeTransition.absoluteDistance,
)
}
else -> {
@@ -516,6 +551,22 @@
var _distance by mutableFloatStateOf(0f)
val distance: Float
get() = _distance
+
+ /** The [UserAction]s associated to this swipe. */
+ var actionUpOrLeft: UserAction = Back
+ var actionDownOrRight: UserAction = Back
+ var actionUpOrLeftNoEdge: UserAction? = null
+ var actionDownOrRightNoEdge: UserAction? = null
+
+ fun upOrLeft(scene: Scene): SceneKey? {
+ return scene.userActions[actionUpOrLeft]
+ ?: actionUpOrLeftNoEdge?.let { scene.userActions[it] }
+ }
+
+ fun downOrRight(scene: Scene): SceneKey? {
+ return scene.userActions[actionDownOrRight]
+ ?: actionDownOrRightNoEdge?.let { scene.userActions[it] }
+ }
}
companion object {
@@ -526,9 +577,9 @@
private class SceneDraggableHandler(
private val gestureHandler: SceneGestureHandler,
) : DraggableHandler {
- override suspend fun onDragStarted(coroutineScope: CoroutineScope, startedPosition: Offset) {
+ override fun onDragStarted(startedPosition: Offset, pointersDown: Int) {
gestureHandler.gestureWithPriority = this
- gestureHandler.onDragStarted()
+ gestureHandler.onDragStarted(pointersDown, startedPosition)
}
override fun onDelta(pixels: Float) {
@@ -537,7 +588,7 @@
}
}
- override suspend fun onDragStopped(coroutineScope: CoroutineScope, velocity: Float) {
+ override fun onDragStopped(velocity: Float) {
if (gestureHandler.gestureWithPriority == this) {
gestureHandler.gestureWithPriority = null
gestureHandler.onDragStopped(velocity = velocity, canChangeScene = true)
@@ -580,11 +631,31 @@
// moving on to the next scene.
var gestureStartedOnNestedChild = false
+ val actionUpOrLeft =
+ Swipe(
+ direction =
+ when (gestureHandler.orientation) {
+ Orientation.Horizontal -> SwipeDirection.Left
+ Orientation.Vertical -> SwipeDirection.Up
+ },
+ pointerCount = 1,
+ )
+
+ val actionDownOrRight =
+ Swipe(
+ direction =
+ when (gestureHandler.orientation) {
+ Orientation.Horizontal -> SwipeDirection.Right
+ Orientation.Vertical -> SwipeDirection.Down
+ },
+ pointerCount = 1,
+ )
+
fun findNextScene(amount: Float): SceneKey? {
val fromScene = gestureHandler.currentScene
return when {
- amount < 0f -> fromScene.upOrLeft(gestureHandler.orientation)
- amount > 0f -> fromScene.downOrRight(gestureHandler.orientation)
+ amount < 0f -> fromScene.userActions[actionUpOrLeft]
+ amount > 0f -> fromScene.userActions[actionDownOrRight]
else -> null
}
}
@@ -625,7 +696,7 @@
onStart = {
gestureHandler.gestureWithPriority = this
priorityScene = nextScene
- gestureHandler.onDragStarted()
+ gestureHandler.onDragStarted(pointersDown = 1, startedPosition = null)
},
onScroll = { offsetAvailable ->
if (gestureHandler.gestureWithPriority != this) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/ListUtils.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/ListUtils.kt
deleted file mode 100644
index 741f00d..0000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/ListUtils.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2023 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.compose.ui.util
-
-import kotlin.contracts.ExperimentalContracts
-import kotlin.contracts.contract
-
-/**
- * Iterates through a [List] using the index and calls [action] for each item. This does not
- * allocate an iterator like [Iterable.forEach].
- *
- * **Do not use for collections that come from public APIs**, since they may not support random
- * access in an efficient way, and this method may actually be a lot slower. Only use for
- * collections that are created by code we control and are known to support random access.
- */
-@Suppress("BanInlineOptIn")
-@OptIn(ExperimentalContracts::class)
-internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
- contract { callsInPlace(action) }
- for (index in indices) {
- val item = get(index)
- action(item)
- }
-}
-
-/**
- * Returns a list containing the results of applying the given [transform] function to each element
- * in the original collection.
- *
- * **Do not use for collections that come from public APIs**, since they may not support random
- * access in an efficient way, and this method may actually be a lot slower. Only use for
- * collections that are created by code we control and are known to support random access.
- */
-@Suppress("BanInlineOptIn")
-@OptIn(ExperimentalContracts::class)
-internal inline fun <T, R> List<T>.fastMap(transform: (T) -> R): List<R> {
- contract { callsInPlace(transform) }
- val target = ArrayList<R>(size)
- fastForEach { target += transform(it) }
- return target
-}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
new file mode 100644
index 0000000..a68282a
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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.compose.animation.scene
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class FixedSizeEdgeDetectorTest {
+ private val detector = FixedSizeEdgeDetector(30.dp)
+ private val layoutSize = IntSize(100, 100)
+ private val density = Density(1f)
+
+ @Test
+ fun horizontalEdges() {
+ fun horizontalEdge(position: Int): Edge? =
+ detector.edge(
+ layoutSize,
+ position = IntOffset(position, 0),
+ density,
+ Orientation.Horizontal,
+ )
+
+ assertThat(horizontalEdge(0)).isEqualTo(Edge.Left)
+ assertThat(horizontalEdge(30)).isEqualTo(Edge.Left)
+ assertThat(horizontalEdge(31)).isEqualTo(null)
+ assertThat(horizontalEdge(69)).isEqualTo(null)
+ assertThat(horizontalEdge(70)).isEqualTo(Edge.Right)
+ assertThat(horizontalEdge(100)).isEqualTo(Edge.Right)
+ }
+
+ @Test
+ fun verticalEdges() {
+ fun verticalEdge(position: Int): Edge? =
+ detector.edge(
+ layoutSize,
+ position = IntOffset(0, position),
+ density,
+ Orientation.Vertical,
+ )
+
+ assertThat(verticalEdge(0)).isEqualTo(Edge.Top)
+ assertThat(verticalEdge(30)).isEqualTo(Edge.Top)
+ assertThat(verticalEdge(31)).isEqualTo(null)
+ assertThat(verticalEdge(69)).isEqualTo(null)
+ assertThat(verticalEdge(70)).isEqualTo(Edge.Bottom)
+ assertThat(verticalEdge(100)).isEqualTo(Edge.Bottom)
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index 6791a85..1eb3392 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -55,7 +55,8 @@
builder = scenesBuilder,
transitions = EmptyTestTransitions,
state = layoutState,
- density = Density(1f)
+ density = Density(1f),
+ edgeDetector = DefaultEdgeDetector,
)
.also { it.size = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt()) },
orientation = Orientation.Vertical,
@@ -104,13 +105,13 @@
@Test
fun onDragStarted_shouldStartATransition() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
}
@Test
fun afterSceneTransitionIsStarted_interceptDragEvents() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
val transition = transitionState as Transition
@@ -123,14 +124,13 @@
@Test
fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDragStopped(
- coroutineScope = coroutineScope,
velocity = velocityThreshold - 0.01f,
)
assertScene(currentScene = SceneA, isIdle = false)
@@ -142,14 +142,13 @@
@Test
fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDragStopped(
- coroutineScope = coroutineScope,
velocity = velocityThreshold,
)
assertScene(currentScene = SceneC, isIdle = false)
@@ -161,23 +160,22 @@
@Test
fun onDragStoppedAfterStarted_returnImmediatelyToIdle() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
- draggable.onDragStopped(coroutineScope = coroutineScope, velocity = 0f)
+ draggable.onDragStopped(velocity = 0f)
assertScene(currentScene = SceneA, isIdle = true)
}
@Test
fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest {
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDelta(pixels = deltaInPixels10)
assertScene(currentScene = SceneA, isIdle = false)
draggable.onDragStopped(
- coroutineScope = coroutineScope,
velocity = velocityThreshold,
)
@@ -191,7 +189,7 @@
assertScene(currentScene = SceneC, isIdle = false)
// Start a new gesture while the offset is animating
- draggable.onDragStarted(coroutineScope = coroutineScope, startedPosition = Offset.Zero)
+ draggable.onDragStarted(startedPosition = Offset.Zero)
assertThat(sceneGestureHandler.isAnimatingOffset).isFalse()
}
@@ -320,7 +318,7 @@
}
@Test
fun beforeDraggableStart_stop_shouldBeIgnored() = runGestureTest {
- draggable.onDragStopped(coroutineScope, velocityThreshold)
+ draggable.onDragStopped(velocityThreshold)
assertScene(currentScene = SceneA, isIdle = true)
}
@@ -332,7 +330,7 @@
@Test
fun startNestedScrollWhileDragging() = runGestureTest {
- draggable.onDragStarted(coroutineScope, Offset.Zero)
+ draggable.onDragStarted(Offset.Zero)
assertScene(currentScene = SceneA, isIdle = false)
val transition = transitionState as Transition
@@ -344,7 +342,7 @@
assertThat(transition.progress).isEqualTo(0.2f)
// this should be ignored, we are scrolling now!
- draggable.onDragStopped(coroutineScope, velocityThreshold)
+ draggable.onDragStopped(velocityThreshold)
assertScene(currentScene = SceneA, isIdle = false)
nestedScrollEvents(available = offsetY10)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index df3b72a..4a6066f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -48,6 +48,14 @@
/** The middle of the layout, in pixels. */
private val Density.middle: Offset
get() = Offset((LayoutWidth / 2).toPx(), (LayoutHeight / 2).toPx())
+
+ /** The middle-top of the layout, in pixels. */
+ private val Density.middleTop: Offset
+ get() = Offset((LayoutWidth / 2).toPx(), 0f)
+
+ /** The middle-left of the layout, in pixels. */
+ private val Density.middleLeft: Offset
+ get() = Offset(0f, (LayoutHeight / 2).toPx())
}
private var currentScene by mutableStateOf(TestScenes.SceneA)
@@ -83,7 +91,13 @@
}
scene(
TestScenes.SceneC,
- userActions = mapOf(Swipe.Down to TestScenes.SceneA),
+ userActions =
+ mapOf(
+ Swipe.Down to TestScenes.SceneA,
+ Swipe(SwipeDirection.Down, pointerCount = 2) to TestScenes.SceneB,
+ Swipe(SwipeDirection.Right, fromEdge = Edge.Left) to TestScenes.SceneB,
+ Swipe(SwipeDirection.Down, fromEdge = Edge.Top) to TestScenes.SceneB,
+ ),
) {
Box(Modifier.fillMaxSize())
}
@@ -242,4 +256,100 @@
assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
}
+
+ @Test
+ fun multiPointerSwipe() {
+ // Start at scene C.
+ currentScene = TestScenes.SceneC
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ TestContent()
+ }
+
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+
+ // Swipe down with two fingers.
+ rule.onRoot().performTouchInput {
+ repeat(2) { i -> down(pointerId = i, middle) }
+ repeat(2) { i ->
+ moveBy(pointerId = i, Offset(0f, touchSlop + 10.dp.toPx()), delayMillis = 1_000)
+ }
+ }
+
+ // We are transitioning to B because we used 2 fingers.
+ val transition = layoutState.transitionState
+ assertThat(transition).isInstanceOf(TransitionState.Transition::class.java)
+ assertThat((transition as TransitionState.Transition).fromScene)
+ .isEqualTo(TestScenes.SceneC)
+ assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
+
+ // Release the fingers and wait for the animation to end. We are back to C because we only
+ // swiped 10dp.
+ rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } }
+ rule.waitForIdle()
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+ }
+
+ @Test
+ fun defaultEdgeSwipe() {
+ // Start at scene C.
+ currentScene = TestScenes.SceneC
+
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ TestContent()
+ }
+
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+
+ // Swipe down from the top edge.
+ rule.onRoot().performTouchInput {
+ down(middleTop)
+ moveBy(Offset(0f, touchSlop + 10.dp.toPx()), delayMillis = 1_000)
+ }
+
+ // We are transitioning to B (and not A) because we started from the top edge.
+ var transition = layoutState.transitionState
+ assertThat(transition).isInstanceOf(TransitionState.Transition::class.java)
+ assertThat((transition as TransitionState.Transition).fromScene)
+ .isEqualTo(TestScenes.SceneC)
+ assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
+
+ // Release the fingers and wait for the animation to end. We are back to C because we only
+ // swiped 10dp.
+ rule.onRoot().performTouchInput { up() }
+ rule.waitForIdle()
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+
+ // Swipe right from the left edge.
+ rule.onRoot().performTouchInput {
+ down(middleLeft)
+ moveBy(Offset(touchSlop + 10.dp.toPx(), 0f), delayMillis = 1_000)
+ }
+
+ // We are transitioning to B (and not A) because we started from the left edge.
+ transition = layoutState.transitionState
+ assertThat(transition).isInstanceOf(TransitionState.Transition::class.java)
+ assertThat((transition as TransitionState.Transition).fromScene)
+ .isEqualTo(TestScenes.SceneC)
+ assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
+
+ // Release the fingers and wait for the animation to end. We are back to C because we only
+ // swiped 10dp.
+ rule.onRoot().performTouchInput { up() }
+ rule.waitForIdle()
+ assertThat(layoutState.transitionState).isInstanceOf(TransitionState.Idle::class.java)
+ assertThat(layoutState.transitionState.currentScene).isEqualTo(TestScenes.SceneC)
+ }
}
diff --git a/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml b/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml
index 183f0e5..9d74677 100644
--- a/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml
+++ b/packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml
@@ -15,18 +15,18 @@
~
-->
-<LinearLayout android:layout_height="match_parent"
+<RelativeLayout
android:layout_width="match_parent"
- android:orientation="vertical"
- android:layoutDirection="ltr"
- android:gravity="center"
+ android:layout_height="match_parent"
+ android:gravity="left|top"
+ android:background="@android:color/transparent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ProgressBar
android:id="@+id/side_fps_progress_bar"
- android:layout_width="55dp"
- android:layout_height="10dp"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/sfps_progress_bar_thickness"
android:indeterminateOnly="false"
android:min="0"
android:max="100"
android:progressDrawable="@drawable/progress_bar" />
-</LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index d1067a9..0628c3e 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -157,4 +157,11 @@
<dimen name="weather_clock_smartspace_translateX">0dp</dimen>
<dimen name="weather_clock_smartspace_translateY">0dp</dimen>
+ <!-- Additional length to add to the SFPS sensor length we get from framework so that the length
+ of the progress bar matches the length of the power button -->
+ <dimen name="sfps_progress_bar_length_extra_padding">12dp</dimen>
+ <!-- Thickness of the progress bar we show for the SFPS based authentication. -->
+ <dimen name="sfps_progress_bar_thickness">6dp</dimen>
+ <!-- Padding from the edge of the screen for the progress bar -->
+ <dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
deleted file mode 100644
index 56587b9..0000000
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/dnd_icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginTop="6dp"
- android:layout_marginBottom="6dp">
-
- <ImageView
- android:layout_width="14dp"
- android:layout_height="14dp"
- android:layout_gravity="center"
- android:src="@*android:drawable/ic_qs_dnd"
- android:tint="?android:attr/textColorTertiary"/>
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4c520444..12bff4a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1045,8 +1045,8 @@
<!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]-->
<string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string>
- <!-- Indicator shown to start the communal tutorial. [CHAR LIMIT=100] -->
- <string name="communal_tutorial_indicator_text">Click on the arrow button to start the communal tutorial</string>
+ <!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
+ <string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 9c368eb..5b59e7d 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -53,6 +53,7 @@
"SystemUIPluginLib",
"SystemUIUnfoldLib",
"SystemUISharedLib-Keyguard",
+ "tracinglib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
"androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
index 7719246..637ce5f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
@@ -19,7 +19,7 @@
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.forEach
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import java.lang.ref.WeakReference
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3605ac2..f06e333 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -216,7 +216,6 @@
@Nullable public Drawable icon;
@Nullable public ThumbnailData thumbnail;
@ViewDebug.ExportedProperty(category="recents")
- @Deprecated
public String title;
@ViewDebug.ExportedProperty(category="recents")
public String titleDescription;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 631423e..10393cf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -137,14 +137,12 @@
}
/**
- * @return a {@link ThumbnailData} with {@link TaskSnapshot} for the given {@param taskId}.
- * The snapshot will be triggered if no cached {@link TaskSnapshot} exists.
+ * @return the task snapshot for the given {@param taskId}.
*/
public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) {
TaskSnapshot snapshot = null;
try {
- snapshot = getService().getTaskSnapshot(taskId, isLowResolution,
- true /* takeSnapshotIfNeeded */);
+ snapshot = getService().getTaskSnapshot(taskId, isLowResolution);
} catch (RemoteException e) {
Log.w(TAG, "Failed to retrieve task snapshot", e);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt
deleted file mode 100644
index 7d1b65a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 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.tracing
-
-import com.android.systemui.tracing.TraceUtils.Companion.instant
-import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CopyableThreadContextElement
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/**
- * Used for safely persisting [TraceData] state when coroutines are suspended and resumed.
- *
- * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private`
- * because [traceCoroutine] is a Public-API inline function.
- *
- * @see traceCoroutine
- */
-@OptIn(DelicateCoroutinesApi::class)
-@ExperimentalCoroutinesApi
-class TraceContextElement(private val traceData: TraceData = TraceData()) :
- CopyableThreadContextElement<TraceData?> {
-
- companion object Key : CoroutineContext.Key<TraceContextElement>
-
- override val key: CoroutineContext.Key<TraceContextElement> = Key
-
- @OptIn(ExperimentalStdlibApi::class)
- override fun updateThreadContext(context: CoroutineContext): TraceData? {
- val oldState = threadLocalTrace.get()
- oldState?.endAllOnThread()
- threadLocalTrace.set(traceData)
- instant("resuming ${context[CoroutineDispatcher]}")
- traceData.beginAllOnThread()
- return oldState
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- override fun restoreThreadContext(context: CoroutineContext, oldState: TraceData?) {
- instant("suspending ${context[CoroutineDispatcher]}")
- traceData.endAllOnThread()
- threadLocalTrace.set(oldState)
- oldState?.beginAllOnThread()
- }
-
- override fun copyForChild(): CopyableThreadContextElement<TraceData?> {
- return TraceContextElement(traceData.copy())
- }
-
- override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
- return TraceContextElement(traceData.copy())
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt
deleted file mode 100644
index b68d38c..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2023 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.tracing
-
-import android.os.Build
-import android.util.Log
-import com.android.systemui.tracing.TraceUtils.Companion.beginSlice
-import com.android.systemui.tracing.TraceUtils.Companion.endSlice
-import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
-import kotlin.random.Random
-
-/**
- * Used for giving each thread a unique [TraceData] for thread-local storage. `null` by default.
- * [threadLocalTrace] can only be used when it is paired with a [TraceContextElement].
- *
- * This ThreadLocal will be `null` if either 1) we aren't in a coroutine, or 2) the coroutine we are
- * in does not have a [TraceContextElement].
- *
- * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private`
- * because [traceCoroutine] is a Public-API inline function.
- *
- * @see traceCoroutine
- */
-val threadLocalTrace = ThreadLocal<TraceData?>()
-
-/**
- * Used for storing trace sections so that they can be added and removed from the currently running
- * thread when the coroutine is suspended and resumed.
- *
- * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private`
- * because [traceCoroutine] is a Public-API inline function.
- *
- * @see traceCoroutine
- */
-class TraceData {
- private var slices = mutableListOf<TraceSection>()
-
- /** Adds current trace slices back to the current thread. Called when coroutine is resumed. */
- fun beginAllOnThread() {
- slices.forEach { beginSlice(it.name) }
- }
-
- /**
- * Removes all current trace slices from the current thread. Called when coroutine is suspended.
- */
- fun endAllOnThread() {
- for (i in 0..slices.size) {
- endSlice()
- }
- }
-
- /**
- * Creates a new trace section with a unique ID and adds it to the current trace data. The slice
- * will also be added to the current thread immediately. This slice will not propagate to parent
- * coroutines, or to child coroutines that have already started. The unique ID is used to verify
- * that the [endSpan] is corresponds to a [beginSpan].
- */
- fun beginSpan(name: String): Int {
- val newSlice = TraceSection(name, Random.nextInt(FIRST_VALID_SPAN, Int.MAX_VALUE))
- slices.add(newSlice)
- beginSlice(name)
- return newSlice.id
- }
-
- /**
- * Used by [TraceContextElement] when launching a child coroutine so that the child coroutine's
- * state is isolated from the parent.
- */
- fun copy(): TraceData {
- return TraceData().also { it.slices.addAll(slices) }
- }
-
- /**
- * Ends the trace section and validates it corresponds with an earlier call to [beginSpan]. The
- * trace slice will immediately be removed from the current thread. This information will not
- * propagate to parent coroutines, or to child coroutines that have already started.
- */
- fun endSpan(id: Int) {
- val v = slices.removeLast()
- if (v.id != id) {
- if (STRICT_MODE) {
- throw IllegalArgumentException(errorMsg)
- } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, errorMsg)
- }
- }
- endSlice()
- }
-
- companion object {
- private const val TAG = "TraceData"
- const val INVALID_SPAN = -1
- const val FIRST_VALID_SPAN = 1
-
- /**
- * If true, throw an exception instead of printing a warning when trace sections beginnings
- * and ends are mismatched.
- */
- private val STRICT_MODE = Build.IS_ENG
-
- private const val errorMsg =
- "Mismatched trace section. This likely means you are accessing the trace local " +
- "storage (threadLocalTrace) without a corresponding CopyableThreadContextElement." +
- " This could happen if you are using a global dispatcher like Dispatchers.IO." +
- " To fix this, use one of the coroutine contexts provided by the dagger scope " +
- "(e.g. \"@Main CoroutineContext\")."
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt
deleted file mode 100644
index 469d9a2..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 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.tracing
-
-import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
-
-/**
- * Represents a section of code executing in a coroutine. This can be split up into multiple slices
- * on different threads as the coroutine is suspended and resumed.
- *
- * This is internal machinery for [traceCoroutine]. It cannot be made `internal` or `private`
- * because [traceCoroutine] is a Public-API inline function.
- *
- * @param name the name of the slice to appear on the current thread's track.
- * @param id used for matching the beginning and end of trace sections and validating correctness
- * @see traceCoroutine
- */
-data class TraceSection(
- val name: String,
- val id: Int,
-)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt
deleted file mode 100644
index 3e235f5..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2023 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.tracing
-
-import android.os.Trace
-
-/**
- * Utility class used to log state changes easily in a track with a custom name.
- *
- * Example of usage:
- * ```kotlin
- * class MyClass {
- * val screenStateLogger = TraceStateLogger("Screen state")
- *
- * fun onTurnedOn() { screenStateLogger.log("on") }
- * fun onTurnedOff() { screenStateLogger.log("off") }
- * }
- * ```
- *
- * This creates a new slice in a perfetto trace only if the state is different than the previous
- * one.
- */
-class TraceStateLogger(
- private val trackName: String,
- private val logOnlyIfDifferent: Boolean = true,
- private val instantEvent: Boolean = true
-) {
-
- private var previousValue: String? = null
-
- /** If needed, logs the value to a track with name [trackName]. */
- fun log(newValue: String) {
- if (instantEvent) {
- Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, newValue)
- }
- if (logOnlyIfDifferent && previousValue == newValue) return
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, 0)
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, newValue, 0)
- previousValue = newValue
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt
deleted file mode 100644
index 12a20ae..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2023 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.tracing
-
-import android.os.Trace
-import android.os.TraceNameSupplier
-import android.util.Log
-import com.android.systemui.tracing.TraceData.Companion.FIRST_VALID_SPAN
-import com.android.systemui.tracing.TraceData.Companion.INVALID_SPAN
-import java.util.concurrent.atomic.AtomicInteger
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.coroutineContext
-import kotlin.random.Random
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
-
-/**
- * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
- * after the passed block.
- */
-inline fun <T> traceSection(tag: String, block: () -> T): T =
- if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
- Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
- try {
- block()
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_APP)
- }
- } else {
- block()
- }
-
-class TraceUtils {
- companion object {
- const val TAG = "TraceUtils"
- private const val DEBUG_COROUTINE_TRACING = false
- const val DEFAULT_TRACK_NAME = "AsyncTraces"
-
- inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
- return Runnable { traceSection(tag) { block() } }
- }
-
- /**
- * Helper function for creating a Runnable object that implements TraceNameSupplier.
- *
- * This is useful for posting Runnables to Handlers with meaningful names.
- */
- inline fun namedRunnable(tag: String, crossinline block: () -> Unit): Runnable {
- return object : Runnable, TraceNameSupplier {
- override fun getTraceName(): String = tag
- override fun run() = block()
- }
- }
-
- /**
- * Cookie used for async traces. Shouldn't be public, but to use it inside inline methods
- * there is no other way around.
- */
- val lastCookie = AtomicInteger(0)
-
- /**
- * Creates an async slice in a track called "AsyncTraces".
- *
- * This can be used to trace coroutine code. Note that all usages of this method will appear
- * under a single track.
- */
- inline fun <T> traceAsync(method: String, block: () -> T): T =
- traceAsync(DEFAULT_TRACK_NAME, method, block)
-
- /**
- * Creates an async slice in a track with [trackName] while [block] runs.
- *
- * This can be used to trace coroutine code. [method] will be the name of the slice,
- * [trackName] of the track. The track is one of the rows visible in a perfetto trace inside
- * SystemUI process.
- */
- inline fun <T> traceAsync(trackName: String, method: String, block: () -> T): T {
- val cookie = lastCookie.incrementAndGet()
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, method, cookie)
- try {
- return block()
- } finally {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, cookie)
- }
- }
-
- /**
- * Convenience function for calling [CoroutineScope.launch] with [traceCoroutine] enable
- * tracing.
- *
- * @see traceCoroutine
- */
- inline fun CoroutineScope.launch(
- crossinline spanName: () -> String,
- context: CoroutineContext = EmptyCoroutineContext,
- // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> Unit
- ): Job = launch(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [CoroutineScope.launch] with [traceCoroutine] enable
- * tracing.
- *
- * @see traceCoroutine
- */
- inline fun CoroutineScope.launch(
- spanName: String,
- context: CoroutineContext = EmptyCoroutineContext,
- // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> Unit
- ): Job = launch(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [CoroutineScope.async] with [traceCoroutine] enable
- * tracing
- *
- * @see traceCoroutine
- */
- inline fun <T> CoroutineScope.async(
- crossinline spanName: () -> String,
- context: CoroutineContext = EmptyCoroutineContext,
- // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> T
- ): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [CoroutineScope.async] with [traceCoroutine] enable
- * tracing.
- *
- * @see traceCoroutine
- */
- inline fun <T> CoroutineScope.async(
- spanName: String,
- context: CoroutineContext = EmptyCoroutineContext,
- // TODO(b/306457056): DO NOT pass CoroutineStart; doing so will regress .odex size
- crossinline block: suspend CoroutineScope.() -> T
- ): Deferred<T> = async(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [runBlocking] with [traceCoroutine] to enable tracing.
- *
- * @see traceCoroutine
- */
- inline fun <T> runBlocking(
- crossinline spanName: () -> String,
- context: CoroutineContext,
- crossinline block: suspend () -> T
- ): T = runBlocking(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [runBlocking] with [traceCoroutine] to enable tracing.
- *
- * @see traceCoroutine
- */
- inline fun <T> runBlocking(
- spanName: String,
- context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
- ): T = runBlocking(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [withContext] with [traceCoroutine] to enable tracing.
- *
- * @see traceCoroutine
- */
- suspend inline fun <T> withContext(
- spanName: String,
- context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
- ): T = withContext(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * Convenience function for calling [withContext] with [traceCoroutine] to enable tracing.
- *
- * @see traceCoroutine
- */
- suspend inline fun <T> withContext(
- crossinline spanName: () -> String,
- context: CoroutineContext,
- crossinline block: suspend CoroutineScope.() -> T
- ): T = withContext(context) { traceCoroutine(spanName) { block() } }
-
- /**
- * A hacky way to propagate the value of the COROUTINE_TRACING flag for static usage in this
- * file. It should only every be set to true during startup. Once true, it cannot be set to
- * false again.
- */
- var coroutineTracingIsEnabled = false
- set(v) {
- if (v) field = true
- }
-
- /**
- * Traces a section of work of a `suspend` [block]. The trace sections will appear on the
- * thread that is currently executing the [block] of work. If the [block] is suspended, all
- * trace sections added using this API will end until the [block] is resumed, which could
- * happen either on this thread or on another thread. If a child coroutine is started, it
- * will inherit the trace sections of its parent. The child will continue to print these
- * trace sections whether or not the parent coroutine is still running them.
- *
- * The current [CoroutineContext] must have a [TraceContextElement] for this API to work.
- * Otherwise, the trace sections will be dropped.
- *
- * For example, in the following trace, Thread #1 ran some work, suspended, then continued
- * working on Thread #2. Meanwhile, Thread #2 created a new child coroutine which inherited
- * its trace sections. Then, the original coroutine resumed on Thread #1 before ending.
- * Meanwhile Thread #3 is still printing trace sections from its parent because they were
- * copied when it was created. There is no way for the parent to communicate to the child
- * that it marked these slices as completed. While this might seem counterintuitive, it
- * allows us to pinpoint the origin of the child coroutine's work.
- *
- * ```
- * Thread #1 | [==== Slice A ====] [==== Slice A ====]
- * | [==== B ====] [=== B ===]
- * --------------------------------------------------------------------------------------
- * Thread #2 | [====== Slice A ======]
- * | [========= B =========]
- * | [===== C ======]
- * --------------------------------------------------------------------------------------
- * Thread #3 | [== Slice A ==] [== Slice A ==]
- * | [===== B =====] [===== B =====]
- * | [===== C =====] [===== C =====]
- * | [=== D ===]
- * ```
- *
- * @param name The name of the code section to appear in the trace
- * @see endSlice
- * @see traceCoroutine
- */
- @OptIn(ExperimentalCoroutinesApi::class)
- suspend inline fun <T> traceCoroutine(
- spanName: Lazy<String>,
- crossinline block: suspend () -> T
- ): T {
- // For coroutine tracing to work, trace spans must be added and removed even when
- // tracing is not active (i.e. when TRACE_TAG_APP is disabled). Otherwise, when the
- // coroutine resumes when tracing is active, we won't know its name.
- val tracer = getTraceData(spanName)
- val coroutineSpanCookie = tracer?.beginSpan(spanName.value) ?: INVALID_SPAN
-
- // For now, also trace to "AsyncTraces". This will allow us to verify the correctness
- // of the COROUTINE_TRACING feature flag.
- val asyncTraceCookie =
- if (Trace.isTagEnabled(Trace.TRACE_TAG_APP))
- Random.nextInt(FIRST_VALID_SPAN, Int.MAX_VALUE)
- else INVALID_SPAN
- if (asyncTraceCookie != INVALID_SPAN) {
- Trace.asyncTraceForTrackBegin(
- Trace.TRACE_TAG_APP,
- DEFAULT_TRACK_NAME,
- spanName.value,
- asyncTraceCookie
- )
- }
- try {
- return block()
- } finally {
- if (asyncTraceCookie != INVALID_SPAN) {
- Trace.asyncTraceForTrackEnd(
- Trace.TRACE_TAG_APP,
- DEFAULT_TRACK_NAME,
- asyncTraceCookie
- )
- }
- tracer?.endSpan(coroutineSpanCookie)
- }
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- suspend fun getTraceData(spanName: Lazy<String>): TraceData? {
- if (!coroutineTracingIsEnabled) {
- logVerbose("Experimental flag COROUTINE_TRACING is off", spanName)
- } else if (coroutineContext[TraceContextElement] == null) {
- logVerbose("Current CoroutineContext is missing TraceContextElement", spanName)
- } else {
- return threadLocalTrace.get().also {
- if (it == null) logVerbose("ThreadLocal TraceData is null", spanName)
- }
- }
- return null
- }
-
- private fun logVerbose(logMessage: String, spanName: Lazy<String>) {
- if (DEBUG_COROUTINE_TRACING && Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "$logMessage. Dropping trace section: \"${spanName.value}\"")
- }
- }
-
- /** @see traceCoroutine */
- suspend inline fun <T> traceCoroutine(
- spanName: String,
- crossinline block: suspend () -> T
- ): T = traceCoroutine(lazyOf(spanName)) { block() }
-
- /** @see traceCoroutine */
- suspend inline fun <T> traceCoroutine(
- crossinline spanName: () -> String,
- crossinline block: suspend () -> T
- ): T = traceCoroutine(lazy(LazyThreadSafetyMode.PUBLICATION) { spanName() }) { block() }
-
- /**
- * Writes a trace message to indicate that a given section of code has begun running __on
- * the current thread__. This must be followed by a corresponding call to [endSlice] in a
- * reasonably short amount of time __on the same thread__ (i.e. _before_ the thread becomes
- * idle again and starts running other, unrelated work).
- *
- * Calls to [beginSlice] and [endSlice] may be nested, and they will render in Perfetto as
- * follows:
- * ```
- * Thread #1 | [==========================]
- * | [==============]
- * | [====]
- * ```
- *
- * This function is provided for convenience to wrap a call to [Trace.traceBegin], which is
- * more verbose to call than [Trace.beginSection], but has the added benefit of not throwing
- * an [IllegalArgumentException] if the provided string is longer than 127 characters. We
- * use the term "slice" instead of "section" to be consistent with Perfetto.
- *
- * # Avoiding malformed traces
- *
- * Improper usage of this API will lead to malformed traces with long slices that sometimes
- * never end. This will look like the following:
- * ```
- * Thread #1 | [===================================================================== ...
- * | [==============] [====================================== ...
- * | [=======] [======] [===================== ...
- * | [=======]
- * ```
- *
- * To avoid this, [beginSlice] and [endSlice] should never be called from `suspend` blocks
- * (instead, use [traceCoroutine] for tracing suspending functions). While it would be
- * technically okay to call from a suspending function if that function were to only wrap
- * non-suspending blocks with [beginSlice] and [endSlice], doing so is risky because suspend
- * calls could be mistakenly added to that block as the code is refactored.
- *
- * Additionally, it is _not_ okay to call [beginSlice] when registering a callback and match
- * it with a call to [endSlice] inside that callback, even if the callback runs on the same
- * thread. Doing so would cause malformed traces because the [beginSlice] wasn't closed
- * before the thread became idle and started running unrelated work.
- *
- * @param sliceName The name of the code section to appear in the trace
- * @see endSlice
- * @see traceCoroutine
- */
- fun beginSlice(sliceName: String) {
- Trace.traceBegin(Trace.TRACE_TAG_APP, sliceName)
- }
-
- /**
- * Writes a trace message to indicate that a given section of code has ended. This call must
- * be preceded by a corresponding call to [beginSlice]. See [beginSlice] for important
- * information regarding usage.
- *
- * @see beginSlice
- * @see traceCoroutine
- */
- fun endSlice() {
- Trace.traceEnd(Trace.TRACE_TAG_APP)
- }
-
- /**
- * Writes a trace message indicating that an instant event occurred on the current thread.
- * Unlike slices, instant events have no duration and do not need to be matched with another
- * call. Perfetto will display instant events using an arrow pointing to the timestamp they
- * occurred:
- * ```
- * Thread #1 | [==============] [======]
- * | [====] ^
- * | ^
- * ```
- *
- * @param eventName The name of the event to appear in the trace.
- */
- fun instant(eventName: String) {
- Trace.instant(Trace.TRACE_TAG_APP, eventName)
- }
-
- /**
- * Writes a trace message indicating that an instant event occurred on the given track.
- * Unlike slices, instant events have no duration and do not need to be matched with another
- * call. Perfetto will display instant events using an arrow pointing to the timestamp they
- * occurred:
- * ```
- * Async | [==============] [======]
- * Track | [====] ^
- * Name | ^
- * ```
- *
- * @param trackName The track where the event should appear in the trace.
- * @param eventName The name of the event to appear in the trace.
- */
- fun instantForTrack(trackName: String, eventName: String) {
- Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, eventName)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 7bf3e8f..54f1457 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -45,6 +45,8 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.KeyguardClockLog;
@@ -95,6 +97,7 @@
private final ClockEventController mClockEventController;
private final LogBuffer mLogBuffer;
private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
+ private final KeyguardRootViewModel mKeyguardRootViewModel;
private final ConfigurationState mConfigurationState;
private final ConfigurationController mConfigurationController;
private final DozeParameters mDozeParameters;
@@ -127,7 +130,7 @@
private KeyguardInteractor mKeyguardInteractor;
private final DelayableExecutor mUiExecutor;
private boolean mCanShowDoubleLineClock = true;
- private DisposableHandle mAodIconsBindJob;
+ private DisposableHandle mAodIconsBindHandle;
@Nullable private NotificationIconContainer mAodIconContainer;
@VisibleForTesting
@@ -179,6 +182,7 @@
ClockEventController clockEventController,
@KeyguardClockLog LogBuffer logBuffer,
NotificationIconContainerAlwaysOnDisplayViewModel aodIconsViewModel,
+ KeyguardRootViewModel keyguardRootViewModel,
ConfigurationState configurationState,
DozeParameters dozeParameters,
AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
@@ -199,6 +203,7 @@
mClockEventController = clockEventController;
mLogBuffer = logBuffer;
mAodIconsViewModel = aodIconsViewModel;
+ mKeyguardRootViewModel = keyguardRootViewModel;
mConfigurationState = configurationState;
mDozeParameters = dozeParameters;
mAodIconViewStore = aodIconViewStore;
@@ -567,21 +572,32 @@
mView.findViewById(
com.android.systemui.res.R.id.left_aligned_notification_icon_container);
if (NotificationIconContainerRefactor.isEnabled()) {
- if (mAodIconsBindJob != null) {
- mAodIconsBindJob.dispose();
+ if (mAodIconsBindHandle != null) {
+ mAodIconsBindHandle.dispose();
}
if (nic != null) {
nic.setOnLockScreen(true);
- mAodIconsBindJob = NotificationIconContainerViewBinder.bind(
- nic,
- mAodIconsViewModel,
- mConfigurationState,
- mConfigurationController,
- mDozeParameters,
- mFeatureFlags,
- mScreenOffAnimationController,
- mAodIconViewStore
- );
+ final DisposableHandle viewHandle = NotificationIconContainerViewBinder.bind(
+ nic,
+ mAodIconsViewModel,
+ mConfigurationState,
+ mConfigurationController,
+ mDozeParameters,
+ mAodIconViewStore);
+ final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
+ nic,
+ mKeyguardRootViewModel.isNotifIconContainerVisible(),
+ mConfigurationState,
+ mFeatureFlags,
+ mScreenOffAnimationController);
+ if (visHandle == null) {
+ mAodIconsBindHandle = viewHandle;
+ } else {
+ mAodIconsBindHandle = () -> {
+ viewHandle.dispose();
+ visHandle.dispose();
+ };
+ }
mAodIconContainer = nic;
}
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index 4160ae1..953cf88 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -30,6 +30,7 @@
var allowOnCurrentOccludingActivity: Boolean = false,
var alternateBouncerShowing: Boolean = false,
var biometricEnabledForUser: Boolean = false,
+ var biometricPromptShowing: Boolean = false,
var bouncerIsOrWillShow: Boolean = false,
var canSkipBouncer: Boolean = false,
var credentialAttempted: Boolean = false,
@@ -61,6 +62,7 @@
allowOnCurrentOccludingActivity.toString(),
alternateBouncerShowing.toString(),
biometricEnabledForUser.toString(),
+ biometricPromptShowing.toString(),
bouncerIsOrWillShow.toString(),
canSkipBouncer.toString(),
credentialAttempted.toString(),
@@ -101,6 +103,7 @@
allowOnCurrentOccludingActivity = model.allowOnCurrentOccludingActivity
alternateBouncerShowing = model.alternateBouncerShowing
biometricEnabledForUser = model.biometricEnabledForUser
+ biometricPromptShowing = model.biometricPromptShowing
bouncerIsOrWillShow = model.bouncerIsOrWillShow
canSkipBouncer = model.canSkipBouncer
credentialAttempted = model.credentialAttempted
@@ -147,6 +150,7 @@
"allowOnCurrentOccludingActivity",
"alternateBouncerShowing",
"biometricAllowedForUser",
+ "biometricPromptShowing",
"bouncerIsOrWillShow",
"canSkipBouncer",
"credentialAttempted",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f19a9ed..baab637 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -380,6 +380,7 @@
private boolean mOccludingAppRequestingFace;
private boolean mSecureCameraLaunched;
private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
+ private boolean mBiometricPromptShowing;
@VisibleForTesting
protected boolean mTelephonyCapable;
private boolean mAllowFingerprintOnCurrentOccludingActivity;
@@ -2010,9 +2011,17 @@
};
private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback =
- (sensorId, userId, isStrongBiometric) -> {
- // Trigger the fingerprint detected path so the bouncer can be shown
- handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+ new FingerprintManager.FingerprintDetectionCallback() {
+ @Override
+ public void onDetectionError(int errorMsgId) {
+ handleFingerprintError(errorMsgId, "");
+ }
+
+ @Override
+ public void onFingerprintDetected(int sensorId, int userId,
+ boolean isStrongBiometric) {
+ handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+ }
};
private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
@@ -2641,6 +2650,19 @@
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_TRIGGERED_ENROLLMENTS_CHANGED));
}
+
+ @Override
+ public void onBiometricPromptShown() {
+ // SysUI should give priority to the biometric prompt requesting FP instead of
+ // taking over the fingerprint listening state.
+ mBiometricPromptShowing = true;
+ }
+
+ @Override
+ public void onBiometricPromptDismissed() {
+ mBiometricPromptShowing = false;
+ updateFingerprintListeningState(BIOMETRIC_ACTION_START);
+ }
});
if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
mPostureController.addCallback(mPostureCallback);
@@ -3139,7 +3161,7 @@
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState;
+ && shouldListenBouncerState && shouldListenUdfpsState && !mBiometricPromptShowing;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -3148,6 +3170,7 @@
mAllowFingerprintOnCurrentOccludingActivity,
mAlternateBouncerShowing,
biometricEnabledForUser,
+ mBiometricPromptShowing,
mPrimaryBouncerIsOrWillBeShowing,
userCanSkipBouncer,
mCredentialAttempted,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 52923a7..345f15c 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,6 +16,8 @@
package com.android.systemui.authentication.domain.interactor
+import com.android.app.tracing.TraceUtils.Companion.async
+import com.android.app.tracing.TraceUtils.Companion.withContext
import com.android.internal.widget.LockPatternView
import com.android.internal.widget.LockscreenCredential
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
@@ -27,8 +29,6 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
-import com.android.systemui.tracing.TraceUtils.Companion.async
-import com.android.systemui.tracing.TraceUtils.Companion.withContext
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 85122ba..e69eced 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -18,6 +18,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
import android.animation.Animator;
@@ -63,7 +64,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
@@ -76,8 +76,8 @@
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -280,7 +280,6 @@
// TODO(b/251476085): remove Config and further decompose these properties out of view classes
AuthContainerView(@NonNull Config config,
- @NonNull FeatureFlags featureFlags,
@NonNull CoroutineScope applicationCoroutineScope,
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
@@ -295,7 +294,7 @@
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper) {
- this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps,
+ this(config, applicationCoroutineScope, fpProps, faceProps,
wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel,
credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
@@ -304,7 +303,6 @@
@VisibleForTesting
AuthContainerView(@NonNull Config config,
- @NonNull FeatureFlags featureFlags,
@NonNull CoroutineScope applicationCoroutineScope,
@Nullable List<FingerprintSensorPropertiesInternal> fpProps,
@Nullable List<FaceSensorPropertiesInternal> faceProps,
@@ -368,7 +366,7 @@
showPrompt(config, layoutInflater, promptViewModel,
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds),
- vibratorHelper, featureFlags);
+ vibratorHelper);
// TODO: De-dupe the logic with AuthCredentialPasswordView
setOnKeyListener((v, keyCode, event) -> {
@@ -390,8 +388,7 @@
@NonNull PromptViewModel viewModel,
@Nullable FingerprintSensorPropertiesInternal fpProps,
@Nullable FaceSensorPropertiesInternal faceProps,
- @NonNull VibratorHelper vibratorHelper,
- @NonNull FeatureFlags featureFlags
+ @NonNull VibratorHelper vibratorHelper
) {
if (Utils.isBiometricAllowed(config.mPromptInfo)) {
mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
@@ -407,7 +404,7 @@
getJankListener(view, TRANSIT,
BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper, featureFlags);
+ vibratorHelper);
// TODO(b/251476085): migrate these dependencies
if (fpProps != null && fpProps.isAnyUdfpsType()) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a64e862..05db56f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -78,7 +78,6 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.data.repository.BiometricType;
import com.android.systemui.statusbar.CommandQueue;
@@ -120,7 +119,6 @@
private final Handler mHandler;
private final Context mContext;
- private final FeatureFlags mFeatureFlags;
private final Execution mExecution;
private final CommandQueue mCommandQueue;
private final ActivityTaskManager mActivityTaskManager;
@@ -743,7 +741,6 @@
}
@Inject
public AuthController(Context context,
- @NonNull FeatureFlags featureFlags,
@Application CoroutineScope applicationCoroutineScope,
Execution execution,
CommandQueue commandQueue,
@@ -770,7 +767,6 @@
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper) {
mContext = context;
- mFeatureFlags = featureFlags;
mExecution = execution;
mUserManager = userManager;
mLockPatternUtils = lockPatternUtils;
@@ -1316,7 +1312,7 @@
config.mRequestId = requestId;
config.mSensorIds = sensorIds;
config.mScaleProvider = this::getScaleFactor;
- return new AuthContainerView(config, mFeatureFlags, mApplicationCoroutineScope, mFpProps, mFaceProps,
+ return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor,
viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 949c117..10e7227 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -53,6 +53,7 @@
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.model.KeyPath
import com.android.app.animation.Interpolators
+import com.android.app.tracing.traceSection
import com.android.internal.annotations.VisibleForTesting
import com.android.keyguard.KeyguardPINView
import com.android.systemui.Dumpable
@@ -63,7 +64,6 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.boundsOnScreen
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index f85203e..2bb19cd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.hardware.biometrics.SensorLocationInternal
import android.view.WindowManager
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation
import com.android.systemui.biometrics.shared.model.DisplayRotation
@@ -27,17 +28,18 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.log.SideFpsLogger
import com.android.systemui.res.R
+import java.util.Optional
import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class SideFpsSensorInteractor
@Inject
@@ -47,9 +49,11 @@
windowManager: WindowManager,
displayStateInteractor: DisplayStateInteractor,
featureFlags: FeatureFlagsClassic,
+ fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>,
+ private val logger: SideFpsLogger,
) {
- private val sensorForCurrentDisplay =
+ private val sensorLocationForCurrentDisplay =
combine(
displayStateInteractor.displayChanges,
fingerprintPropertyRepository.sensorLocations,
@@ -65,72 +69,99 @@
flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L)
val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
- isAvailable.flatMapLatest { sfpsAvailable ->
- if (sfpsAvailable) {
- // todo (b/305236201) also add the settings check here.
- flowOf(featureFlags.isEnabled(Flags.REST_TO_UNLOCK))
- } else {
- flowOf(false)
+ if (
+ fingerprintInteractiveToAuthProvider.isEmpty ||
+ !featureFlags.isEnabled(Flags.REST_TO_UNLOCK)
+ ) {
+ flowOf(false)
+ } else {
+ combine(
+ isAvailable,
+ fingerprintInteractiveToAuthProvider.get().enabledForCurrentUser
+ ) { sfpsAvailable, isSettingEnabled ->
+ sfpsAvailable && isSettingEnabled
}
}
val sensorLocation: Flow<SideFpsSensorLocation> =
- combine(displayStateInteractor.currentRotation, sensorForCurrentDisplay, ::Pair).map {
- (rotation, sensorLocation: SensorLocationInternal) ->
- val isSensorVerticalInDefaultOrientation = sensorLocation.sensorLocationY != 0
- // device dimensions in the current rotation
- val size = windowManager.maximumWindowMetrics.bounds
- val isDefaultOrientation = rotation.isDefaultOrientation()
- // Width and height are flipped is device is not in rotation_0 or rotation_180
- // Flipping it to the width and height of the device in default orientation.
- val displayWidth = if (isDefaultOrientation) size.width() else size.height()
- val displayHeight = if (isDefaultOrientation) size.height() else size.width()
- val sensorWidth = context.resources?.getInteger(R.integer.config_sfpsSensorWidth) ?: 0
+ combine(displayStateInteractor.currentRotation, sensorLocationForCurrentDisplay, ::Pair)
+ .map { (rotation, sensorLocation: SensorLocationInternal) ->
+ val isSensorVerticalInDefaultOrientation = sensorLocation.sensorLocationY != 0
+ // device dimensions in the current rotation
+ val windowMetrics = windowManager.maximumWindowMetrics
+ val size = windowMetrics.bounds
+ val isDefaultOrientation = rotation.isDefaultOrientation()
+ // Width and height are flipped is device is not in rotation_0 or rotation_180
+ // Flipping it to the width and height of the device in default orientation.
+ val displayWidth = if (isDefaultOrientation) size.width() else size.height()
+ val displayHeight = if (isDefaultOrientation) size.height() else size.width()
+ val sensorLengthInPx = sensorLocation.sensorRadius * 2
- val (sensorLeft, sensorTop) =
- if (isSensorVerticalInDefaultOrientation) {
- when (rotation) {
- DisplayRotation.ROTATION_0 -> {
- Pair(displayWidth, sensorLocation.sensorLocationY)
+ val (sensorLeft, sensorTop) =
+ if (isSensorVerticalInDefaultOrientation) {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 -> {
+ Pair(displayWidth, sensorLocation.sensorLocationY)
+ }
+ DisplayRotation.ROTATION_90 -> {
+ Pair(sensorLocation.sensorLocationY, 0)
+ }
+ DisplayRotation.ROTATION_180 -> {
+ Pair(
+ 0,
+ displayHeight -
+ sensorLocation.sensorLocationY -
+ sensorLengthInPx
+ )
+ }
+ DisplayRotation.ROTATION_270 -> {
+ Pair(
+ displayHeight -
+ sensorLocation.sensorLocationY -
+ sensorLengthInPx,
+ displayWidth
+ )
+ }
}
- DisplayRotation.ROTATION_90 -> {
- Pair(sensorLocation.sensorLocationY, 0)
- }
- DisplayRotation.ROTATION_180 -> {
- Pair(0, displayHeight - sensorLocation.sensorLocationY - sensorWidth)
- }
- DisplayRotation.ROTATION_270 -> {
- Pair(
- displayHeight - sensorLocation.sensorLocationY - sensorWidth,
- displayWidth
- )
+ } else {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 -> {
+ Pair(sensorLocation.sensorLocationX, 0)
+ }
+ DisplayRotation.ROTATION_90 -> {
+ Pair(
+ 0,
+ displayWidth - sensorLocation.sensorLocationX - sensorLengthInPx
+ )
+ }
+ DisplayRotation.ROTATION_180 -> {
+ Pair(
+ displayWidth -
+ sensorLocation.sensorLocationX -
+ sensorLengthInPx,
+ displayHeight
+ )
+ }
+ DisplayRotation.ROTATION_270 -> {
+ Pair(displayHeight, sensorLocation.sensorLocationX)
+ }
}
}
- } else {
- when (rotation) {
- DisplayRotation.ROTATION_0 -> {
- Pair(sensorLocation.sensorLocationX, 0)
- }
- DisplayRotation.ROTATION_90 -> {
- Pair(0, displayWidth - sensorLocation.sensorLocationX - sensorWidth)
- }
- DisplayRotation.ROTATION_180 -> {
- Pair(
- displayWidth - sensorLocation.sensorLocationX - sensorWidth,
- displayHeight
- )
- }
- DisplayRotation.ROTATION_270 -> {
- Pair(displayHeight, sensorLocation.sensorLocationX)
- }
- }
- }
- SideFpsSensorLocation(
- left = sensorLeft,
- top = sensorTop,
- width = sensorWidth,
- isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation
- )
- }
+ SideFpsSensorLocation(
+ left = sensorLeft,
+ top = sensorTop,
+ length = sensorLengthInPx,
+ isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation
+ )
+ }
+ .distinctUntilChanged()
+ .onEach {
+ logger.sensorLocationStateChanged(
+ it.left,
+ it.top,
+ it.length,
+ it.isSensorVerticalInDefaultOrientation
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt
index 35f8e3b..12f374f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt
@@ -21,8 +21,8 @@
val left: Int,
/** Pixel offset from the top of the screen */
val top: Int,
- /** Width in pixels of the SFPS sensor */
- val width: Int,
+ /** Length of the SFPS sensor in pixels in current display density */
+ val length: Int,
/**
* Whether the sensor is vertical when the device is in its default orientation (Rotation_0 or
* Rotation_180)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index ac48b6a..32d9067 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -49,8 +49,6 @@
import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
import com.android.systemui.biometrics.ui.viewmodel.PromptSize
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
@@ -78,7 +76,6 @@
legacyCallback: Spaghetti.Callback,
applicationScope: CoroutineScope,
vibratorHelper: VibratorHelper,
- featureFlags: FeatureFlags,
): Spaghetti {
val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
@@ -380,13 +377,11 @@
}
// Play haptics
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- launch {
- viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
- if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
- vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
- viewModel.clearHaptics()
- }
+ launch {
+ viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
+ if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
+ vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
+ viewModel.clearHaptics()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index e49b4a7..647aaf3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -29,10 +29,7 @@
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@@ -52,9 +49,7 @@
constructor(
displayStateInteractor: DisplayStateInteractor,
promptSelectorInteractor: PromptSelectorInteractor,
- private val vibrator: VibratorHelper,
@Application context: Context,
- private val featureFlags: FeatureFlags,
) {
/** The set of modalities available for this prompt */
val modalities: Flow<BiometricModalities> =
@@ -339,7 +334,7 @@
_message.value = PromptMessage.Error(message)
if (hapticFeedback) {
- vibrator.error(failedModality)
+ vibrateOnError()
}
messageJob?.cancel()
@@ -457,7 +452,7 @@
_message.value = PromptMessage.Empty
if (!needsUserConfirmation) {
- vibrator.success(modality)
+ vibrateOnSuccess()
}
messageJob?.cancel()
@@ -495,7 +490,7 @@
_isAuthenticated.value = authState.asExplicitlyConfirmed()
_message.value = PromptMessage.Empty
- vibrator.success(authState.authenticatedModality)
+ vibrateOnSuccess()
messageJob?.cancel()
messageJob = null
@@ -530,20 +525,12 @@
_forceLargeSize.value = true
}
- private fun VibratorHelper.success(modality: BiometricModality) {
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
- } else {
- vibrateAuthSuccess("$TAG, modality = $modality BP::success")
- }
+ private fun vibrateOnSuccess() {
+ _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
}
- private fun VibratorHelper.error(modality: BiometricModality = BiometricModality.None) {
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- _hapticsToPlay.value = HapticFeedbackConstants.REJECT
- } else {
- vibrateAuthError("$TAG, modality = $modality BP::error")
- }
+ private fun vibrateOnError() {
+ _hapticsToPlay.value = HapticFeedbackConstants.REJECT
}
/** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index f77f989..a79a654 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -75,5 +75,8 @@
/** Indicates an a11y action was made. */
void onA11yAction();
+
+ /** Initialize the class. */
+ void init();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index 0dfaf0f..d6b9a11 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -23,6 +23,10 @@
/** */
public class FalsingCollectorFake implements FalsingCollector {
+ @Override
+ public void init() {
+ }
+
@Inject
public FalsingCollectorFake() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index a6b073d..12df96e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -32,13 +32,14 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.sensors.ThresholdSensorEvent;
@@ -65,9 +66,11 @@
private final ProximitySensor mProximitySensor;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
+ private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private final BatteryController mBatteryController;
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
+ private final JavaAdapter mJavaAdapter;
private final SystemClock mSystemClock;
private final Lazy<SelectedUserInteractor> mUserInteractor;
@@ -136,10 +139,11 @@
ProximitySensor proximitySensor,
StatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ Lazy<ShadeInteractor> shadeInteractorLazy,
BatteryController batteryController,
DockManager dockManager,
@Main DelayableExecutor mainExecutor,
+ JavaAdapter javaAdapter,
SystemClock systemClock,
Lazy<SelectedUserInteractor> userInteractor) {
mFalsingDataProvider = falsingDataProvider;
@@ -149,12 +153,17 @@
mProximitySensor = proximitySensor;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
+ mShadeInteractorLazy = shadeInteractorLazy;
mBatteryController = batteryController;
mDockManager = dockManager;
mMainExecutor = mainExecutor;
+ mJavaAdapter = javaAdapter;
mSystemClock = systemClock;
mUserInteractor = userInteractor;
+ }
+ @Override
+ public void init() {
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
@@ -163,7 +172,10 @@
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
- shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
+ mJavaAdapter.alwaysCollectFlow(
+ mShadeInteractorLazy.get().isQsExpanded(),
+ this::onQsExpansionChanged
+ );
mBatteryController.addCallback(mBatteryListener);
mDockManager.addListener(mDockEventListener);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
index e5b404f..c5d8c79 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
@@ -23,6 +23,10 @@
@SysUISingleton
class FalsingCollectorNoOp @Inject constructor() : FalsingCollector {
+ override fun init() {
+ logDebug("NOOP: init")
+ }
+
override fun onSuccessfulUnlock() {
logDebug("NOOP: onSuccessfulUnlock")
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt
new file mode 100644
index 0000000..b79538a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.classifier
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Initializes classes related to falsing. */
+@SysUISingleton
+class FalsingCoreStartable @Inject constructor(val falsingCollector: FalsingCollector) :
+ CoreStartable {
+ override fun start() {
+ falsingCollector.init()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index 2729b7b..af467ef 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -19,9 +19,9 @@
import android.content.res.Resources;
import android.view.ViewConfiguration;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.statusbar.phone.NotificationTapHelper;
@@ -37,7 +37,7 @@
import javax.inject.Named;
/** Dagger Module for Falsing. */
-@Module
+@Module(includes = {FalsingStartModule.class})
public interface FalsingModule {
String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers";
String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop";
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
new file mode 100644
index 0000000..a9f8f37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.classifier
+
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface FalsingStartModule {
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(FalsingCoreStartable::class)
+ fun bindFalsingCoreStartable(falsingCoreStartable: FalsingCoreStartable?): CoreStartable?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
index b2bc06f..48d3742 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
@@ -20,4 +20,7 @@
data class SharedNotificationContainerPosition(
val top: Float = 0f,
val bottom: Float = 0f,
+
+ /** Whether any modifications to top/bottom are smoothly animated */
+ val animate: Boolean = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 485e512..9cab17e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -1,15 +1,28 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.FeatureFlags
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
+
+ /**
+ * Target scene as requested by the underlying [SceneTransitionLayout] or through
+ * [setDesiredScene].
+ */
+ val desiredScene: StateFlow<CommunalSceneKey>
+
+ /** Updates the requested scene. */
+ fun setDesiredScene(desiredScene: CommunalSceneKey)
}
@SysUISingleton
@@ -23,4 +36,12 @@
get() =
featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) &&
featureFlags.communalHub()
+
+ private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
+ MutableStateFlow(CommunalSceneKey.Blank)
+ override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
+
+ override fun setDesiredScene(desiredScene: CommunalSceneKey) {
+ _desiredScene.value = desiredScene
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 6238707..ccccbb6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -19,10 +19,13 @@
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
/** Encapsulates business-logic related to communal mode. */
@SysUISingleton
@@ -47,4 +50,22 @@
* (have an allocated id).
*/
val widgetContent: Flow<List<CommunalWidgetContentModel>> = widgetRepository.communalWidgets
+
+ /**
+ * Target scene as requested by the underlying [SceneTransitionLayout] or through
+ * [onSceneChanged].
+ */
+ val desiredScene: StateFlow<CommunalSceneKey> = communalRepository.desiredScene
+
+ /**
+ * Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
+ * [CommunalSceneKey.Communal].
+ */
+ val isCommunalShowing: Flow<Boolean> =
+ communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }
+
+ /** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
+ fun onSceneChanged(newScene: CommunalSceneKey) {
+ communalRepository.setDesiredScene(newScene)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 276df4e..7f43eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -19,12 +19,23 @@
import android.provider.Settings
import com.android.systemui.communal.data.repository.CommunalTutorialRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
/** Encapsulates business-logic related to communal tutorial state. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -32,8 +43,12 @@
class CommunalTutorialInteractor
@Inject
constructor(
- communalTutorialRepository: CommunalTutorialRepository,
+ @Application private val scope: CoroutineScope,
+ private val communalTutorialRepository: CommunalTutorialRepository,
keyguardInteractor: KeyguardInteractor,
+ private val communalInteractor: CommunalInteractor,
+ private val sceneContainerFlags: SceneContainerFlags,
+ private val sceneInteractor: SceneInteractor,
) {
/** An observable for whether the tutorial is available. */
val isTutorialAvailable: Flow<Boolean> =
@@ -45,4 +60,63 @@
tutorialSettingState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
}
.distinctUntilChanged()
+
+ /**
+ * A flow of the new tutorial state after transitioning. The new state will be calculated based
+ * on the current tutorial state and transition state as following:
+ * HUB_MODE_TUTORIAL_NOT_STARTED + communal scene -> HUB_MODE_TUTORIAL_STARTED
+ * HUB_MODE_TUTORIAL_STARTED + non-communal scene -> HUB_MODE_TUTORIAL_COMPLETED
+ * HUB_MODE_TUTORIAL_COMPLETED + any scene -> won't emit
+ */
+ private val tutorialStateToUpdate: Flow<Int> =
+ communalTutorialRepository.tutorialSettingState
+ .flatMapLatest { tutorialSettingState ->
+ if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
+ return@flatMapLatest flowOf(null)
+ }
+ if (sceneContainerFlags.isEnabled()) {
+ sceneInteractor.desiredScene.map { sceneModel ->
+ nextStateAfterTransition(
+ tutorialSettingState,
+ sceneModel.key == SceneKey.Communal
+ )
+ }
+ } else {
+ communalInteractor.isCommunalShowing.map {
+ nextStateAfterTransition(tutorialSettingState, it)
+ }
+ }
+ }
+ .filterNotNull()
+ .distinctUntilChanged()
+
+ private fun nextStateAfterTransition(tutorialState: Int, isCommunalShowing: Boolean): Int? {
+ if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED && isCommunalShowing) {
+ return Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+ }
+ if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_STARTED && !isCommunalShowing) {
+ return Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+ }
+ return null
+ }
+
+ private var job: Job? = null
+ private fun listenForTransitionToUpdateTutorialState() {
+ if (!communalInteractor.isCommunalEnabled) {
+ return
+ }
+ job =
+ scope.launch {
+ tutorialStateToUpdate.collect {
+ communalTutorialRepository.setTutorialState(it)
+ if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
+ job?.cancel()
+ }
+ }
+ }
+ }
+
+ init {
+ listenForTransitionToUpdateTutorialState()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index 39a6476..c903709 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -16,14 +16,19 @@
package com.android.systemui.communal.shared.model
-/** Supported sizes for communal content in the layout grid. */
-enum class CommunalContentSize {
+/**
+ * Supported sizes for communal content in the layout grid.
+ *
+ * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents
+ * HALF, 2 represents THIRD, and 1 represents SIXTH.
+ */
+enum class CommunalContentSize(val span: Int) {
/** Content takes the full height of the column. */
- FULL,
+ FULL(6),
/** Content takes half of the height of the column. */
- HALF,
+ HALF(3),
/** Content takes a third of the height of the column. */
- THIRD,
+ THIRD(2),
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
new file mode 100644
index 0000000..2be909c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalSceneKey.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.communal.shared.model
+
+/** Definition of the possible scenes for the communal UI. */
+sealed class CommunalSceneKey(
+ private val loggingName: String,
+) {
+ /** The communal scene containing the hub UI. */
+ object Communal : CommunalSceneKey("communal")
+
+ /** The default scene, shows nothing and is only there to allow swiping to communal. */
+ object Blank : CommunalSceneKey("blank")
+
+ override fun toString(): String {
+ return loggingName
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
index dab6819..4dfc371 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
@@ -44,6 +44,8 @@
)
}
}
+
+ launch { viewModel.alpha.collect { view.alpha = it } }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
index 98060dc..b60dc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
@@ -9,7 +9,7 @@
* This model stays in the UI layer.
*/
data class CommunalContentUiModel(
+ val id: String,
val view: View,
- val size: CommunalContentSize,
- val priority: Int,
+ val size: CommunalContentSize = CommunalContentSize.HALF,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
index eaf9550..274e61a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
@@ -17,15 +17,21 @@
package com.android.systemui.communal.ui.viewmodel
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
/** View model for communal tutorial indicator on keyguard */
class CommunalTutorialIndicatorViewModel
@Inject
constructor(
communalTutorialInteractor: CommunalTutorialInteractor,
+ bottomAreaInteractor: KeyguardBottomAreaInteractor,
) {
/** An observable for whether the tutorial indicator view should be visible. */
val showIndicator: Flow<Boolean> = communalTutorialInteractor.isTutorialAvailable
+
+ /** An observable for the alpha level for the tutorial indicator. */
+ val alpha: Flow<Float> = bottomAreaInteractor.alpha.distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 25c64ea..de9b563 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -20,12 +20,13 @@
import android.content.Context
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
-import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@SysUISingleton
@@ -34,7 +35,7 @@
constructor(
@Application private val context: Context,
private val appWidgetHost: AppWidgetHost,
- communalInteractor: CommunalInteractor,
+ private val communalInteractor: CommunalInteractor,
tutorialInteractor: CommunalTutorialInteractor,
) {
/** Whether communal hub should show tutorial content. */
@@ -42,17 +43,22 @@
/** List of widgets to be displayed in the communal hub. */
val widgetContent: Flow<List<CommunalContentUiModel>> =
- communalInteractor.widgetContent.map {
- it.map {
+ communalInteractor.widgetContent.map { widgets ->
+ widgets.map Widget@{ widget ->
// TODO(b/306406256): As adding and removing widgets functionalities are
// supported, cache the host views so they're not recreated each time.
- val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo)
- return@map CommunalContentUiModel(
+ val hostView =
+ appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo)
+ return@Widget CommunalContentUiModel(
+ // TODO(b/308148193): a more scalable solution for unique ids.
+ id = "widget_${widget.appWidgetId}",
view = hostView,
- priority = it.priority,
- // All widgets have HALF size.
- size = CommunalContentSize.HALF,
)
}
}
+
+ val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
+ fun onSceneChanged(scene: CommunalSceneKey) {
+ communalInteractor.onSceneChanged(scene)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 00d95c0..0f038e1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -37,8 +37,6 @@
import com.android.systemui.controls.settings.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -59,7 +57,6 @@
private val controlsMetricsLogger: ControlsMetricsLogger,
private val vibrator: VibratorHelper,
private val controlsSettingsRepository: ControlsSettingsRepository,
- private val featureFlags: FeatureFlags,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
@@ -123,17 +120,12 @@
}
override fun drag(cvh: ControlViewHolder, isEdge: Boolean) {
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- val constant =
- if (isEdge)
- HapticFeedbackConstants.SEGMENT_TICK
- else
- HapticFeedbackConstants.SEGMENT_FREQUENT_TICK
- vibrator.performHapticFeedback(cvh.layout, constant)
- } else {
- val effect = if (isEdge) Vibrations.rangeEdgeEffect else Vibrations.rangeMiddleEffect
- vibrate(effect)
- }
+ val constant =
+ if (isEdge)
+ HapticFeedbackConstants.SEGMENT_TICK
+ else
+ HapticFeedbackConstants.SEGMENT_FREQUENT_TICK
+ vibrator.performHapticFeedback(cvh.layout, constant)
}
override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt
deleted file mode 100644
index 29b7e985..0000000
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt
+++ /dev/null
@@ -1,37 +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.systemui.controls.ui
-
-import android.os.VibrationEffect
-import android.os.VibrationEffect.Composition.PRIMITIVE_TICK
-
-object Vibrations {
- val rangeEdgeEffect = initRangeEdgeEffect()
- val rangeMiddleEffect = initRangeMiddleEffect()
-
- private fun initRangeEdgeEffect(): VibrationEffect {
- val composition = VibrationEffect.startComposition()
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- return composition.compose()
- }
-
- private fun initRangeMiddleEffect(): VibrationEffect {
- val composition = VibrationEffect.startComposition()
- composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.1f)
- return composition.compose()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 1dd4abf..236c5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -48,7 +48,6 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
-import com.android.systemui.statusbar.events.StatusBarEventsModule;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpModule;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -99,7 +98,6 @@
ReferenceScreenshotModule.class,
RotationLockModule.class,
SceneContainerFrameworkModule.class,
- StatusBarEventsModule.class,
StartCentralSurfacesModule.class,
VolumeModule.class,
WallpaperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a41bb2f..7915088 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -96,6 +96,7 @@
import com.android.systemui.statusbar.connectivity.ConnectivityModule;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
+import com.android.systemui.statusbar.events.StatusBarEventsModule;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -209,6 +210,7 @@
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
SmartspaceModule.class,
+ StatusBarEventsModule.class,
StatusBarModule.class,
StatusBarPipelineModule.class,
StatusBarPolicyModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 2ba687b..26c5ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -23,15 +23,15 @@
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
import android.os.Handler
-import android.os.Trace
import android.util.Log
import android.view.Display
+import com.android.app.tracing.FlowTracing.traceEach
+import com.android.app.tracing.traceSection
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.DisplayEvent
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.Compile
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -43,10 +43,9 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
@@ -97,7 +96,6 @@
@Application applicationScope: CoroutineScope,
@Background backgroundCoroutineDispatcher: CoroutineDispatcher
) : DisplayRepository {
- // Displays are enabled only after receiving them in [onDisplayAdded]
private val allDisplayEvents: Flow<DisplayEvent> =
conflatedCallbackFlow {
val callback =
@@ -124,16 +122,22 @@
awaitClose { displayManager.unregisterDisplayListener(callback) }
}
.onStart { emit(DisplayEvent.Changed(Display.DEFAULT_DISPLAY)) }
+ .debugLog("allDisplayEvents")
.flowOn(backgroundCoroutineDispatcher)
override val displayChangeEvent: Flow<Int> =
- allDisplayEvents.filter { it is DisplayEvent.Changed }.map { it.displayId }
+ allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
override val displayAdditionEvent: Flow<Display?> =
- allDisplayEvents
- .filter { it is DisplayEvent.Added }
- .map { displayManager.getDisplay(it.displayId) }
+ allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
+ displayManager.getDisplay(it.displayId)
+ }
+ /**
+ * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
+ *
+ * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
+ */
private val enabledDisplays =
allDisplayEvents
.map { getDisplays() }
@@ -143,9 +147,7 @@
override val displays: Flow<Set<Display>> = enabledDisplays
private fun getDisplays(): Set<Display> =
- traceSection("DisplayRepository#getDisplays()") {
- displayManager.displays?.toSet() ?: emptySet()
- }
+ traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() }
/** Propagate to the listeners only enabled displays */
private val enabledDisplayIds: Flow<Set<Int>> =
@@ -153,7 +155,8 @@
.map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() }
.debugLog("enabledDisplayIds")
- private val ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+ private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
+ private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
private fun getInitialConnectedDisplays(): Set<Int> =
displayManager
@@ -177,7 +180,7 @@
Log.d(TAG, "display with id=$id connected.")
}
connectedIds += id
- ignoredDisplayIds.value -= id
+ _ignoredDisplayIds.value -= id
trySend(connectedIds.toSet())
}
@@ -186,7 +189,7 @@
if (DEBUG) {
Log.d(TAG, "display with id=$id disconnected.")
}
- ignoredDisplayIds.value -= id
+ _ignoredDisplayIds.value -= id
trySend(connectedIds.toSet())
}
}
@@ -214,17 +217,23 @@
private val connectedExternalDisplayIds: Flow<Set<Int>> =
connectedDisplayIds
.map { connectedDisplayIds ->
- connectedDisplayIds
- .filter { id -> displayManager.getDisplay(id)?.type == Display.TYPE_EXTERNAL }
- .toSet()
+ traceSection("$TAG#filteringExternalDisplays") {
+ connectedDisplayIds
+ .filter { id -> getDisplayType(id) == Display.TYPE_EXTERNAL }
+ .toSet()
+ }
}
.flowOn(backgroundCoroutineDispatcher)
.debugLog("connectedExternalDisplayIds")
+ private fun getDisplayType(displayId: Int): Int? =
+ traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
+
/**
- * Pending displays are the ones connected, but not enabled and not ignored. A connected display
- * is ignored after the user makes the decision to use it or not. For now, the initial decision
- * from the user is final and not reversible.
+ * Pending displays are the ones connected, but not enabled and not ignored.
+ *
+ * A connected display is ignored after the user makes the decision to use it or not. For now,
+ * the initial decision from the user is final and not reversible.
*/
private val pendingDisplayIds: Flow<Set<Int>> =
combine(enabledDisplayIds, connectedExternalDisplayIds, ignoredDisplayIds) {
@@ -241,12 +250,16 @@
}
connectedExternalDisplayIds - enabledDisplaysIds - ignoredDisplayIds
}
- .debugLog("pendingDisplayIds")
+ .debugLog("allPendingDisplayIds")
+
+ /** Which display id should be enabled among the pending ones. */
+ private val pendingDisplayId: Flow<Int?> =
+ pendingDisplayIds.map { it.maxOrNull() }.distinctUntilChanged().debugLog("pendingDisplayId")
override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?> =
- pendingDisplayIds
- .map { pendingDisplayIds ->
- val id = pendingDisplayIds.maxOrNull() ?: return@map null
+ pendingDisplayId
+ .map { displayId ->
+ val id = displayId ?: return@map null
object : DisplayRepository.PendingDisplay {
override val id = id
@@ -263,7 +276,7 @@
override suspend fun ignore() {
traceSection("DisplayRepository#ignore($id)") {
- ignoredDisplayIds.value += id
+ _ignoredDisplayIds.value += id
}
}
@@ -282,11 +295,7 @@
private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
return if (DEBUG) {
- this.onEach {
- Log.d(TAG, "$flowName: $it")
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "$TAG#$flowName", 0)
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "$TAG#$flowName", "$it", 0)
- }
+ traceEach(flowName, logcat = true)
} else {
this
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 4cade77..323ed98 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -261,6 +261,13 @@
}
//TODO: brightnessfloat change usages to float.
private int clampToUserSetting(int brightness) {
+ int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
+ if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+ return brightness;
+ }
+
int userSetting = mSystemSettings.getIntForUser(
Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 1c2ff4b..36d789e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -16,12 +16,14 @@
package com.android.systemui.flags;
+import static com.android.systemui.Flags.exampleFlag;
import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
import static com.android.systemui.flags.FlagManager.EXTRA_NAME;
import static com.android.systemui.flags.FlagManager.EXTRA_VALUE;
import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS;
+import static com.android.systemui.shared.Flags.exampleSharedFlag;
import static java.util.Objects.requireNonNull;
@@ -539,6 +541,8 @@
pw.println("can override: true");
pw.println("teamfood: " + mGantryFlags.sysuiTeamfood());
pw.println("booleans: " + mBooleanFlagCache.size());
+ pw.println("example_flag: " + exampleFlag());
+ pw.println("example_shared_flag: " + exampleSharedFlag());
// Sort our flags for dumping
TreeMap<String, Boolean> dumpBooleanMap = new TreeMap<>(mBooleanFlagCache);
dumpBooleanMap.forEach((key, value) -> pw.println(" sysui_flag_" + key + ": " + value));
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f4a9f73..c1106b3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -258,7 +258,7 @@
// TODO(b/290652751): Tracking bug.
@JvmField
val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
- unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
+ unreleasedFlag("migrate_split_keyguard_bottom_area")
// TODO(b/297037052): Tracking bug.
@JvmField
@@ -273,7 +273,7 @@
/** Migrate the lock icon view to the new keyguard root view. */
// TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
+ @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon")
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -400,8 +400,7 @@
// 600- status bar
// TODO(b/291315866): Tracking Bug
- @JvmField val SIGNAL_CALLBACK_DEPRECATION =
- unreleasedFlag("signal_callback_deprecation", teamfood = true)
+ @JvmField val SIGNAL_CALLBACK_DEPRECATION = releasedFlag("signal_callback_deprecation")
// TODO(b/301610137): Tracking bug
@JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true)
@@ -410,19 +409,15 @@
val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
releasedFlag("filter_provisioning_network_subscriptions")
- // TODO(b/265892345): Tracking Bug
- val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
-
// TODO(b/292533677): Tracking Bug
- val WIFI_TRACKER_LIB_FOR_WIFI_ICON =
- unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true)
+ val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon")
// TODO(b/293863612): Tracking Bug
@JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
releasedFlag("incompatible_charging_battery_icon")
// TODO(b/293585143): Tracking Bug
- val INSTANT_TETHER = unreleasedFlag("instant_tether", teamfood = true)
+ val INSTANT_TETHER = releasedFlag("instant_tether")
// TODO(b/294588085): Tracking Bug
val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 4b98526..c490ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -30,13 +30,13 @@
import android.os.Binder
import android.os.Bundle
import android.util.Log
+import com.android.app.tracing.TraceUtils.Companion.runBlocking
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
-import com.android.systemui.tracing.TraceUtils.Companion.runBlocking
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c56dfde..61c8e1bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -27,9 +27,10 @@
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
@@ -46,7 +47,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.Lazy
import javax.inject.Inject
@@ -63,14 +64,15 @@
private val keyguardRootViewModel: KeyguardRootViewModel,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
private val notificationShadeWindowView: NotificationShadeWindowView,
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
private val indicationController: KeyguardIndicationController,
- private val keyguardStateController: KeyguardStateController,
+ private val screenOffAnimationController: ScreenOffAnimationController,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val configuration: ConfigurationState,
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: Lazy<LockIconViewController>,
@@ -143,10 +145,11 @@
KeyguardRootViewBinder.bind(
keyguardRootView,
keyguardRootViewModel,
+ configuration,
featureFlags,
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
- keyguardStateController,
+ screenOffAnimationController,
shadeInteractor,
{ keyguardStatusViewController!!.getClockController() },
interactionJankMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index 5c1b731..f9b89b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -17,7 +17,7 @@
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
import javax.inject.Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 0e795ae..c4962a1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -18,6 +18,7 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
+import com.android.app.tracing.TraceUtils.Companion.launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -31,7 +32,6 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.tracing.TraceUtils.Companion.launch
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index fbe92e3..448411e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -22,6 +22,7 @@
import android.content.Context
import android.content.Intent
import android.util.Log
+import com.android.app.tracing.TraceUtils.Companion.withContext
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
@@ -48,7 +49,6 @@
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.tracing.TraceUtils.Companion.withContext
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 4d5c503..ad48957 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -16,23 +16,31 @@
package com.android.systemui.keyguard.ui.binder
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.annotation.DrawableRes
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.RefactorFlag
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
@@ -40,29 +48,38 @@
import com.android.systemui.plugins.ClockController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.VibratorHelper
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import com.android.systemui.util.ui.AnimatedValue
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
import javax.inject.Provider
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
-@ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
object KeyguardRootViewBinder {
@JvmStatic
fun bind(
view: ViewGroup,
viewModel: KeyguardRootViewModel,
- featureFlags: FeatureFlags,
+ configuration: ConfigurationState,
+ featureFlags: FeatureFlagsClassic,
occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
chipbarCoordinator: ChipbarCoordinator,
- keyguardStateController: KeyguardStateController,
+ screenOffAnimationController: ScreenOffAnimationController,
shadeInteractor: ShadeInteractor,
clockControllerProvider: Provider<ClockController>?,
interactionJankMonitor: InteractionJankMonitor?,
@@ -147,6 +164,24 @@
}
}
+ if (NotificationIconContainerRefactor.isEnabled) {
+ launch {
+ val iconsAppearTranslationPx =
+ configuration
+ .getDimensionPixelSize(R.dimen.shelf_appear_translation)
+ .stateIn(this)
+ viewModel.isNotifIconContainerVisible.collect { isVisible ->
+ childViews[aodNotificationIconContainerId]
+ ?.setAodNotifIconContainerIsVisible(
+ isVisible,
+ featureFlags,
+ iconsAppearTranslationPx.value,
+ screenOffAnimationController,
+ )
+ }
+ }
+ }
+
interactionJankMonitor?.let { jankMonitor ->
launch {
viewModel.goneToAodTransition.collect {
@@ -242,11 +277,18 @@
}
)
+ view.setOnApplyWindowInsetsListener { v: View, insets: WindowInsets ->
+ val insetTypes = WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout()
+ viewModel.topInset = insets.getInsetsIgnoringVisibility(insetTypes).top
+ insets
+ }
+
return object : DisposableHandle {
override fun dispose() {
disposableHandle.dispose()
view.removeOnLayoutChangeListener(onLayoutChangeListener)
view.setOnHierarchyChangeListener(null)
+ view.setOnApplyWindowInsetsListener(null)
childViews.clear()
}
}
@@ -288,7 +330,6 @@
oldBottom: Int
) {
val nsslPlaceholder = v.findViewById(R.id.nssl_placeholder) as View?
-
if (nsslPlaceholder != null) {
// After layout, ensure the notifications are positioned correctly
viewModel.onSharedNotificationContainerPositionChanged(
@@ -296,8 +337,132 @@
nsslPlaceholder.bottom.toFloat(),
)
}
+
+ val ksv = v.findViewById(R.id.keyguard_status_view) as View?
+ if (ksv != null) {
+ viewModel.statusViewTop = ksv.top
+ }
}
}
+ @JvmStatic
+ fun bindAodIconVisibility(
+ view: View,
+ isVisible: Flow<AnimatedValue<Boolean>>,
+ configuration: ConfigurationState,
+ featureFlags: FeatureFlagsClassic,
+ screenOffAnimationController: ScreenOffAnimationController,
+ ): DisposableHandle? {
+ RefactorFlag(featureFlags, Flags.MIGRATE_KEYGUARD_STATUS_VIEW).assertInLegacyMode()
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return null
+ return view.repeatWhenAttached {
+ lifecycleScope.launch {
+ val iconAppearTranslationPx =
+ configuration
+ .getDimensionPixelSize(R.dimen.shelf_appear_translation)
+ .stateIn(this)
+ isVisible.collect { isVisible ->
+ view.setAodNotifIconContainerIsVisible(
+ isVisible,
+ featureFlags,
+ iconAppearTranslationPx.value,
+ screenOffAnimationController,
+ )
+ }
+ }
+ }
+ }
+
+ private fun View.setAodNotifIconContainerIsVisible(
+ isVisible: AnimatedValue<Boolean>,
+ featureFlags: FeatureFlagsClassic,
+ iconsAppearTranslationPx: Int,
+ screenOffAnimationController: ScreenOffAnimationController,
+ ) {
+ val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
+ animate().cancel()
+ val animatorListener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ isVisible.stopAnimating()
+ }
+ }
+ when {
+ !isVisible.isAnimating -> {
+ alpha = 1f
+ if (!statusViewMigrated) {
+ translationY = 0f
+ }
+ visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
+ }
+ featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
+ animateInIconTranslation(statusViewMigrated)
+ if (isVisible.value) {
+ CrossFadeHelper.fadeIn(this, animatorListener)
+ } else {
+ CrossFadeHelper.fadeOut(this, animatorListener)
+ }
+ }
+ !isVisible.value -> {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInIconTranslation(statusViewMigrated)
+ CrossFadeHelper.fadeOut(this, animatorListener)
+ }
+ visibility != View.VISIBLE -> {
+ // No fading here, let's just appear the icons instead!
+ visibility = View.VISIBLE
+ alpha = 1f
+ appearIcons(
+ animate = screenOffAnimationController.shouldAnimateAodIcons(),
+ iconsAppearTranslationPx,
+ statusViewMigrated,
+ animatorListener,
+ )
+ }
+ else -> {
+ // Let's make sure the icons are translated to 0, since we cancelled it above
+ animateInIconTranslation(statusViewMigrated)
+ // We were fading out, let's fade in instead
+ CrossFadeHelper.fadeIn(this, animatorListener)
+ }
+ }
+ }
+
+ private fun View.appearIcons(
+ animate: Boolean,
+ iconAppearTranslation: Int,
+ statusViewMigrated: Boolean,
+ animatorListener: Animator.AnimatorListener,
+ ) {
+ if (animate) {
+ if (!statusViewMigrated) {
+ translationY = -iconAppearTranslation.toFloat()
+ }
+ alpha = 0f
+ animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .apply { if (statusViewMigrated) animateInIconTranslation() }
+ .setListener(animatorListener)
+ .start()
+ } else {
+ alpha = 1.0f
+ if (!statusViewMigrated) {
+ translationY = 0f
+ }
+ }
+ }
+
+ private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
+ if (!statusViewMigrated) {
+ animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
+ }
+ }
+
+ private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
+ setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
+
private const val ID = "occluding_app_device_entry_unlock_msg"
+ private const val AOD_ICONS_APPEAR_DURATION: Long = 200
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index 1acea5c..ba4876f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -16,19 +16,27 @@
package com.android.systemui.keyguard.ui.binder
+import android.animation.ValueAnimator
+import android.graphics.Point
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.SideFpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
+import com.android.systemui.log.SideFpsLogger
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.kotlin.Quint
+import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
+private const val spfsProgressBarCommand = "sfps-progress-bar"
+
@SysUISingleton
class SideFpsProgressBarViewBinder
@Inject
@@ -37,38 +45,112 @@
private val view: SideFpsProgressBar,
@Application private val applicationScope: CoroutineScope,
private val sfpsController: dagger.Lazy<SideFpsController>,
+ private val logger: SideFpsLogger,
+ private val commandRegistry: CommandRegistry,
) : CoreStartable {
override fun start() {
+ commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() }
applicationScope.launch {
viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled ->
+ logger.isProlongedTouchRequiredForAuthenticationChanged(enabled)
if (enabled) {
launch {
combine(
viewModel.isVisible,
- viewModel.sensorLocation,
- viewModel.shouldRotate90Degrees,
+ viewModel.progressBarLocation,
+ viewModel.rotation,
viewModel.isFingerprintAuthRunning,
- viewModel.sensorWidth,
+ viewModel.progressBarLength,
::Quint
)
- .collectLatest {
- (visible, location, shouldRotate, fpDetectRunning, sensorWidth) ->
- view.updateView(visible, location, shouldRotate, sensorWidth)
- // We have to hide the SFPS indicator as the progress bar will
- // be shown at the same location
- if (visible) {
- sfpsController.get().hideIndicator()
- } else if (fpDetectRunning) {
- sfpsController.get().showIndicator()
- }
+ .collectLatest { (visible, location, rotation, fpDetectRunning, length)
+ ->
+ updateView(
+ visible,
+ location,
+ fpDetectRunning,
+ length,
+ viewModel.progressBarThickness,
+ rotation,
+ )
}
}
launch { viewModel.progress.collectLatest { view.setProgress(it) } }
} else {
- view.hideOverlay()
+ view.hide()
}
}
}
}
+
+ private fun updateView(
+ visible: Boolean,
+ location: Point,
+ fpDetectRunning: Boolean,
+ length: Int,
+ thickness: Int,
+ rotation: Float,
+ ) {
+ logger.sfpsProgressBarStateChanged(visible, location, fpDetectRunning, length, rotation)
+ view.updateView(visible, location, length, thickness, rotation)
+ // We have to hide the SFPS indicator as the progress bar will
+ // be shown at the same location
+ if (visible) {
+ logger.hidingSfpsIndicator()
+ sfpsController.get().hideIndicator()
+ } else if (fpDetectRunning) {
+ logger.showingSfpsIndicator()
+ sfpsController.get().showIndicator()
+ }
+ }
+
+ inner class SfpsProgressBarCommand : Command {
+ private var animator: ValueAnimator? = null
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ if (args.isEmpty() || args[0] == "show" && args.size != 6) {
+ pw.println("invalid command")
+ help(pw)
+ } else {
+ when (args[0]) {
+ "show" -> {
+ animator?.cancel()
+ updateView(
+ visible = true,
+ location = Point(Integer.parseInt(args[1]), Integer.parseInt(args[2])),
+ fpDetectRunning = true,
+ length = Integer.parseInt(args[3]),
+ thickness = Integer.parseInt(args[4]),
+ rotation = Integer.parseInt(args[5]).toFloat(),
+ )
+ animator =
+ ValueAnimator.ofFloat(0.0f, 1.0f).apply {
+ repeatMode = ValueAnimator.REVERSE
+ repeatCount = ValueAnimator.INFINITE
+ addUpdateListener { view.setProgress(it.animatedValue as Float) }
+ }
+ animator?.start()
+ }
+ "hide" -> {
+ animator?.cancel()
+ updateView(
+ visible = false,
+ location = Point(0, 0),
+ fpDetectRunning = false,
+ length = 0,
+ thickness = 0,
+ rotation = 0.0f,
+ )
+ }
+ }
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar $spfsProgressBarCommand <command>")
+ pw.println("Available commands:")
+ pw.println(" show x y width height rotation")
+ pw.println(" hide")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index fe6a21e..b797c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -44,10 +44,10 @@
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
@@ -76,7 +76,7 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
@@ -99,12 +99,13 @@
private val quickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
displayManager: DisplayManager,
private val windowManager: WindowManager,
+ private val configuration: ConfigurationState,
private val clockController: ClockEventController,
private val clockRegistry: ClockRegistry,
private val broadcastDispatcher: BroadcastDispatcher,
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
private val falsingManager: FalsingManager,
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
@@ -113,9 +114,8 @@
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
- private val keyguardStateController: KeyguardStateController,
+ private val screenOffAnimationController: ScreenOffAnimationController,
private val shadeInteractor: ShadeInteractor,
- private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -341,10 +341,11 @@
KeyguardRootViewBinder.bind(
keyguardRootView,
keyguardRootViewModel,
+ configuration,
featureFlags,
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
- keyguardStateController,
+ screenOffAnimationController,
shadeInteractor,
null, // clock provider only needed for burn in
null, // jank monitor not required for preview mode
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
index f7ab1ee..853f176 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
@@ -22,17 +22,16 @@
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.ProgressBar
-import com.android.systemui.biometrics.Utils
+import androidx.core.view.isGone
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import javax.inject.Inject
private const val TAG = "SideFpsProgressBar"
-const val progressBarHeight = 100
-
@SysUISingleton
class SideFpsProgressBar
@Inject
@@ -40,31 +39,36 @@
private val layoutInflater: LayoutInflater,
private val windowManager: WindowManager,
) {
- private var progressBarWidth = 200
+ private var overlayView: View? = null
+
fun updateView(
visible: Boolean,
- location: Point,
- shouldRotate90Degrees: Boolean,
- progressBarWidth: Int
+ viewLeftTopLocation: Point,
+ progressBarWidth: Int,
+ progressBarHeight: Int,
+ rotation: Float,
) {
if (visible) {
- this.progressBarWidth = progressBarWidth
- createAndShowOverlay(location, shouldRotate90Degrees)
+ createAndShowOverlay(viewLeftTopLocation, rotation, progressBarWidth, progressBarHeight)
} else {
- hideOverlay()
+ hide()
}
}
- fun hideOverlay() {
- overlayView = null
+ fun hide() {
+ progressBar?.isGone = true
}
private val overlayViewParams =
WindowManager.LayoutParams(
- progressBarHeight,
- progressBarWidth,
+ // overlay is always full screen
+ MATCH_PARENT,
+ MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSPARENT
)
.apply {
@@ -78,37 +82,31 @@
WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
}
- private var overlayView: View? = null
- set(value) {
- field?.let { oldView -> windowManager.removeView(oldView) }
- field = value
- field?.let { newView -> windowManager.addView(newView, overlayViewParams) }
- }
-
private fun createAndShowOverlay(
- fingerprintSensorLocation: Point,
- shouldRotate90Degrees: Boolean
+ viewLeftTop: Point,
+ rotation: Float,
+ progressBarLength: Int,
+ progressBarThickness: Int,
) {
if (overlayView == null) {
overlayView = layoutInflater.inflate(R.layout.sidefps_progress_bar, null, false)
+ windowManager.addView(overlayView, overlayViewParams)
+ progressBar?.pivotX = 0.0f
+ progressBar?.pivotY = 0.0f
}
- overlayViewParams.x = fingerprintSensorLocation.x
- overlayViewParams.y = fingerprintSensorLocation.y
- if (shouldRotate90Degrees) {
- overlayView?.rotation = 270.0f
- overlayViewParams.width = progressBarHeight
- overlayViewParams.height = progressBarWidth
- } else {
- overlayView?.rotation = 0.0f
- overlayViewParams.width = progressBarWidth
- overlayViewParams.height = progressBarHeight
- }
- windowManager.updateViewLayout(overlayView, overlayViewParams)
+ progressBar?.layoutParams?.width = progressBarLength
+ progressBar?.layoutParams?.height = progressBarThickness
+ progressBar?.translationX = viewLeftTop.x.toFloat()
+ progressBar?.translationY = viewLeftTop.y.toFloat()
+ progressBar?.rotation = rotation
+ progressBar?.isGone = false
+ overlayView?.requestLayout()
}
fun setProgress(value: Float) {
- overlayView
- ?.findViewById<ProgressBar?>(R.id.side_fps_progress_bar)
- ?.setProgress((value * 100).toInt(), false)
+ progressBar?.setProgress((value * 100).toInt(), false)
}
+
+ private val progressBar: ProgressBar?
+ get() = overlayView?.findViewById(R.id.side_fps_progress_bar)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index b7fe960..0390077 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -31,7 +31,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
-import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
@@ -39,7 +38,6 @@
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -54,9 +52,7 @@
private val featureFlags: FeatureFlagsClassic,
private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
- private val notificationPanelView: NotificationPanelView,
private val notificationIconAreaController: NotificationIconAreaController,
- private val screenOffAnimationController: ScreenOffAnimationController,
) : KeyguardSection() {
private var nicBindingDisposable: DisposableHandle? = null
@@ -97,8 +93,6 @@
configurationState,
configurationController,
dozeParameters,
- featureFlags,
- screenOffAnimationController,
nicAodIconViewStore,
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 1f98082..60f75f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -22,14 +22,28 @@
import android.view.View.VISIBLE
import com.android.app.animation.Interpolators
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.plugins.ClockController
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.AnimatedValue
+import com.android.systemui.util.ui.toAnimatedValueFlow
+import com.android.systemui.util.ui.zip
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,15 +59,21 @@
import kotlinx.coroutines.flow.onStart
@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
class KeyguardRootViewModel
@Inject
constructor(
private val context: Context,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val dozeParameters: DozeParameters,
+ private val featureFlags: FeatureFlagsClassic,
private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val burnInInteractor: BurnInInteractor,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
- private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ screenOffAnimationController: ScreenOffAnimationController,
) {
data class PreviewMode(val isInPreviewMode: Boolean = false)
@@ -65,7 +85,12 @@
*/
private val previewMode = MutableStateFlow(PreviewMode())
- public var clockControllerProvider: Provider<ClockController>? = null
+ var clockControllerProvider: Provider<ClockController>? = null
+
+ /** System insets that keyguard needs to stay out of */
+ var topInset: Int = 0
+ /** Status view top, without translation added in */
+ var statusViewTop: Int = 0
val burnInLayerVisibility: Flow<Int> =
keyguardTransitionInteractor.startedKeyguardState
@@ -102,9 +127,12 @@
scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation),
)
} else {
+ // Ensure the desired translation doesn't encroach on the top inset
+ val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolation).toInt()
+ val translationY = -(statusViewTop - Math.max(topInset, statusViewTop + burnInY))
BurnInModel(
translationX = MathUtils.lerp(0, burnIn.translationX, interpolation).toInt(),
- translationY = MathUtils.lerp(0, burnIn.translationY, interpolation).toInt(),
+ translationY = translationY,
scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation),
scaleClockOnly = true,
)
@@ -166,6 +194,47 @@
}
}
+ /** Is the notification icon container visible? */
+ val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
+ combine(
+ keyguardTransitionInteractor.finishedKeyguardState.map {
+ KeyguardState.lockscreenVisibleInState(it)
+ },
+ deviceEntryInteractor.isBypassEnabled,
+ areNotifsFullyHiddenAnimated(),
+ isPulseExpandingAnimated(),
+ ) {
+ onKeyguard: Boolean,
+ isBypassEnabled: Boolean,
+ notifsFullyHidden: AnimatedValue<Boolean>,
+ pulseExpanding: AnimatedValue<Boolean>,
+ ->
+ when {
+ // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
+ // animation is playing, in which case we want them to be visible if we're
+ // animating in the AOD UI and will be switching to KEYGUARD shortly.
+ !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() ->
+ AnimatedValue.NotAnimating(false)
+ else ->
+ zip(notifsFullyHidden, pulseExpanding) {
+ areNotifsFullyHidden,
+ isPulseExpanding,
+ ->
+ when {
+ // If we're bypassing, then we're visible
+ isBypassEnabled -> true
+ // If we are pulsing (and not bypassing), then we are hidden
+ isPulseExpanding -> false
+ // If notifs are fully gone, then we're visible
+ areNotifsFullyHidden -> true
+ // Otherwise, we're hidden
+ else -> false
+ }
+ }
+ }
+ }
+ .distinctUntilChanged()
+
/**
* Puts this view-model in "preview mode", which means it's being used for UI that is rendering
* the lock screen preview in wallpaper picker / settings and not the real experience on the
@@ -183,4 +252,39 @@
keyguardInteractor.sharedNotificationContainerPosition.value =
SharedNotificationContainerPosition(top, bottom)
}
+
+ /** Is there an expanded pulse, are we animating in response? */
+ private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
+ return notificationsKeyguardInteractor.isPulseExpanding
+ .pairwise(initialValue = null)
+ // If pulsing changes, start animating, unless it's the first emission
+ .map { (prev, expanding) -> AnimatableEvent(expanding, startAnimating = prev != null) }
+ .toAnimatedValueFlow()
+ }
+
+ /** Are notifications completely hidden from view, are we animating in response? */
+ private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> {
+ return notificationsKeyguardInteractor.areNotificationsFullyHidden
+ .pairwise(initialValue = null)
+ .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled ->
+ val animate =
+ when {
+ // Don't animate for the first value
+ prev == null -> false
+ // Always animate if bypass is enabled.
+ bypassEnabled -> true
+ // If we're not bypassing and we're not going to AOD, then we're not
+ // animating.
+ !dozeParameters.alwaysOn -> false
+ // Don't animate when going to AOD if the display needs blanking.
+ dozeParameters.displayNeedsBlanking -> false
+ // We only want the appear animations to happen when the notifications
+ // get fully hidden, since otherwise the un-hide animation overlaps.
+ featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
+ else -> fullyHidden
+ }
+ AnimatableEvent(fullyHidden, animate)
+ }
+ .toAnimatedValueFlow()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index 2c3b431..f8996b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -17,10 +17,12 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.animation.ValueAnimator
+import android.content.Context
import android.graphics.Point
import androidx.core.animation.doOnEnd
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
+import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.isDefaultOrientation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -29,6 +31,7 @@
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -36,6 +39,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -43,6 +47,7 @@
class SideFpsProgressBarViewModel
@Inject
constructor(
+ private val context: Context,
private val fpAuthRepository: DeviceEntryFingerprintAuthRepository,
private val sfpsSensorInteractor: SideFpsSensorInteractor,
displayStateInteractor: DisplayStateInteractor,
@@ -57,24 +62,84 @@
_progress.value = 0.0f
}
+ private val additionalSensorLengthPadding =
+ context.resources.getDimension(R.dimen.sfps_progress_bar_length_extra_padding).toInt()
+
val isVisible: Flow<Boolean> = _visible.asStateFlow()
val progress: Flow<Float> = _progress.asStateFlow()
- val sensorWidth: Flow<Int> = sfpsSensorInteractor.sensorLocation.map { it.width }
+ val progressBarLength: Flow<Int> =
+ sfpsSensorInteractor.sensorLocation
+ .map { it.length + additionalSensorLengthPadding }
+ .distinctUntilChanged()
- val sensorLocation: Flow<Point> =
- sfpsSensorInteractor.sensorLocation.map { Point(it.left, it.top) }
+ val progressBarThickness =
+ context.resources.getDimension(R.dimen.sfps_progress_bar_thickness).toInt()
+
+ val progressBarLocation =
+ combine(displayStateInteractor.currentRotation, sfpsSensorInteractor.sensorLocation, ::Pair)
+ .map { (rotation, sensorLocation) ->
+ val paddingFromEdge =
+ context.resources
+ .getDimension(R.dimen.sfps_progress_bar_padding_from_edge)
+ .toInt()
+ val lengthOfTheProgressBar = sensorLocation.length + additionalSensorLengthPadding
+ val viewLeftTop = Point(sensorLocation.left, sensorLocation.top)
+ val totalDistanceFromTheEdge = paddingFromEdge + progressBarThickness
+
+ val isSensorVerticalNow =
+ sensorLocation.isSensorVerticalInDefaultOrientation ==
+ rotation.isDefaultOrientation()
+ if (isSensorVerticalNow) {
+ // Sensor is vertical to the current orientation, we rotate it 270 deg
+ // around the (left,top) point as the pivot. We need to push it down the
+ // length of the progress bar so that it is still aligned to the sensor
+ viewLeftTop.y += lengthOfTheProgressBar
+ val isSensorOnTheNearEdge =
+ rotation == DisplayRotation.ROTATION_180 ||
+ rotation == DisplayRotation.ROTATION_90
+ if (isSensorOnTheNearEdge) {
+ // Add just the padding from the edge to push the progress bar right
+ viewLeftTop.x += paddingFromEdge
+ } else {
+ // View left top is pushed left from the edge by the progress bar thickness
+ // and the padding.
+ viewLeftTop.x -= totalDistanceFromTheEdge
+ }
+ } else {
+ // Sensor is horizontal to the current orientation.
+ val isSensorOnTheNearEdge =
+ rotation == DisplayRotation.ROTATION_0 ||
+ rotation == DisplayRotation.ROTATION_90
+ if (isSensorOnTheNearEdge) {
+ // Add just the padding from the edge to push the progress bar down
+ viewLeftTop.y += paddingFromEdge
+ } else {
+ // Sensor is now at the bottom edge of the device in the current rotation.
+ // We want to push it up from the bottom edge by the padding and
+ // the thickness of the progressbar.
+ viewLeftTop.y -= totalDistanceFromTheEdge
+ viewLeftTop.x -= additionalSensorLengthPadding
+ }
+ }
+ viewLeftTop
+ }
val isFingerprintAuthRunning: Flow<Boolean> = fpAuthRepository.isRunning
- val shouldRotate90Degrees: Flow<Boolean> =
+ val rotation: Flow<Float> =
combine(displayStateInteractor.currentRotation, sfpsSensorInteractor.sensorLocation, ::Pair)
.map { (rotation, sensorLocation) ->
- if (rotation.isDefaultOrientation()) {
- sensorLocation.isSensorVerticalInDefaultOrientation
+ if (
+ rotation.isDefaultOrientation() ==
+ sensorLocation.isSensorVerticalInDefaultOrientation
+ ) {
+ // We should rotate the progress bar 270 degrees in the clockwise direction with
+ // the left top point as the pivot so that it fills up from bottom to top
+ 270.0f
} else {
- !sensorLocation.isSensorVerticalInDefaultOrientation
+ 0.0f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt
new file mode 100644
index 0000000..919072a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/SideFpsLogger.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 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.log
+
+import android.graphics.Point
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.BouncerLog
+import javax.inject.Inject
+
+private const val TAG = "SideFpsLogger"
+
+/**
+ * Helper class for logging for SFPS related functionality
+ *
+ * To enable logcat echoing for an entire buffer:
+ * ```
+ * adb shell settings put global systemui/buffer/BouncerLog <logLevel>
+ *
+ * ```
+ */
+@SysUISingleton
+class SideFpsLogger @Inject constructor(@BouncerLog private val buffer: LogBuffer) {
+ fun sfpsProgressBarStateChanged(
+ visible: Boolean,
+ location: Point,
+ fpDetectRunning: Boolean,
+ sensorWidth: Int,
+ rotation: Float,
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = visible
+ int1 = location.x
+ int2 = location.y
+ str1 = "$rotation"
+ bool3 = fpDetectRunning
+ long1 = sensorWidth.toLong()
+ },
+ {
+ "SFPS progress bar state changed: visible: $bool1, " +
+ "sensorLocation (x, y): ($int1, $int2), " +
+ "rotation = $str1, " +
+ "fpDetectRunning: $bool3, " +
+ "sensorWidth: $long1"
+ }
+ )
+ }
+
+ fun hidingSfpsIndicator() {
+ buffer.log(TAG, LogLevel.DEBUG, "hiding SFPS indicator to show progress bar")
+ }
+
+ fun showingSfpsIndicator() {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ "Requesting show SFPS indicator because progress bar " +
+ "is being hidden and FP detect is currently running"
+ )
+ }
+
+ fun isProlongedTouchRequiredForAuthenticationChanged(enabled: Boolean) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { bool1 = enabled },
+ { "isProlongedTouchRequiredForAuthentication: $bool1" }
+ )
+ }
+
+ fun sensorLocationStateChanged(
+ pointOnScreenX: Int,
+ pointOnScreenY: Int,
+ sensorLength: Int,
+ isSensorVerticalInDefaultOrientation: Boolean
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = pointOnScreenX
+ int2 = pointOnScreenY
+ str2 = "$sensorLength"
+ bool1 = isSensorVerticalInDefaultOrientation
+ },
+ {
+ "SideFpsSensorLocation state changed: " +
+ "pointOnScreen: ($int1, $int2), " +
+ "sensorLength: $str2, " +
+ "sensorVerticalInDefaultOrientation: $bool1"
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 601aebe..3e8b49d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -56,6 +56,7 @@
import android.util.Log
import android.util.Pair as APair
import androidx.media.utils.MediaConstants
+import com.android.app.tracing.traceSection
import com.android.internal.annotations.Keep
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
@@ -86,7 +87,6 @@
import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
-import com.android.systemui.tracing.traceSection
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index d3bc61b..a252470 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -34,6 +34,7 @@
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.traceSection
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -66,7 +67,6 @@
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.Utils
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index f3d41aa..b1ff708 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -34,8 +34,11 @@
import android.view.ViewGroupOverlay
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
+import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -53,10 +56,11 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
private val TAG: String = MediaHierarchyManager::class.java.simpleName
@@ -96,11 +100,13 @@
private val mediaManager: MediaDataManager,
private val keyguardViewController: KeyguardViewController,
private val dreamOverlayStateController: DreamOverlayStateController,
+ private val communalInteractor: CommunalInteractor,
configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
panelEventsEvents: ShadeStateEvents,
private val secureSettings: SecureSettings,
@Main private val handler: Handler,
+ @Application private val coroutineScope: CoroutineScope,
private val splitShadeStateController: SplitShadeStateController,
private val logger: MediaViewLogger,
) {
@@ -209,7 +215,7 @@
else result.setIntersect(animationStartClipping, targetClipping)
}
- private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_DREAM_OVERLAY + 1)
+ private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_COMMUNAL_HUB + 1)
/**
* The last location where this view was at before going to the desired location. This is useful
* for guided transitions.
@@ -401,6 +407,9 @@
}
}
+ /** Is the communal UI showing */
+ private var isCommunalShowing: Boolean = false
+
/**
* The current cross fade progress. 0.5f means it's just switching between the start and the end
* location and the content is fully faded, while 0.75f means that we're halfway faded in again
@@ -563,6 +572,14 @@
settingsObserver,
UserHandle.USER_ALL
)
+
+ // Listen to the communal UI state.
+ coroutineScope.launch {
+ communalInteractor.isCommunalShowing.collect { value ->
+ isCommunalShowing = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
}
private fun updateConfiguration() {
@@ -1115,6 +1132,9 @@
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
+ // TODO(b/308813166): revisit logic once interactions between the hub and
+ // shade/keyguard state are finalized
+ isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB
onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
}
@@ -1224,6 +1244,9 @@
/** Attached on the dream overlay */
const val LOCATION_DREAM_OVERLAY = 3
+ /** Attached to a view in the communal UI grid */
+ const val LOCATION_COMMUNAL_HUB = 4
+
/** Attached at the root of the hierarchy in an overlay */
const val IN_OVERLAY = -1000
@@ -1261,7 +1284,8 @@
MediaHierarchyManager.LOCATION_QS,
MediaHierarchyManager.LOCATION_QQS,
MediaHierarchyManager.LOCATION_LOCKSCREEN,
- MediaHierarchyManager.LOCATION_DREAM_OVERLAY
+ MediaHierarchyManager.LOCATION_DREAM_OVERLAY,
+ MediaHierarchyManager.LOCATION_COMMUNAL_HUB
]
)
@Retention(AnnotationRetention.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
index 0129c49..1f711cf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
@@ -16,8 +16,8 @@
package com.android.systemui.media.controls.ui
+import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.animation.MeasurementOutput
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index 6b82746..1ec43c5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -20,6 +20,7 @@
import android.content.res.Configuration
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.app.tracing.traceSection
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
@@ -27,7 +28,6 @@
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tracing.traceSection
import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index 20ea60f..16a703a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -20,11 +20,11 @@
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.media.controls.ui.MediaLocation
+import com.android.systemui.res.R
import java.lang.IllegalArgumentException
import javax.inject.Inject
@@ -154,6 +154,8 @@
MediaUiEvent.MEDIA_CAROUSEL_LOCATION_LOCKSCREEN
MediaHierarchyManager.LOCATION_DREAM_OVERLAY ->
MediaUiEvent.MEDIA_CAROUSEL_LOCATION_DREAM
+ MediaHierarchyManager.LOCATION_COMMUNAL_HUB ->
+ MediaUiEvent.MEDIA_CAROUSEL_LOCATION_COMMUNAL
else -> throw IllegalArgumentException("Unknown media carousel location $location")
}
logger.log(event)
@@ -276,6 +278,8 @@
MEDIA_CAROUSEL_LOCATION_LOCKSCREEN(1039),
@UiEvent(doc = "The media carousel moved to the dream state")
MEDIA_CAROUSEL_LOCATION_DREAM(1040),
+ @UiEvent(doc = "The media carousel moved to the communal hub UI")
+ MEDIA_CAROUSEL_LOCATION_COMMUNAL(1520),
@UiEvent(doc = "A media recommendation card was added to the media carousel")
MEDIA_RECOMMENDATION_ADDED(1041),
@UiEvent(doc = "A media recommendation card was removed from the media carousel")
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 888cd0b..8f752e5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -46,6 +46,7 @@
String QUICK_QS_PANEL = "media_quick_qs_panel";
String KEYGUARD = "media_keyguard";
String DREAM = "dream";
+ String COMMUNAL_HUB = "communal_Hub";
/** */
@Provides
@@ -87,6 +88,16 @@
return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
}
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(COMMUNAL_HUB)
+ static MediaHost providesCommunalMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+ MediaHostStatesManager statesManager) {
+ return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+ }
+
/** Provides a logging buffer related to the media tap-to-transfer chip on the sender device. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
index 97ec654..6e3b7b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
@@ -31,7 +31,7 @@
private val logger: MediaMuteAwaitLogger,
@Main private val mainExecutor: Executor
) {
- private val deviceIconUtil = DeviceIconUtil()
+ private val deviceIconUtil = DeviceIconUtil(context)
/** Creates a [MediaMuteAwaitConnectionManager]. */
fun create(localMediaManager: LocalMediaManager): MediaMuteAwaitConnectionManager {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 8103152..13271c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -21,8 +21,8 @@
import android.os.Looper
import android.provider.Settings
import android.view.View
+import android.widget.Switch
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -34,6 +34,7 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
@@ -96,6 +97,7 @@
override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
+ state.expandedAccessibilityClassName = Switch::class.java.name
model.applyTo(state, mContext)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
new file mode 100644
index 0000000..dfeb65b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.data.entity
+
+import android.graphics.drawable.Icon
+
+sealed interface CustomTileDefaults {
+
+ data object Error : CustomTileDefaults
+ data class Result(
+ val icon: Icon,
+ val label: CharSequence,
+ ) : CustomTileDefaults
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
new file mode 100644
index 0000000..1546ec2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.data.repository
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.withContext
+
+/** Gets a label and an icon for a custom tile based on its package. */
+interface CustomTileDefaultsRepository {
+
+ /**
+ * Returns [CustomTileDefaults] for a specified [user]. An updated value may be emitted as a
+ * response for [requestNewDefaults].
+ *
+ * @see requestNewDefaults
+ */
+ fun defaults(user: UserHandle): Flow<CustomTileDefaults>
+
+ /**
+ * Requests the new default from the [PackageManager]. The result is cached until the input of
+ * this method changes or [force] == true is passed.
+ *
+ * Listen to [defaults] to get the loaded result
+ */
+ fun requestNewDefaults(
+ user: UserHandle,
+ componentName: ComponentName,
+ force: Boolean = false,
+ )
+}
+
+@QSTileScope
+class CustomTileDefaultsRepositoryImpl
+@Inject
+constructor(
+ private val context: Context,
+ @Application applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : CustomTileDefaultsRepository {
+
+ private val defaultsRequests =
+ MutableSharedFlow<DefaultsRequest>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+
+ private val defaults: SharedFlow<DefaultsResult> =
+ defaultsRequests
+ .distinctUntilChanged { old, new ->
+ if (new.force) {
+ // force update should always pass
+ false
+ } else {
+ old == new
+ }
+ }
+ .map { DefaultsResult(it.user, loadDefaults(it.user, it.componentName)) }
+ .shareIn(applicationScope, SharingStarted.WhileSubscribed(), replay = 1)
+
+ override fun defaults(user: UserHandle): Flow<CustomTileDefaults> =
+ defaults.filter { it.user == user }.map { it.data }
+
+ override fun requestNewDefaults(
+ user: UserHandle,
+ componentName: ComponentName,
+ force: Boolean,
+ ) {
+ defaultsRequests.tryEmit(DefaultsRequest(user, componentName, force))
+ }
+
+ private suspend fun loadDefaults(
+ user: UserHandle,
+ componentName: ComponentName
+ ): CustomTileDefaults =
+ withContext(backgroundDispatcher) {
+ try {
+ val userContext = context.createContextAsUser(user, 0)
+ val info = componentName.getServiceInfo(userContext.packageManager)
+
+ val iconRes = if (info.icon == NO_ICON_RES) info.applicationInfo.icon else info.icon
+ if (iconRes == NO_ICON_RES) {
+ return@withContext CustomTileDefaults.Error
+ }
+
+ CustomTileDefaults.Result(
+ Icon.createWithResource(componentName.packageName, iconRes),
+ info.loadLabel(userContext.packageManager)
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ CustomTileDefaults.Error
+ }
+ }
+
+ private fun ComponentName.getServiceInfo(
+ packageManager: PackageManager,
+ ): ServiceInfo {
+ val isSystemApp = packageManager.getApplicationInfo(packageName, 0).isSystemApp
+ var flags =
+ (PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE)
+ if (isSystemApp) {
+ flags = flags or PackageManager.MATCH_DISABLED_COMPONENTS
+ }
+ return packageManager.getServiceInfo(this, flags)
+ }
+
+ private data class DefaultsRequest(
+ val user: UserHandle,
+ val componentName: ComponentName,
+ val force: Boolean = false,
+ )
+
+ private data class DefaultsResult(val user: UserHandle, val data: CustomTileDefaults)
+
+ private companion object {
+
+ const val NO_ICON_RES = 0
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
new file mode 100644
index 0000000..6d7d88f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundScope
+import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileUser
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+interface CustomTilePackageUpdatesRepository {
+
+ val packageChanges: Flow<Unit>
+}
+
+@CustomTileBoundScope
+class CustomTilePackageUpdatesRepositoryImpl
+@Inject
+constructor(
+ tileSpec: TileSpec.CustomTileSpec,
+ @CustomTileUser user: UserHandle,
+ serviceManager: TileServiceManager,
+ defaultsRepository: CustomTileDefaultsRepository,
+ @CustomTileBoundScope boundScope: CoroutineScope,
+) : CustomTilePackageUpdatesRepository {
+
+ override val packageChanges: Flow<Unit> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ serviceManager.setTileChangeListener { changedComponentName ->
+ if (changedComponentName == tileSpec.componentName) {
+ trySend(Unit)
+ }
+ }
+
+ awaitClose { serviceManager.setTileChangeListener(null) }
+ }
+ .onEach { defaultsRepository.requestNewDefaults(user, tileSpec.componentName, true) }
+ .shareIn(boundScope, SharingStarted.WhileSubscribed())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index ccff8af..482bf9b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -23,6 +23,8 @@
import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
import dagger.Binds
import dagger.Module
@@ -43,4 +45,9 @@
@Binds
fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData>
+
+ @Binds
+ fun bindCustomTileDefaultsRepository(
+ impl: CustomTileDefaultsRepositoryImpl
+ ): CustomTileDefaultsRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
index e33b3e9..d382d20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
@@ -23,7 +23,7 @@
/** @see CustomTileBoundScope */
@CustomTileBoundScope
-@Subcomponent
+@Subcomponent(modules = [CustomTileBoundModule::class])
interface CustomTileBoundComponent {
@Subcomponent.Builder
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
new file mode 100644
index 0000000..889424a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.di.bound
+
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CustomTileBoundModule {
+
+ @Binds
+ fun bindCustomTilePackageUpdatesRepository(
+ impl: CustomTilePackageUpdatesRepositoryImpl
+ ): CustomTilePackageUpdatesRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 1416c10..a950539 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -29,12 +29,12 @@
import android.view.RemoteAnimationTarget
import android.view.WindowManager
import android.view.WindowManagerGlobal
+import com.android.app.tracing.TraceUtils.Companion.launch
import com.android.internal.infra.ServiceConnector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.tracing.TraceUtils.Companion.launch
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index 8b3548b..f56f416 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -20,7 +20,7 @@
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.TraceUtils.Companion.launch
import kotlinx.coroutines.CoroutineScope
import java.util.function.Consumer
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index c6b2cf5..713ede6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -25,7 +25,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import com.android.systemui.tracing.TraceUtils.Companion.launch
+import com.android.app.tracing.TraceUtils.Companion.launch
import kotlinx.coroutines.withContext
/** Provides state from the main SystemUI process on behalf of the Screenshot process. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 385c813..38d00f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -18,9 +18,9 @@
import android.media.MediaPlayer
import android.util.Log
+import com.android.app.tracing.TraceUtils.Companion.async
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.tracing.TraceUtils.Companion.async
import com.google.errorprone.annotations.CanIgnoreReturnValue
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index ccac533..f6c25e0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -5,6 +5,7 @@
import android.util.Log
import android.view.Display
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import com.android.app.tracing.TraceUtils.Companion.launch
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.dagger.SysUISingleton
@@ -13,7 +14,6 @@
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
-import com.android.systemui.tracing.TraceUtils.Companion.launch
import java.util.function.Consumer
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index cc59f87..dfe6adc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1070,7 +1070,6 @@
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
mQsController.init();
- mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
mShadeHeadsUpTracker.addTrackingHeadsUpListener(
mNotificationStackScrollLayoutController::setTrackingHeadsUp);
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
@@ -4219,7 +4218,7 @@
return mShadeExpansionStateManager;
}
- private void onQsExpansionChanged(boolean expanded) {
+ void onQsExpansionChanged(boolean expanded) {
updateExpandedHeightToMaxHeight();
setStatusAccessibilityImportance(expanded
? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 0426388..5114826 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -156,7 +156,6 @@
KeyguardStateController keyguardStateController,
ScreenOffAnimationController screenOffAnimationController,
AuthController authController,
- ShadeExpansionStateManager shadeExpansionStateManager,
Lazy<ShadeInteractor> shadeInteractorLazy,
ShadeWindowLogger logger,
Lazy<SelectedUserInteractor> userInteractor) {
@@ -185,7 +184,6 @@
.addCallback(mStateListener,
SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
configurationController.addCallback(this);
- shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
float desiredPreferredRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
@@ -303,6 +301,11 @@
mShadeInteractorLazy.get().isAnyExpanded(),
this::onShadeOrQsExpanded
);
+ collectFlow(
+ mWindowRootView,
+ mShadeInteractorLazy.get().isQsExpanded(),
+ this::onQsExpansionChanged
+ );
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index a2627ed..a2ca49d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -113,6 +113,7 @@
private final SysUIKeyEventHandler mSysUIKeyEventHandler;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
+ private final QuickSettingsController mQuickSettingsController;
private GestureDetector mPulsingWakeupGestureHandler;
private GestureDetector mDreamingWakeupGestureHandler;
private View mBrightnessMirror;
@@ -188,6 +189,7 @@
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
SysUIKeyEventHandler sysUIKeyEventHandler,
+ QuickSettingsController quickSettingsController,
PrimaryBouncerInteractor primaryBouncerInteractor,
AlternateBouncerInteractor alternateBouncerInteractor,
SelectedUserInteractor selectedUserInteractor) {
@@ -220,6 +222,7 @@
mSysUIKeyEventHandler = sysUIKeyEventHandler;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mQuickSettingsController = quickSettingsController;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -454,6 +457,16 @@
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
+ if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
+ // When on lockscreen, if the touch originates at the top of the screen
+ // go directly to QS and not the shade
+ if (mQuickSettingsController.shouldQuickSettingsIntercept(
+ ev.getX(), ev.getY(), 0)) {
+ mShadeLogger.d("NSWVC: QS intercepted");
+ return true;
+ }
+ }
+
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index cc46b23..866cfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -26,23 +26,27 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.res.R
+import androidx.lifecycle.lifecycleScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.fragments.FragmentService
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.ViewController
import com.android.systemui.util.concurrency.DelayableExecutor
+import kotlinx.coroutines.launch
import java.util.function.Consumer
import javax.inject.Inject
import kotlin.reflect.KMutableProperty0
@@ -56,7 +60,7 @@
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
private val shadeHeaderController: ShadeHeaderController,
- private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeInteractor: ShadeInteractor,
private val fragmentService: FragmentService,
@Main private val delayableExecutor: DelayableExecutor,
private val featureFlags: FeatureFlags,
@@ -65,7 +69,6 @@
private val splitShadeStateController: SplitShadeStateController
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
- private var qsExpanded = false
private var splitShadeEnabled = false
private var isQSDetailShowing = false
private var isQSCustomizing = false
@@ -89,13 +92,6 @@
taskbarVisible = visible
}
}
- private val shadeQsExpansionListener: ShadeQsExpansionListener =
- ShadeQsExpansionListener { isQsExpanded ->
- if (qsExpanded != isQsExpanded) {
- qsExpanded = isQsExpanded
- mView.invalidate()
- }
- }
// With certain configuration changes (like light/dark changes), the nav bar will disappear
// for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value
@@ -122,6 +118,11 @@
}
override fun onInit() {
+ mView.repeatWhenAttached {
+ lifecycleScope.launch {
+ shadeInteractor.isQsExpanded.collect{ _ -> mView.invalidate() }
+ }
+ }
val currentMode: Int = navigationModeController.addListener { mode: Int ->
isGestureNavigation = QuickStepContract.isGesturalMode(mode)
}
@@ -137,7 +138,6 @@
public override fun onViewAttached() {
updateResources()
overviewProxyService.addCallback(taskbarVisibilityListener)
- shadeExpansionStateManager.addQsExpansionListener(shadeQsExpansionListener)
mView.setInsetsChangedListener(delayedInsetSetter)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
mView.setConfigurationChangedListener { updateResources() }
@@ -146,7 +146,6 @@
override fun onViewDetached() {
overviewProxyService.removeCallback(taskbarVisibilityListener)
- shadeExpansionStateManager.removeQsExpansionListener(shadeQsExpansionListener)
mView.removeOnInsetsChangedListener()
mView.removeQSFragmentAttachedListener()
mView.setConfigurationChangedListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 0ec7a36..d73fa14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -207,12 +207,6 @@
/** pointerId of the pointer we're currently tracking */
private int mTrackingPointer;
- /**
- * Indicates that QS is in expanded state which can happen by:
- * - single pane shade: expanding shade and then expanding QS
- * - split shade: just expanding shade (QS are expanded automatically)
- */
- private boolean mExpanded;
/** Indicates QS is at its max height */
private boolean mFullyExpanded;
/**
@@ -594,9 +588,8 @@
return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
}
-
public boolean getExpanded() {
- return mExpanded;
+ return mShadeRepository.getLegacyIsQsExpanded().getValue();
}
@VisibleForTesting
@@ -613,7 +606,7 @@
// close the whole shade with one motion. Also this will be always true when closing
// split shade as there QS are always expanded so every collapsing motion is motion from
// expanded QS to closed panel
- return mExpandImmediate || (mExpanded
+ return mExpandImmediate || (getExpanded()
&& !isTracking() && !isExpansionAnimating()
&& !mExpansionFromOverscroll);
}
@@ -778,11 +771,11 @@
@VisibleForTesting
void setExpanded(boolean expanded) {
- boolean changed = mExpanded != expanded;
+ boolean changed = getExpanded() != expanded;
if (changed) {
- mExpanded = expanded;
+ mShadeRepository.setLegacyIsQsExpanded(expanded);
updateQsState();
- mShadeExpansionStateManager.onQsExpansionChanged(expanded);
+ mPanelViewControllerLazy.get().onQsExpansionChanged(expanded);
mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded,
getMinExpansionHeight(), getMaxExpansionHeight(),
mStackScrollerOverscrolling, mAnimatorExpand, mAnimating);
@@ -846,7 +839,7 @@
/** Called when Shade view layout changed. Updates QS expansion or
* starts size change animation if height has changed. */
void handleShadeLayoutChanged(int oldMaxHeight) {
- if (mExpanded && mFullyExpanded) {
+ if (getExpanded() && mFullyExpanded) {
mExpansionHeight = mMaxExpansionHeight;
if (mExpansionHeightSetToMaxListener != null) {
mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
@@ -988,24 +981,24 @@
}
void updateQsState() {
- boolean qsFullScreen = mExpanded && !mSplitShadeEnabled;
+ boolean qsFullScreen = getExpanded() && !mSplitShadeEnabled;
mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
mNotificationStackScrollLayoutController.setScrollingEnabled(
mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll));
if (mQsStateUpdateListener != null) {
- mQsStateUpdateListener.onQsStateUpdated(mExpanded, mStackScrollerOverscrolling);
+ mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling);
}
if (mQs == null) return;
- mQs.setExpanded(mExpanded);
+ mQs.setExpanded(getExpanded());
}
/** update expanded state of QS */
public void updateExpansion() {
if (mQs == null) return;
final float squishiness;
- if ((mExpandImmediate || mExpanded) && !mSplitShadeEnabled) {
+ if ((mExpandImmediate || getExpanded()) && !mSplitShadeEnabled) {
squishiness = 1;
} else if (mTransitioningToFullShadeProgress > 0.0f) {
squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction();
@@ -1125,7 +1118,7 @@
mMediaHierarchyManager.setCollapsingShadeFromQS(mExpandedWhenExpandingStarted
/* We also start expanding when flinging closed Qs. Let's exclude that */
&& !mAnimating);
- if (mExpanded) {
+ if (getExpanded()) {
onExpansionStarted();
}
// Since there are QS tiles in the header now, we need to make sure we start listening
@@ -1234,7 +1227,7 @@
Math.min(top / (float) mScrimCornerRadius, 1f));
float bottomRadius = mSplitShadeEnabled ? screenCornerRadius : 0;
- if (!mExpanded) {
+ if (!getExpanded()) {
bottomRadius = calculateBottomCornerRadius(bottomRadius);
}
mScrimController.setNotificationBottomRadius(bottomRadius);
@@ -1547,7 +1540,7 @@
@VisibleForTesting
void onHeightChanged() {
mMaxExpansionHeight = isQsFragmentCreated() ? mQs.getDesiredHeight() : 0;
- if (mExpanded && mFullyExpanded) {
+ if (getExpanded() && mFullyExpanded) {
mExpansionHeight = mMaxExpansionHeight;
if (mExpansionHeightSetToMaxListener != null) {
mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
@@ -2083,7 +2076,7 @@
ipw.print("mTrackingPointer=");
ipw.println(mTrackingPointer);
ipw.print("mExpanded=");
- ipw.println(mExpanded);
+ ipw.println(getExpanded());
ipw.print("mFullyExpanded=");
ipw.println(mFullyExpanded);
ipw.print("mExpandImmediate=");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 9493989..fca98f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -36,15 +36,12 @@
class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
- private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
@PanelState private var state: Int = STATE_CLOSED
@FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
private var expanded: Boolean = false
- private var qsExpanded: Boolean = false
- private var qsExpansionFraction = 0f
private var tracking: Boolean = false
private var dragDownPxAmount: Float = 0f
@@ -64,15 +61,6 @@
expansionListeners.remove(listener)
}
- fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
- qsExpansionListeners.add(listener)
- listener.onQsExpansionChanged(qsExpanded)
- }
-
- fun removeQsExpansionListener(listener: ShadeQsExpansionListener) {
- qsExpansionListeners.remove(listener)
- }
-
/** Adds a listener that will be notified when the panel state has changed. */
fun addStateListener(listener: ShadeStateListener) {
stateListeners.add(listener)
@@ -153,14 +141,6 @@
expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) }
}
- /** Called when the quick settings expansion changes to fully expanded or collapsed. */
- fun onQsExpansionChanged(qsExpanded: Boolean) {
- this.qsExpanded = qsExpanded
-
- debugLog("qsExpanded=$qsExpanded")
- qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
- }
-
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 024c8e3..e2e4556 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -83,6 +83,17 @@
val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean>
/**
+ * QuickSettingsController.mExpanded as a flow. Indicates that QS is in expanded state:
+ * - single pane shade: expanding shade and then expanding QS
+ * - split shade: just expanding shade (QS are expanded automatically)
+ */
+ @Deprecated("Use ShadeInteractor instead") val legacyIsQsExpanded: StateFlow<Boolean>
+
+ /** Sets whether QS is expanded. */
+ @Deprecated("Use ShadeInteractor instead")
+ fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean)
+
+ /**
* Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input
* transfer from the status bar, home screen, or trackpad.
*/
@@ -175,6 +186,15 @@
override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> =
_legacyExpandedOrAwaitingInputTransfer.asStateFlow()
+ private val _legacyIsQsExpanded = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead")
+ override val legacyIsQsExpanded: StateFlow<Boolean> = _legacyIsQsExpanded.asStateFlow()
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
+ _legacyIsQsExpanded.value = legacyIsQsExpanded
+ }
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyExpandedOrAwaitingInputTransfer(
legacyExpandedOrAwaitingInputTransfer: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index f043c71..a4c4503 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -121,16 +121,37 @@
/**
* The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
- * report 0f.
+ * report 0f. If split shade is enabled, value matches shadeExpansion.
*/
val qsExpansion: StateFlow<Float> =
if (sceneContainerFlags.isEnabled()) {
- sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+ val qsExp = sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+ combine(isSplitShadeEnabled, shadeExpansion, qsExp) {
+ isSplitShadeEnabled,
+ shadeExp,
+ qsExp ->
+ if (isSplitShadeEnabled) {
+ shadeExp
+ } else {
+ qsExp
+ }
+ }
.stateIn(scope, SharingStarted.Eagerly, 0f)
} else {
repository.qsExpansion
}
+ /** Whether Quick Settings is expanded a non-zero amount. */
+ val isQsExpanded: StateFlow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ qsExpansion
+ .map { it > 0 }
+ .distinctUntilChanged()
+ .stateIn(scope, SharingStarted.Eagerly, false)
+ } else {
+ repository.legacyIsQsExpanded
+ }
+
/** The amount [0-1] either QS or the shade has been opened. */
val anyExpansion: StateFlow<Float> =
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 4ea7026..e19fcd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -32,17 +32,15 @@
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,13 +62,11 @@
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController,
private val headsUpManager: HeadsUpManager,
- private val roundnessManager: NotificationRoundnessManager,
configurationController: ConfigurationController,
private val statusBarStateController: StatusBarStateController,
private val falsingManager: FalsingManager,
- shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeInteractor: ShadeInteractor,
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
- private val falsingCollector: FalsingCollector,
dumpManager: DumpManager
) : Gefingerpoken, Dumpable {
companion object {
@@ -115,7 +111,6 @@
private val isFalseTouch: Boolean
get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
- var qsExpanded: Boolean = false
var pulseExpandAbortListener: Runnable? = null
var bouncerShowing: Boolean = false
@@ -127,12 +122,6 @@
}
})
- shadeExpansionStateManager.addQsExpansionListener { isQsExpanded ->
- if (qsExpanded != isQsExpanded) {
- qsExpanded = isQsExpanded
- }
- }
-
mPowerManager = context.getSystemService(PowerManager::class.java)
dumpManager.registerDumpable(this)
}
@@ -148,7 +137,8 @@
}
private fun canHandleMotionEvent(): Boolean {
- return wakeUpCoordinator.canShowPulsingHuns && !qsExpanded && !bouncerShowing
+ return wakeUpCoordinator.canShowPulsingHuns && !shadeInteractor.isQsExpanded.value &&
+ !bouncerShowing
}
private fun startExpansion(event: MotionEvent): Boolean {
@@ -379,7 +369,6 @@
it.println("isExpanding: $isExpanding")
it.println("leavingLockscreen: $leavingLockscreen")
it.println("mPulsing: $mPulsing")
- it.println("qsExpanded: $qsExpanded")
it.println("bouncerShowing: $bouncerShowing")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 5af7cfc..a36d36c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.events
-import androidx.core.animation.Animator
import android.annotation.UiThread
import android.graphics.Point
import android.graphics.Rect
@@ -24,13 +23,15 @@
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
-import com.android.internal.annotations.GuardedBy
-import com.android.systemui.res.R
+import androidx.core.animation.Animator
import com.android.app.animation.Interpolators
+import com.android.internal.annotations.GuardedBy
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
@@ -43,6 +44,8 @@
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -64,11 +67,12 @@
@SysUISingleton
open class PrivacyDotViewController @Inject constructor(
@Main private val mainExecutor: Executor,
+ @Application scope: CoroutineScope,
private val stateController: StatusBarStateController,
private val configurationController: ConfigurationController,
private val contentInsetsProvider: StatusBarContentInsetsProvider,
private val animationScheduler: SystemStatusAnimationScheduler,
- shadeExpansionStateManager: ShadeExpansionStateManager
+ shadeInteractor: ShadeInteractor?
) {
private lateinit var tl: View
private lateinit var tr: View
@@ -135,10 +139,12 @@
}
})
- shadeExpansionStateManager.addQsExpansionListener { isQsExpanded ->
- dlog("setQsExpanded $isQsExpanded")
- synchronized(lock) {
- nextViewState = nextViewState.copy(qsExpanded = isQsExpanded)
+ scope.launch {
+ shadeInteractor?.isQsExpanded?.collect { isQsExpanded ->
+ dlog("setQsExpanded $isQsExpanded")
+ synchronized(lock) {
+ nextViewState = nextViewState.copy(qsExpanded = isQsExpanded)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
index 84796f9..ed96482 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
@@ -17,19 +17,11 @@
package com.android.systemui.statusbar.events
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
+import dagger.Binds
import dagger.Module
import dagger.Provides
-import kotlinx.coroutines.CoroutineScope
@Module
interface StatusBarEventsModule {
@@ -42,41 +34,11 @@
fun provideSystemStatusAnimationSchedulerLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("SystemStatusAnimationSchedulerLog", 60)
}
-
- @Provides
- @SysUISingleton
- fun provideSystemStatusAnimationScheduler(
- featureFlags: FeatureFlags,
- coordinator: SystemEventCoordinator,
- chipAnimationController: SystemEventChipAnimationController,
- statusBarWindowController: StatusBarWindowController,
- dumpManager: DumpManager,
- systemClock: SystemClock,
- @Application coroutineScope: CoroutineScope,
- @Main executor: DelayableExecutor,
- logger: SystemStatusAnimationSchedulerLogger
- ): SystemStatusAnimationScheduler {
- return if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
- SystemStatusAnimationSchedulerImpl(
- coordinator,
- chipAnimationController,
- statusBarWindowController,
- dumpManager,
- systemClock,
- coroutineScope,
- logger
- )
- } else {
- SystemStatusAnimationSchedulerLegacyImpl(
- coordinator,
- chipAnimationController,
- statusBarWindowController,
- dumpManager,
- systemClock,
- executor
- )
- }
- }
}
-}
+ @Binds
+ @SysUISingleton
+ fun bindSystemStatusAnimationScheduler(
+ systemStatusAnimationSchedulerImpl: SystemStatusAnimationSchedulerImpl
+ ): SystemStatusAnimationScheduler
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 73c0bfe..fec1765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -32,8 +32,6 @@
import androidx.core.animation.ValueAnimator
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
@@ -47,8 +45,7 @@
class SystemEventChipAnimationController @Inject constructor(
private val context: Context,
private val statusBarWindowController: StatusBarWindowController,
- private val contentInsetsProvider: StatusBarContentInsetsProvider,
- private val featureFlags: FeatureFlags,
+ private val contentInsetsProvider: StatusBarContentInsetsProvider
) : SystemStatusAnimationCallback {
private lateinit var animationWindowView: FrameLayout
@@ -317,15 +314,8 @@
it.marginEnd = marginEnd
}
- private fun initializeAnimRect() = if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
- animRect.set(chipBounds)
- } else {
- animRect.set(
- chipLeft,
- currentAnimatedView!!.view.top,
- chipRight,
- currentAnimatedView!!.view.bottom)
- }
+ private fun initializeAnimRect() = animRect.set(chipBounds)
+
/**
* To be called during an animation, sets the width and updates the current animated chip view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index e9d5dec..a73d517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -24,8 +24,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
import com.android.systemui.privacy.PrivacyChipBuilder
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
@@ -49,7 +48,6 @@
private val batteryController: BatteryController,
private val privacyController: PrivacyItemController,
private val context: Context,
- private val featureFlags: FeatureFlags,
@Application private val appScope: CoroutineScope,
connectedDisplayInteractor: ConnectedDisplayInteractor
) {
@@ -76,9 +74,7 @@
}
fun notifyPluggedIn(@IntRange(from = 0, to = 100) batteryLevel: Int) {
- if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
- scheduler.onStatusEvent(BatteryEvent(batteryLevel))
- }
+ scheduler.onStatusEvent(BatteryEvent(batteryLevel))
}
fun notifyPrivacyItemsEmpty() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
deleted file mode 100644
index 6b5a548..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
+++ /dev/null
@@ -1,315 +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.events
-
-import android.os.Process
-import android.provider.DeviceConfig
-import android.util.Log
-import androidx.core.animation.Animator
-import androidx.core.animation.AnimatorListenerAdapter
-import androidx.core.animation.AnimatorSet
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.util.Assert
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import java.io.PrintWriter
-import javax.inject.Inject
-
-/**
- * Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
- * ```
- * - Avoiding log spam by only allowing 12 events per minute (1event/5s)
- * - Waits 100ms to schedule any event for debouncing/prioritization
- * - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent])
- * - Only schedules a single event, and throws away lowest priority events
- * ```
- *
- * There are 4 basic stages of animation at play here:
- * ```
- * 1. System chrome animation OUT
- * 2. Chip animation IN
- * 3. Chip animation OUT; potentially into a dot
- * 4. System chrome animation IN
- * ```
- *
- * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system
- * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize
- * their respective views based on the progress of the animator. Interpolation differences TBD
- */
-open class SystemStatusAnimationSchedulerLegacyImpl
-@Inject
-constructor(
- private val coordinator: SystemEventCoordinator,
- private val chipAnimationController: SystemEventChipAnimationController,
- private val statusBarWindowController: StatusBarWindowController,
- private val dumpManager: DumpManager,
- private val systemClock: SystemClock,
- @Main private val executor: DelayableExecutor
-) : SystemStatusAnimationScheduler {
-
- companion object {
- private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
- }
-
- fun isImmersiveIndicatorEnabled(): Boolean {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_ENABLE_IMMERSIVE_INDICATOR,
- true
- )
- }
-
- @SystemAnimationState private var animationState: Int = IDLE
-
- /** True if the persistent privacy dot should be active */
- var hasPersistentDot = false
- protected set
-
- private var scheduledEvent: StatusEvent? = null
-
- val listeners = mutableSetOf<SystemStatusAnimationCallback>()
-
- init {
- coordinator.attachScheduler(this)
- dumpManager.registerDumpable(TAG, this)
- }
-
- @SystemAnimationState override fun getAnimationState() = animationState
-
- override fun onStatusEvent(event: StatusEvent) {
- // Ignore any updates until the system is up and running. However, for important events that
- // request to be force visible (like privacy), ignore whether it's too early.
- if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
- return
- }
-
- // Don't deal with threading for now (no need let's be honest)
- Assert.isMainThread()
- if (
- (event.priority > (scheduledEvent?.priority ?: -1)) &&
- animationState != ANIMATING_OUT &&
- animationState != SHOWING_PERSISTENT_DOT
- ) {
- // events can only be scheduled if a higher priority or no other event is in progress
- if (DEBUG) {
- Log.d(TAG, "scheduling event $event")
- }
-
- scheduleEvent(event)
- } else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) {
- if (DEBUG) {
- Log.d(TAG, "updating current event from: $event. animationState=$animationState")
- }
- scheduledEvent?.updateFromEvent(event)
- if (event.forceVisible) {
- hasPersistentDot = true
- // If we missed the chance to show the persistent dot, do it now
- if (animationState == IDLE) {
- notifyTransitionToPersistentDot()
- }
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "ignoring event $event")
- }
- }
- }
-
- override fun removePersistentDot() {
- if (!hasPersistentDot || !isImmersiveIndicatorEnabled()) {
- return
- }
-
- hasPersistentDot = false
- notifyHidePersistentDot()
- return
- }
-
- fun isTooEarly(): Boolean {
- return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
- }
-
- /** Clear the scheduled event (if any) and schedule a new one */
- private fun scheduleEvent(event: StatusEvent) {
- scheduledEvent = event
-
- if (event.forceVisible) {
- hasPersistentDot = true
- }
-
- // If animations are turned off, we'll transition directly to the dot
- if (!event.showAnimation && event.forceVisible) {
- notifyTransitionToPersistentDot()
- scheduledEvent = null
- return
- }
-
- chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator)
- animationState = ANIMATION_QUEUED
- executor.executeDelayed({ runChipAnimation() }, DEBOUNCE_DELAY)
- }
-
- /**
- * 1. Define a total budget for the chip animation (1500ms)
- * 2. Send out callbacks to listeners so that they can generate animations locally
- * 3. Update the scheduler state so that clients know where we are
- * 4. Maybe: provide scaffolding such as: dot location, margins, etc
- * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we
- * collect all of the animators and run them together.
- */
- private fun runChipAnimation() {
- statusBarWindowController.setForceStatusBarVisible(true)
- animationState = ANIMATING_IN
-
- val animSet = collectStartAnimations()
- if (animSet.totalDuration > 500) {
- throw IllegalStateException(
- "System animation total length exceeds budget. " +
- "Expected: 500, actual: ${animSet.totalDuration}"
- )
- }
- animSet.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- animationState = RUNNING_CHIP_ANIM
- }
- }
- )
- animSet.start()
-
- executor.executeDelayed(
- {
- val animSet2 = collectFinishAnimations()
- animationState = ANIMATING_OUT
- animSet2.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- animationState =
- if (hasPersistentDot) {
- SHOWING_PERSISTENT_DOT
- } else {
- IDLE
- }
-
- statusBarWindowController.setForceStatusBarVisible(false)
- }
- }
- )
- animSet2.start()
- scheduledEvent = null
- },
- DISPLAY_LENGTH
- )
- }
-
- private fun collectStartAnimations(): AnimatorSet {
- val animators = mutableListOf<Animator>()
- listeners.forEach { listener ->
- listener.onSystemEventAnimationBegin()?.let { anim -> animators.add(anim) }
- }
- animators.add(chipAnimationController.onSystemEventAnimationBegin())
- val animSet = AnimatorSet().also { it.playTogether(animators) }
-
- return animSet
- }
-
- private fun collectFinishAnimations(): AnimatorSet {
- val animators = mutableListOf<Animator>()
- listeners.forEach { listener ->
- listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim ->
- animators.add(anim)
- }
- }
- animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot))
- if (hasPersistentDot) {
- val dotAnim = notifyTransitionToPersistentDot()
- if (dotAnim != null) {
- animators.add(dotAnim)
- }
- }
- val animSet = AnimatorSet().also { it.playTogether(animators) }
-
- return animSet
- }
-
- private fun notifyTransitionToPersistentDot(): Animator? {
- val anims: List<Animator> =
- listeners.mapNotNull {
- it.onSystemStatusAnimationTransitionToPersistentDot(
- scheduledEvent?.contentDescription
- )
- }
- if (anims.isNotEmpty()) {
- val aSet = AnimatorSet()
- aSet.playTogether(anims)
- return aSet
- }
-
- return null
- }
-
- private fun notifyHidePersistentDot(): Animator? {
- val anims: List<Animator> = listeners.mapNotNull { it.onHidePersistentDot() }
-
- if (animationState == SHOWING_PERSISTENT_DOT) {
- animationState = IDLE
- }
-
- if (anims.isNotEmpty()) {
- val aSet = AnimatorSet()
- aSet.playTogether(anims)
- return aSet
- }
-
- return null
- }
-
- override fun addCallback(listener: SystemStatusAnimationCallback) {
- Assert.isMainThread()
-
- if (listeners.isEmpty()) {
- coordinator.startObserving()
- }
- listeners.add(listener)
- }
-
- override fun removeCallback(listener: SystemStatusAnimationCallback) {
- Assert.isMainThread()
-
- listeners.remove(listener)
- if (listeners.isEmpty()) {
- coordinator.stopObserving()
- }
- }
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("Scheduled event: $scheduledEvent")
- pw.println("Has persistent privacy dot: $hasPersistentDot")
- pw.println("Animation state: $animationState")
- pw.println("Listeners:")
- if (listeners.isEmpty()) {
- pw.println("(none)")
- } else {
- listeners.forEach { pw.println(" $it") }
- }
- }
-}
-
-private const val DEBUG = false
-private const val TAG = "SystemStatusAnimationSchedulerLegacyImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
index b2bdb72..a1a2ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
@@ -21,7 +21,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.Assert
import com.android.systemui.util.ListenerSet
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import java.util.Collections.unmodifiableList
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicReference
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 860697b..64970e4 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
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index e8afac5..3809ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -28,7 +28,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Compile
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
index 1f6f42d..1dd6242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -23,7 +23,7 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.util.NamedListenerSet
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
/**
* Set of classes that represent the various events that [NotifCollection] can dispatch to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index ca8e4fe..9fc4e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -23,7 +23,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.util.Compile
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
/**
* Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index c2791a0..9b55210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index c6d8500..61e6f65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -18,7 +18,7 @@
import android.annotation.MainThread
import android.view.View
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
/**
* Given a "spec" that describes a "tree" of views, adds and removes views from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 2c59ee8..1935866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 8064f04..12ee54d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -29,13 +32,64 @@
*/
@SysUISingleton
class ActiveNotificationListRepository @Inject constructor() {
- /**
- * Notifications actively presented to the user in the notification stack.
- *
- * @see com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
- */
- val activeNotifications = MutableStateFlow(emptyMap<String, ActiveNotificationModel>())
+ /** Notifications actively presented to the user in the notification list. */
+ val activeNotifications = MutableStateFlow(ActiveNotificationsStore())
/** Are any already-seen notifications currently filtered out of the active list? */
val hasFilteredOutSeenNotifications = MutableStateFlow(false)
}
+
+/** Represents the notification list, comprised of groups and individual notifications. */
+data class ActiveNotificationsStore(
+ /** Notification groups, stored by key. */
+ val groups: Map<String, ActiveNotificationGroupModel> = emptyMap(),
+ /** All individual notifications, including top-level and group children, stored by key. */
+ val individuals: Map<String, ActiveNotificationModel> = emptyMap(),
+ /**
+ * Ordered top-level list of entries in the notification list (either groups or individual),
+ * represented as [Key]s. The associated [ActiveNotificationEntryModel] can be retrieved by
+ * invoking [get].
+ */
+ val renderList: List<Key> = emptyList(),
+) {
+ operator fun get(key: Key): ActiveNotificationEntryModel? {
+ return when (key) {
+ is Key.Group -> groups[key.key]
+ is Key.Individual -> individuals[key.key]
+ }
+ }
+
+ /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */
+ sealed class Key {
+ data class Individual(val key: String) : Key()
+ data class Group(val key: String) : Key()
+ }
+
+ /** Mutable builder for an [ActiveNotificationsStore]. */
+ class Builder {
+ private val groups = mutableMapOf<String, ActiveNotificationGroupModel>()
+ private val individuals = mutableMapOf<String, ActiveNotificationModel>()
+ private val renderList = mutableListOf<Key>()
+
+ fun build() = ActiveNotificationsStore(groups, individuals, renderList)
+
+ fun addEntry(entry: ActiveNotificationEntryModel) {
+ when (entry) {
+ is ActiveNotificationModel -> addIndividualNotif(entry)
+ is ActiveNotificationGroupModel -> addNotifGroup(entry)
+ }
+ }
+
+ fun addIndividualNotif(notif: ActiveNotificationModel) {
+ renderList.add(Key.Individual(notif.key))
+ individuals[notif.key] = notif
+ }
+
+ fun addNotifGroup(group: ActiveNotificationGroupModel) {
+ renderList.add(Key.Group(group.key))
+ groups[group.key] = group
+ individuals[group.summary.key] = group.summary
+ group.children.forEach { individuals[it.key] = it }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index bfec60bc..85ba205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -27,6 +28,16 @@
repository: ActiveNotificationListRepository,
) {
/** Notifications actively presented to the user in the notification stack, in order. */
- val notifications: Flow<Collection<ActiveNotificationModel>> =
- repository.activeNotifications.map { it.values }
+ val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> =
+ repository.activeNotifications.map { store ->
+ store.renderList.map { key ->
+ val entry =
+ store[key]
+ ?: error("Could not find notification with key $key in active notif store.")
+ when (entry) {
+ is ActiveNotificationGroupModel -> entry.summary
+ is ActiveNotificationModel -> entry
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 604ecbc..6f4ed9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.graphics.drawable.Icon
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import javax.inject.Inject
import kotlinx.coroutines.flow.update
-private typealias ModelStore = Map<String, ActiveNotificationModel>
-
/**
* Logic for passing information from the
* [com.android.systemui.statusbar.notification.collection.NotifPipeline] to the presentation
@@ -37,106 +40,166 @@
private val sectionStyleProvider: SectionStyleProvider,
) {
/**
- * Sets the current list of rendered notification entries as displayed in the notification
- * stack.
- *
- * @see com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository.activeNotifications
+ * Sets the current list of rendered notification entries as displayed in the notification list.
*/
fun setRenderedList(entries: List<ListEntry>) {
repository.activeNotifications.update { existingModels ->
- entries.associateBy(
- keySelector = { it.key },
- valueTransform = { it.toModel(existingModels) },
- )
+ buildActiveNotificationsStore(existingModels, sectionStyleProvider) {
+ entries.forEach(::addListEntry)
+ }
+ }
+ }
+}
+
+private fun buildActiveNotificationsStore(
+ existingModels: ActiveNotificationsStore,
+ sectionStyleProvider: SectionStyleProvider,
+ block: ActiveNotificationsStoreBuilder.() -> Unit
+): ActiveNotificationsStore =
+ ActiveNotificationsStoreBuilder(existingModels, sectionStyleProvider).apply(block).build()
+
+private class ActiveNotificationsStoreBuilder(
+ private val existingModels: ActiveNotificationsStore,
+ private val sectionStyleProvider: SectionStyleProvider,
+) {
+ private val builder = ActiveNotificationsStore.Builder()
+
+ fun build(): ActiveNotificationsStore = builder.build()
+
+ /**
+ * Convert a [ListEntry] into [ActiveNotificationEntryModel]s, and add them to the
+ * [ActiveNotificationsStore]. Special care is taken to avoid re-allocating models if the result
+ * would be identical to an existing model (at the expense of additional computations).
+ */
+ fun addListEntry(entry: ListEntry) {
+ when (entry) {
+ is GroupEntry -> {
+ entry.summary?.let { summary ->
+ val summaryModel = summary.toModel()
+ val childModels = entry.children.map { it.toModel() }
+ builder.addNotifGroup(
+ existingModels.createOrReuse(
+ key = entry.key,
+ summary = summaryModel,
+ children = childModels
+ )
+ )
+ }
+ }
+ else -> {
+ entry.representativeEntry?.let { notifEntry ->
+ builder.addIndividualNotif(notifEntry.toModel())
+ }
+ }
}
}
- private fun ListEntry.toModel(
- existingModels: ModelStore,
- ): ActiveNotificationModel =
+ private fun NotificationEntry.toModel(): ActiveNotificationModel =
existingModels.createOrReuse(
key = key,
- groupKey = representativeEntry?.sbn?.groupKey,
+ groupKey = sbn.groupKey,
isAmbient = sectionStyleProvider.isMinimized(this),
- isRowDismissed = representativeEntry?.isRowDismissed == true,
+ isRowDismissed = isRowDismissed,
isSilent = sectionStyleProvider.isSilent(this),
- isLastMessageFromReply = representativeEntry?.isLastMessageFromReply == true,
- isSuppressedFromStatusBar = representativeEntry?.shouldSuppressStatusBar() == true,
- isPulsing = representativeEntry?.showingPulsing() == true,
- aodIcon = representativeEntry?.icons?.aodIcon?.sourceIcon,
- shelfIcon = representativeEntry?.icons?.shelfIcon?.sourceIcon,
- statusBarIcon = representativeEntry?.icons?.statusBarIcon?.sourceIcon,
+ isLastMessageFromReply = isLastMessageFromReply,
+ isSuppressedFromStatusBar = shouldSuppressStatusBar(),
+ isPulsing = showingPulsing(),
+ aodIcon = icons.aodIcon?.sourceIcon,
+ shelfIcon = icons.shelfIcon?.sourceIcon,
+ statusBarIcon = icons.statusBarIcon?.sourceIcon,
)
+}
- private fun ModelStore.createOrReuse(
- key: String,
- groupKey: String?,
- isAmbient: Boolean,
- isRowDismissed: Boolean,
- isSilent: Boolean,
- isLastMessageFromReply: Boolean,
- isSuppressedFromStatusBar: Boolean,
- isPulsing: Boolean,
- aodIcon: Icon?,
- shelfIcon: Icon?,
- statusBarIcon: Icon?
- ): ActiveNotificationModel {
- return this[key]?.takeIf {
- it.isCurrent(
- key = key,
- groupKey = groupKey,
- isAmbient = isAmbient,
- isRowDismissed = isRowDismissed,
- isSilent = isSilent,
- isLastMessageFromReply = isLastMessageFromReply,
- isSuppressedFromStatusBar = isSuppressedFromStatusBar,
- isPulsing = isPulsing,
- aodIcon = aodIcon,
- shelfIcon = shelfIcon,
- statusBarIcon = statusBarIcon
- )
- }
- ?: ActiveNotificationModel(
- key = key,
- groupKey = groupKey,
- isAmbient = isAmbient,
- isRowDismissed = isRowDismissed,
- isSilent = isSilent,
- isLastMessageFromReply = isLastMessageFromReply,
- isSuppressedFromStatusBar = isSuppressedFromStatusBar,
- isPulsing = isPulsing,
- aodIcon = aodIcon,
- shelfIcon = shelfIcon,
- statusBarIcon = statusBarIcon,
- )
+private fun ActiveNotificationsStore.createOrReuse(
+ key: String,
+ groupKey: String?,
+ isAmbient: Boolean,
+ isRowDismissed: Boolean,
+ isSilent: Boolean,
+ isLastMessageFromReply: Boolean,
+ isSuppressedFromStatusBar: Boolean,
+ isPulsing: Boolean,
+ aodIcon: Icon?,
+ shelfIcon: Icon?,
+ statusBarIcon: Icon?
+): ActiveNotificationModel {
+ return individuals[key]?.takeIf {
+ it.isCurrent(
+ key = key,
+ groupKey = groupKey,
+ isAmbient = isAmbient,
+ isRowDismissed = isRowDismissed,
+ isSilent = isSilent,
+ isLastMessageFromReply = isLastMessageFromReply,
+ isSuppressedFromStatusBar = isSuppressedFromStatusBar,
+ isPulsing = isPulsing,
+ aodIcon = aodIcon,
+ shelfIcon = shelfIcon,
+ statusBarIcon = statusBarIcon
+ )
}
+ ?: ActiveNotificationModel(
+ key = key,
+ groupKey = groupKey,
+ isAmbient = isAmbient,
+ isRowDismissed = isRowDismissed,
+ isSilent = isSilent,
+ isLastMessageFromReply = isLastMessageFromReply,
+ isSuppressedFromStatusBar = isSuppressedFromStatusBar,
+ isPulsing = isPulsing,
+ aodIcon = aodIcon,
+ shelfIcon = shelfIcon,
+ statusBarIcon = statusBarIcon,
+ )
+}
- private fun ActiveNotificationModel.isCurrent(
- key: String,
- groupKey: String?,
- isAmbient: Boolean,
- isRowDismissed: Boolean,
- isSilent: Boolean,
- isLastMessageFromReply: Boolean,
- isSuppressedFromStatusBar: Boolean,
- isPulsing: Boolean,
- aodIcon: Icon?,
- shelfIcon: Icon?,
- statusBarIcon: Icon?
- ): Boolean {
- return when {
- key != this.key -> false
- groupKey != this.groupKey -> false
- isAmbient != this.isAmbient -> false
- isRowDismissed != this.isRowDismissed -> false
- isSilent != this.isSilent -> false
- isLastMessageFromReply != this.isLastMessageFromReply -> false
- isSuppressedFromStatusBar != this.isSuppressedFromStatusBar -> false
- isPulsing != this.isPulsing -> false
- aodIcon != this.aodIcon -> false
- shelfIcon != this.shelfIcon -> false
- statusBarIcon != this.statusBarIcon -> false
- else -> true
- }
+private fun ActiveNotificationModel.isCurrent(
+ key: String,
+ groupKey: String?,
+ isAmbient: Boolean,
+ isRowDismissed: Boolean,
+ isSilent: Boolean,
+ isLastMessageFromReply: Boolean,
+ isSuppressedFromStatusBar: Boolean,
+ isPulsing: Boolean,
+ aodIcon: Icon?,
+ shelfIcon: Icon?,
+ statusBarIcon: Icon?
+): Boolean {
+ return when {
+ key != this.key -> false
+ groupKey != this.groupKey -> false
+ isAmbient != this.isAmbient -> false
+ isRowDismissed != this.isRowDismissed -> false
+ isSilent != this.isSilent -> false
+ isLastMessageFromReply != this.isLastMessageFromReply -> false
+ isSuppressedFromStatusBar != this.isSuppressedFromStatusBar -> false
+ isPulsing != this.isPulsing -> false
+ aodIcon != this.aodIcon -> false
+ shelfIcon != this.shelfIcon -> false
+ statusBarIcon != this.statusBarIcon -> false
+ else -> true
+ }
+}
+
+private fun ActiveNotificationsStore.createOrReuse(
+ key: String,
+ summary: ActiveNotificationModel,
+ children: List<ActiveNotificationModel>,
+): ActiveNotificationGroupModel {
+ return groups[key]?.takeIf { it.isCurrent(key, summary, children) }
+ ?: ActiveNotificationGroupModel(key, summary, children)
+}
+
+private fun ActiveNotificationGroupModel.isCurrent(
+ key: String,
+ summary: ActiveNotificationModel,
+ children: List<ActiveNotificationModel>,
+): Boolean {
+ return when {
+ key != this.key -> false
+ summary != this.summary -> false
+ children != this.children -> false
+ else -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 05c88e0..9e8654a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -34,7 +34,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
index 00d873e..30e2f0e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt
@@ -48,7 +48,7 @@
showPulsing: Boolean = true,
): Flow<Set<ActiveNotificationModel>> {
return combine(
- activeNotificationsInteractor.notifications,
+ activeNotificationsInteractor.topLevelRepresentativeNotifications,
keyguardViewStateRepository.areNotificationsFullyHidden,
) { notifications, notifsFullyHidden ->
notifications
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index c1f728a..db0fa99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -15,26 +15,19 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
import android.graphics.Color
import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
-import android.view.ViewPropertyAnimator
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
import androidx.lifecycle.lifecycleScope
-import com.android.app.animation.Interpolators
import com.android.internal.policy.SystemBarUtils
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
-import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.collection.NotifCollection
@@ -47,7 +40,6 @@
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.util.children
@@ -65,7 +57,6 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.mapNotNull
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Binds a view-model to a [NotificationIconContainer]. */
@@ -118,8 +109,6 @@
configuration: ConfigurationState,
configurationController: ConfigurationController,
dozeParameters: DozeParameters,
- featureFlags: FeatureFlagsClassic,
- screenOffAnimationController: ScreenOffAnimationController,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
@@ -134,16 +123,6 @@
}
launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
- // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC
- // view-binder
- launch {
- viewModel.isVisible.bindIsVisible(
- view,
- configuration,
- featureFlags,
- screenOffAnimationController,
- )
- }
launch {
configuration
.getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
@@ -333,106 +312,11 @@
setDecorColor(iconColors.tint)
}
- private suspend fun Flow<AnimatedValue<Boolean>>.bindIsVisible(
- view: NotificationIconContainer,
- configuration: ConfigurationState,
- featureFlags: FeatureFlagsClassic,
- screenOffAnimationController: ScreenOffAnimationController,
- ): Unit = coroutineScope {
- val iconAppearTranslation =
- configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
- val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
- collect { isVisible ->
- view.animate().cancel()
- val animatorListener =
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- isVisible.stopAnimating()
- }
- }
- when {
- !isVisible.isAnimating -> {
- view.alpha = 1f
- if (!statusViewMigrated) {
- view.translationY = 0f
- }
- view.visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
- }
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
- view.animateInIconTranslation(statusViewMigrated)
- if (isVisible.value) {
- CrossFadeHelper.fadeIn(view, animatorListener)
- } else {
- CrossFadeHelper.fadeOut(view, animatorListener)
- }
- }
- !isVisible.value -> {
- // Let's make sure the icon are translated to 0, since we cancelled it above
- view.animateInIconTranslation(statusViewMigrated)
- CrossFadeHelper.fadeOut(view, animatorListener)
- }
- view.visibility != View.VISIBLE -> {
- // No fading here, let's just appear the icons instead!
- view.visibility = View.VISIBLE
- view.alpha = 1f
- view.appearIcons(
- animate = screenOffAnimationController.shouldAnimateAodIcons(),
- iconAppearTranslation.value,
- statusViewMigrated,
- animatorListener,
- )
- }
- else -> {
- // Let's make sure the icons are translated to 0, since we cancelled it above
- view.animateInIconTranslation(statusViewMigrated)
- // We were fading out, let's fade in instead
- CrossFadeHelper.fadeIn(view, animatorListener)
- }
- }
- }
- }
-
- private fun View.appearIcons(
- animate: Boolean,
- iconAppearTranslation: Int,
- statusViewMigrated: Boolean,
- animatorListener: Animator.AnimatorListener,
- ) {
- if (animate) {
- if (!statusViewMigrated) {
- translationY = -iconAppearTranslation.toFloat()
- }
- alpha = 0f
- animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR)
- .setDuration(AOD_ICONS_APPEAR_DURATION)
- .apply { if (statusViewMigrated) animateInIconTranslation() }
- .setListener(animatorListener)
- .start()
- } else {
- alpha = 1.0f
- if (!statusViewMigrated) {
- translationY = 0f
- }
- }
- }
-
- private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
- if (!statusViewMigrated) {
- animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
- }
- }
-
- private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
- setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
-
/** External storage for [StatusBarIconView] instances. */
fun interface IconViewStore {
fun iconView(key: String): StatusBarIconView?
}
- private const val AOD_ICONS_APPEAR_DURATION: Long = 200
@ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 611ed89..835c059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -15,26 +15,16 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
-import android.graphics.Rect
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
-import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.util.kotlin.pairwise
-import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
-import com.android.systemui.util.ui.zip
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -46,14 +36,9 @@
class NotificationIconContainerAlwaysOnDisplayViewModel
@Inject
constructor(
- private val deviceEntryInteractor: DeviceEntryInteractor,
- private val dozeParameters: DozeParameters,
- private val featureFlags: FeatureFlagsClassic,
iconsInteractor: AlwaysOnDisplayNotificationIconsInteractor,
keyguardInteractor: KeyguardInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
- screenOffAnimationController: ScreenOffAnimationController,
shadeInteractor: ShadeInteractor,
) {
@@ -84,45 +69,6 @@
.distinctUntilChanged()
.toAnimatedValueFlow()
- /** Is the icon container visible? */
- val isVisible: Flow<AnimatedValue<Boolean>> =
- combine(
- keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE },
- deviceEntryInteractor.isBypassEnabled,
- areNotifsFullyHiddenAnimated(),
- isPulseExpandingAnimated(),
- ) {
- onKeyguard: Boolean,
- isBypassEnabled: Boolean,
- notifsFullyHidden: AnimatedValue<Boolean>,
- pulseExpanding: AnimatedValue<Boolean>,
- ->
- when {
- // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
- // animation is playing, in which case we want them to be visible if we're
- // animating in the AOD UI and will be switching to KEYGUARD shortly.
- !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() ->
- AnimatedValue.NotAnimating(false)
- else ->
- zip(notifsFullyHidden, pulseExpanding) {
- areNotifsFullyHidden,
- isPulseExpanding,
- ->
- when {
- // If we're bypassing, then we're visible
- isBypassEnabled -> true
- // If we are pulsing (and not bypassing), then we are hidden
- isPulseExpanding -> false
- // If notifs are fully gone, then we're visible
- areNotifsFullyHidden -> true
- // Otherwise, we're hidden
- else -> false
- }
- }
- }
- }
- .distinctUntilChanged()
-
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
iconsInteractor.aodNotifs.map { entries ->
@@ -130,43 +76,4 @@
visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
)
}
-
- /** Is there an expanded pulse, are we animating in response? */
- private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
- return notificationsKeyguardInteractor.isPulseExpanding
- .pairwise(initialValue = null)
- // If pulsing changes, start animating, unless it's the first emission
- .map { (prev, expanding) -> AnimatableEvent(expanding, startAnimating = prev != null) }
- .toAnimatedValueFlow()
- }
-
- /** Are notifications completely hidden from view, are we animating in response? */
- private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> {
- return notificationsKeyguardInteractor.areNotificationsFullyHidden
- .pairwise(initialValue = null)
- .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled ->
- val animate =
- when {
- // Don't animate for the first value
- prev == null -> false
- // Always animate if bypass is enabled.
- bypassEnabled -> true
- // If we're not bypassing and we're not going to AOD, then we're not
- // animating.
- !dozeParameters.alwaysOn -> false
- // Don't animate when going to AOD if the display needs blanking.
- dozeParameters.displayNeedsBlanking -> false
- // We only want the appear animations to happen when the notifications
- // get fully hidden, since otherwise the un-hide animation overlaps.
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
- else -> fullyHidden
- }
- AnimatableEvent(fullyHidden, animate)
- }
- .toAnimatedValueFlow()
- }
-
- private class IconColorsImpl(override val tint: Int) : NotificationIconColors {
- override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int = tint
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 53631e3..82626acc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -61,7 +61,7 @@
darkIconInteractor.tintAreas,
darkIconInteractor.tintColor,
// Included so that tints are re-applied after entries are changed.
- notificationsInteractor.notifications,
+ notificationsInteractor.topLevelRepresentativeNotifications,
) { areas, tint, _ ->
NotificationIconColorLookup { viewBounds: Rect ->
if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
new file mode 100644
index 0000000..538be14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2023 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.interruption
+
+import android.app.Notification.BubbleMetadata
+import android.app.Notification.VISIBILITY_PRIVATE
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.database.ContentObserver
+import android.hardware.display.AmbientDisplayConfiguration
+import android.os.Handler
+import android.os.PowerManager
+import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
+import android.provider.Settings.Global.HEADS_UP_OFF
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.SystemClock
+
+class PeekDisabledSuppressor(
+ private val globalSettings: GlobalSettings,
+ private val headsUpManager: HeadsUpManager,
+ private val logger: NotificationInterruptLogger,
+ @Main private val mainHandler: Handler,
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") {
+ private var isEnabled = false
+
+ override fun shouldSuppress(): Boolean = !isEnabled
+
+ override fun start() {
+ val observer =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) {
+ val wasEnabled = isEnabled
+
+ isEnabled =
+ globalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF) !=
+ HEADS_UP_OFF
+
+ // QQQ: Do we want to log this even if it hasn't changed?
+ logger.logHeadsUpFeatureChanged(isEnabled)
+
+ // QQQ: Is there a better place for this side effect? What if HeadsUpManager
+ // registered for it directly?
+ if (wasEnabled && !isEnabled) {
+ logger.logWillDismissAll()
+ headsUpManager.releaseAllImmediately()
+ }
+ }
+ }
+
+ globalSettings.registerContentObserver(
+ globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
+ /* notifyForDescendants = */ true,
+ observer
+ )
+
+ // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused.
+
+ observer.onChange(/* selfChange = */ true)
+ }
+}
+
+class PulseDisabledSuppressor(
+ private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val userTracker: UserTracker,
+) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") {
+ override fun shouldSuppress(): Boolean =
+ !ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId)
+}
+
+class PulseBatterySaverSuppressor(private val batteryController: BatteryController) :
+ VisualInterruptionCondition(
+ types = setOf(PULSE),
+ reason = "pulsing disabled by battery saver"
+ ) {
+ override fun shouldSuppress() = batteryController.isAodPowerSave()
+}
+
+class PeekPackageSnoozedSuppressor(private val headsUpManager: HeadsUpManager) :
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "package snoozed") {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ when {
+ // Assume any notification with an FSI is time-sensitive (like an alarm or incoming
+ // call) and ignore whether HUNs have been snoozed for the package.
+ entry.sbn.notification.fullScreenIntent != null -> false
+
+ // Otherwise, check if the package is snoozed.
+ else -> headsUpManager.isSnoozed(entry.sbn.packageName)
+ }
+}
+
+class PeekAlreadyBubbledSuppressor(private val statusBarStateController: StatusBarStateController) :
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "already bubbled") {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ when {
+ statusBarStateController.state != SHADE -> false
+ else -> entry.isBubble
+ }
+}
+
+class PeekDndSuppressor() :
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "suppressed by DND") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressPeek()
+}
+
+class PeekNotImportantSuppressor() :
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "not important") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_HIGH
+}
+
+class PeekDeviceNotInUseSuppressor(
+ private val powerManager: PowerManager,
+ private val statusBarStateController: StatusBarStateController
+) : VisualInterruptionCondition(types = setOf(PEEK), reason = "not in use") {
+ override fun shouldSuppress() =
+ when {
+ !powerManager.isScreenOn || statusBarStateController.isDreaming -> true
+ else -> false
+ }
+}
+
+class PeekOldWhenSuppressor(private val systemClock: SystemClock) :
+ VisualInterruptionFilter(types = setOf(PEEK), reason = "old when") {
+ private fun whenAge(entry: NotificationEntry) =
+ systemClock.currentTimeMillis() - entry.sbn.notification.`when`
+
+ override fun shouldSuppress(entry: NotificationEntry): Boolean =
+ when {
+ // Ignore a "when" of 0, as it is unlikely to be a meaningful timestamp.
+ entry.sbn.notification.`when` <= 0L -> false
+
+ // Assume all HUNs with FSIs, foreground services, or user-initiated jobs are
+ // time-sensitive, regardless of their "when".
+ entry.sbn.notification.fullScreenIntent != null ||
+ entry.sbn.notification.isForegroundService ||
+ entry.sbn.notification.isUserInitiatedJob -> false
+
+ // Otherwise, check if the HUN's "when" is too old.
+ else -> whenAge(entry) >= MAX_HUN_WHEN_AGE_MS
+ }
+}
+
+class PulseEffectSuppressor() :
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "ambient effect suppressed") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressAmbient()
+}
+
+class PulseLockscreenVisibilityPrivateSuppressor() :
+ VisualInterruptionFilter(
+ types = setOf(PULSE),
+ reason = "notification hidden on lock screen by override"
+ ) {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE
+}
+
+class PulseLowImportanceSuppressor() :
+ VisualInterruptionFilter(types = setOf(PULSE), reason = "importance less than DEFAULT") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_DEFAULT
+}
+
+class HunGroupAlertBehaviorSuppressor() :
+ VisualInterruptionFilter(
+ types = setOf(PEEK, PULSE),
+ reason = "suppressive group alert behavior"
+ ) {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ entry.sbn.let { it.isGroup && it.notification.suppressAlertingDueToGrouping() }
+}
+
+class HunJustLaunchedFsiSuppressor() :
+ VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "just launched FSI") {
+ override fun shouldSuppress(entry: NotificationEntry) = entry.hasJustLaunchedFullScreenIntent()
+}
+
+class BubbleNotAllowedSuppressor() :
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "not allowed") {
+ override fun shouldSuppress(entry: NotificationEntry) = !entry.canBubble()
+}
+
+class BubbleNoMetadataSuppressor() :
+ VisualInterruptionFilter(types = setOf(BUBBLE), reason = "no bubble metadata") {
+
+ private fun isValidMetadata(metadata: BubbleMetadata?) =
+ metadata != null && (metadata.intent != null || metadata.shortcutId != null)
+
+ override fun shouldSuppress(entry: NotificationEntry) = !isValidMetadata(entry.bubbleMetadata)
+}
+
+class AlertKeyguardVisibilitySuppressor(
+ private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider
+) : VisualInterruptionFilter(types = setOf(PEEK, PULSE, BUBBLE), reason = "hidden on keyguard") {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ keyguardNotificationVisibilityProvider.shouldHideNotification(entry)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 301ddbf..f2ade34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.interruption;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_OFF;
+
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA;
@@ -24,12 +27,10 @@
import android.app.Notification;
import android.app.NotificationManager;
-import android.content.ContentResolver;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
-import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import androidx.annotation.NonNull;
@@ -48,6 +49,8 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import java.util.ArrayList;
import java.util.List;
@@ -66,7 +69,6 @@
private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
- private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatteryController mBatteryController;
@@ -77,6 +79,8 @@
private final UiEventLogger mUiEventLogger;
private final UserTracker mUserTracker;
private final DeviceProvisionedController mDeviceProvisionedController;
+ private final SystemClock mSystemClock;
+ private final GlobalSettings mGlobalSettings;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -111,7 +115,6 @@
@Inject
public NotificationInterruptStateProviderImpl(
- ContentResolver contentResolver,
PowerManager powerManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
BatteryController batteryController,
@@ -124,8 +127,9 @@
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
UserTracker userTracker,
- DeviceProvisionedController deviceProvisionedController) {
- mContentResolver = contentResolver;
+ DeviceProvisionedController deviceProvisionedController,
+ SystemClock systemClock,
+ GlobalSettings globalSettings) {
mPowerManager = powerManager;
mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
@@ -137,15 +141,16 @@
mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider;
mUiEventLogger = uiEventLogger;
mUserTracker = userTracker;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mSystemClock = systemClock;
+ mGlobalSettings = globalSettings;
ContentObserver headsUpObserver = new ContentObserver(mainHandler) {
@Override
public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContentResolver,
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
+ final boolean wasUsing = mUseHeadsUp;
+ final boolean settingEnabled = HEADS_UP_OFF
+ != mGlobalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF);
+ mUseHeadsUp = ENABLE_HEADS_UP && settingEnabled;
mLogger.logHeadsUpFeatureChanged(mUseHeadsUp);
if (wasUsing != mUseHeadsUp) {
if (!mUseHeadsUp) {
@@ -157,16 +162,15 @@
};
if (ENABLE_HEADS_UP) {
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ mGlobalSettings.registerContentObserver(
+ mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
true,
headsUpObserver);
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+ mGlobalSettings.registerContentObserver(
+ mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true,
headsUpObserver);
}
headsUpObserver.onChange(true); // set up
- mDeviceProvisionedController = deviceProvisionedController;
}
@Override
@@ -603,7 +607,7 @@
}
final long when = notification.when;
- final long now = System.currentTimeMillis();
+ final long now = mSystemClock.currentTimeMillis();
final long age = now - when;
if (age < MAX_HUN_WHEN_AGE_MS) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index 920bbe9..da8474e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -51,6 +52,13 @@
}
/**
+ * Initializes the provider.
+ *
+ * Must be called before any method except [addLegacySuppressor].
+ */
+ fun start() {}
+
+ /**
* Adds a [component][suppressor] that can suppress visual interruptions.
*
* This class may call suppressors in any order.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
new file mode 100644
index 0000000..2730683
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2023 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.interruption
+
+import android.hardware.display.AmbientDisplayConfiguration
+import android.os.Handler
+import android.os.PowerManager
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+class VisualInterruptionDecisionProviderImpl
+@Inject
+constructor(
+ private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val batteryController: BatteryController,
+ private val globalSettings: GlobalSettings,
+ private val headsUpManager: HeadsUpManager,
+ private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
+ private val logger: NotificationInterruptLogger,
+ @Main private val mainHandler: Handler,
+ private val powerManager: PowerManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val systemClock: SystemClock,
+ private val userTracker: UserTracker,
+) : VisualInterruptionDecisionProvider {
+ private var started = false
+
+ override fun start() {
+ check(!started)
+
+ addCondition(PeekDisabledSuppressor(globalSettings, headsUpManager, logger, mainHandler))
+ addCondition(PulseDisabledSuppressor(ambientDisplayConfiguration, userTracker))
+ addCondition(PulseBatterySaverSuppressor(batteryController))
+ addFilter(PeekPackageSnoozedSuppressor(headsUpManager))
+ addFilter(PeekAlreadyBubbledSuppressor(statusBarStateController))
+ addFilter(PeekDndSuppressor())
+ addFilter(PeekNotImportantSuppressor())
+ addCondition(PeekDeviceNotInUseSuppressor(powerManager, statusBarStateController))
+ addFilter(PeekOldWhenSuppressor(systemClock))
+ addFilter(PulseEffectSuppressor())
+ addFilter(PulseLockscreenVisibilityPrivateSuppressor())
+ addFilter(PulseLowImportanceSuppressor())
+ addFilter(BubbleNotAllowedSuppressor())
+ addFilter(BubbleNoMetadataSuppressor())
+ addFilter(HunGroupAlertBehaviorSuppressor())
+ addFilter(HunJustLaunchedFsiSuppressor())
+ addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
+
+ started = true
+ }
+
+ private class DecisionImpl(
+ override val shouldInterrupt: Boolean,
+ override val logReason: String
+ ) : Decision
+
+ private class FullScreenIntentDecisionImpl(
+ override val shouldInterrupt: Boolean,
+ override val wouldInterruptWithoutDnd: Boolean,
+ override val logReason: String,
+ val originalEntry: NotificationEntry,
+ ) : FullScreenIntentDecision {
+ var hasBeenLogged = false
+ }
+
+ private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>()
+ private val conditions = mutableListOf<VisualInterruptionCondition>()
+ private val filters = mutableListOf<VisualInterruptionFilter>()
+
+ override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
+ legacySuppressors.add(suppressor)
+ }
+
+ override fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
+ legacySuppressors.remove(suppressor)
+ }
+
+ fun addCondition(condition: VisualInterruptionCondition) {
+ conditions.add(condition)
+ condition.start()
+ }
+
+ @VisibleForTesting
+ fun removeCondition(condition: VisualInterruptionCondition) {
+ conditions.remove(condition)
+ }
+
+ fun addFilter(filter: VisualInterruptionFilter) {
+ filters.add(filter)
+ filter.start()
+ }
+
+ @VisibleForTesting
+ fun removeFilter(filter: VisualInterruptionFilter) {
+ filters.remove(filter)
+ }
+
+ override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision {
+ check(started)
+ return makeHeadsUpDecision(entry)
+ }
+
+ override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision {
+ check(started)
+ return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) }
+ }
+
+ override fun makeUnloggedFullScreenIntentDecision(
+ entry: NotificationEntry
+ ): FullScreenIntentDecision {
+ check(started)
+ return makeFullScreenDecision(entry)
+ }
+
+ override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) {
+ check(started)
+ val decisionImpl =
+ decision as? FullScreenIntentDecisionImpl
+ ?: run {
+ Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision")
+ return
+ }
+ if (decision.hasBeenLogged) {
+ Log.wtf(TAG, "Already logged decision: $decision")
+ return
+ }
+ logFullScreenIntentDecision(decisionImpl)
+ decision.hasBeenLogged = true
+ }
+
+ override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision {
+ check(started)
+ return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) }
+ }
+
+ private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl {
+ if (statusBarStateController.isDozing) {
+ return makePulseDecision(entry)
+ } else {
+ return makePeekDecision(entry)
+ }
+ }
+
+ private fun makePeekDecision(entry: NotificationEntry): DecisionImpl {
+ checkConditions(PEEK)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkFilters(PEEK, entry)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressInterruptions"
+ )
+ }
+ checkAwakeSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressAwakeInterruptions"
+ )
+ }
+ checkAwakeHeadsUpSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressAwakeHeadsUpInterruptions"
+ )
+ }
+ return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
+ }
+
+ private fun makePulseDecision(entry: NotificationEntry): DecisionImpl {
+ checkConditions(PULSE)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkFilters(PULSE, entry)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressInterruptions"
+ )
+ }
+ return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
+ }
+
+ private fun makeBubbleDecision(entry: NotificationEntry): DecisionImpl {
+ checkConditions(BUBBLE)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkFilters(BUBBLE, entry)?.let {
+ return DecisionImpl(shouldInterrupt = false, logReason = it.reason)
+ }
+ checkSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressInterruptions"
+ )
+ }
+ checkAwakeSuppressors(entry)?.let {
+ return DecisionImpl(
+ shouldInterrupt = false,
+ logReason = "${it.name}.suppressAwakeInterruptions"
+ )
+ }
+ return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed")
+ }
+
+ private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl {
+ // Not yet implemented.
+ return FullScreenIntentDecisionImpl(
+ shouldInterrupt = true,
+ wouldInterruptWithoutDnd = true,
+ logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl",
+ originalEntry = entry
+ )
+ }
+
+ private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) {
+ // Not yet implemented.
+ }
+
+ private fun logBubbleDecision(entry: NotificationEntry, decision: DecisionImpl) {
+ // Not yet implemented.
+ }
+
+ private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) {
+ // Not yet implemented.
+ }
+
+ private fun checkSuppressors(entry: NotificationEntry) =
+ legacySuppressors.firstOrNull { it.suppressInterruptions(entry) }
+
+ private fun checkAwakeSuppressors(entry: NotificationEntry) =
+ legacySuppressors.firstOrNull { it.suppressAwakeInterruptions(entry) }
+
+ private fun checkAwakeHeadsUpSuppressors(entry: NotificationEntry) =
+ legacySuppressors.firstOrNull { it.suppressAwakeHeadsUp(entry) }
+
+ private fun checkConditions(type: VisualInterruptionType): VisualInterruptionCondition? =
+ conditions.firstOrNull { it.types.contains(type) && it.shouldSuppress() }
+
+ private fun checkFilters(
+ type: VisualInterruptionType,
+ entry: NotificationEntry
+ ): VisualInterruptionFilter? =
+ filters.firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) }
+}
+
+private const val TAG = "VisualInterruptionDecisionProviderImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
new file mode 100644
index 0000000..2624363
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.interruption
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the visual interruptions refactor flag state. */
+object VisualInterruptionRefactor {
+ const val FLAG_NAME = Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR
+
+ /** Whether the refactor is enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.visualInterruptionsRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index 4ef80e3..39199df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.statusbar.notification.interruption
import com.android.internal.logging.UiEventLogger.UiEventEnum
@@ -50,6 +51,12 @@
/** An optional UiEvent ID to be recorded when this suppresses an interruption. */
val uiEventId: UiEventEnum?
+
+ /**
+ * Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before
+ * any other methods are called on the suppressor.
+ */
+ fun start() {}
}
/** A reason why visual interruptions might be suppressed regardless of the notification. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
index c89f2fa..f096dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
@@ -21,12 +21,12 @@
import android.util.Log
import android.util.StatsEvent
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.tracing.traceSection
import java.lang.Exception
import java.util.concurrent.Executor
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 78370ba..eb1c1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -17,9 +17,17 @@
import android.graphics.drawable.Icon
-/** Model for entries in the notification stack. */
+/**
+ * Model for a top-level "entry" in the notification list, either an
+ * [individual notification][ActiveNotificationModel], or a [group][ActiveNotificationGroupModel].
+ */
+sealed class ActiveNotificationEntryModel
+
+/**
+ * Model for an individual notification in the notification list. These can appear as either an
+ * individual top-level notification, or as a child or summary of a [ActiveNotificationGroupModel].
+ */
data class ActiveNotificationModel(
- /** Notification key associated with this entry. */
val key: String,
/** Notification group key associated with this entry. */
val groupKey: String?,
@@ -47,4 +55,11 @@
val shelfIcon: Icon?,
/** Icon to display in the status bar. */
val statusBarIcon: Icon?,
-)
+) : ActiveNotificationEntryModel()
+
+/** Model for a group of notifications. */
+data class ActiveNotificationGroupModel(
+ val key: String,
+ val summary: ActiveNotificationModel,
+ val children: List<ActiveNotificationModel>,
+) : ActiveNotificationEntryModel()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index a98efba..af2ca26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.view.LayoutInflater
+import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.reinflateAndBindLatest
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -31,7 +32,6 @@
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tracing.traceSection
/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
object NotificationListViewBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 2af7181..6785da4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -59,10 +59,8 @@
launch {
viewModel.position.collect {
- controller.updateTopPadding(
- it.top,
- controller.isAddOrRemoveAnimationPending()
- )
+ val animate = it.animate || controller.isAddOrRemoveAnimationPending()
+ controller.updateTopPadding(it.top, animate)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 1229cb9..b86b5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -25,6 +25,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -118,8 +119,15 @@
}
}
} else {
- interactor.topPosition.map { top ->
- keyguardInteractor.sharedNotificationContainerPosition.value.copy(top = top)
+ interactor.topPosition.sample(shadeInteractor.qsExpansion, ::Pair).map {
+ (top, qsExpansion) ->
+ // When QS expansion > 0, it should directly set the top padding so do not
+ // animate it
+ val animate = qsExpansion == 0f
+ keyguardInteractor.sharedNotificationContainerPosition.value.copy(
+ top = top,
+ animate = animate
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 2d125462..e1fba2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -494,7 +494,7 @@
// this runnable is called right after the keyguard is solved, so we tell
// WM that we should dismiss it to avoid flickers when opening an activity
// that can also be shown over the keyguard.
- options.setDismissKeyguard()
+ options.setDismissKeyguardIfInsecure()
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index ae70384..c207324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -25,6 +25,7 @@
import android.util.TimeUtils;
import com.android.app.animation.Interpolators;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -95,6 +96,7 @@
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private final CommandQueue mCommandQueue;
+ private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private boolean mTransitionDeferring;
private long mTransitionDeferringStartTime;
@@ -134,6 +136,8 @@
mDozeAmount = mStatusBarStateController.getDozeAmount();
mContext = context;
mDisplayId = mContext.getDisplayId();
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
+ mHandler, mContext, null);
}
/** Call to cleanup the LightBarTransitionsController when done with it. */
@@ -279,7 +283,8 @@
*/
public boolean supportsIconTintForNavMode(int navigationMode) {
// In gesture mode, we already do region sampling to update tint based on content beneath.
- return !QuickStepContract.isGesturalMode(navigationMode);
+ return !QuickStepContract.isGesturalMode(navigationMode)
+ || mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 894549d..cba72d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -38,7 +38,7 @@
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.android.systemui.util.leak.RotationUtils.getExactRotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import java.io.PrintWriter
import java.lang.Math.max
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 1eea348..fb586ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -31,7 +31,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.tracing.TraceUtils
+import com.android.app.tracing.TraceUtils
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index fa9256f..2797b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -162,6 +162,7 @@
.setContentText(context.getString(R.string.stylus_battery_low_subtitle))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setLocalOnly(true)
+ .setOnlyAlertOnce(true)
.setAutoCancel(true)
.build()
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index c3c0291..2afb435 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -48,7 +48,7 @@
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.concurrency.ThreadFactory
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
import java.util.concurrent.Executor
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index dfff7c4..12b8845 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -17,10 +17,10 @@
import android.content.Context
import android.os.Trace
+import com.android.app.tracing.TraceStateLogger
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.tracing.TraceStateLogger
import com.android.systemui.unfold.system.DeviceStateRepository
import com.android.systemui.unfold.updates.FoldStateRepository
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt b/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
index 1e0f420..45d742f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import java.util.concurrent.CopyOnWriteArrayList
import java.util.function.Consumer
diff --git a/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
index cec9580..f49d616 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
@@ -20,7 +20,7 @@
import android.util.AttributeSet
import android.view.Choreographer
import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
/**
* [MotionLayout] that avoids remeasuring with the same inputs in the same frame.
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
index afd2360..de92318 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
@@ -13,7 +13,7 @@
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.annotation.Px
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
class DrawableSize {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index 5396bca..81737c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -7,8 +7,8 @@
import com.android.systemui.dagger.qualifiers.Tracing
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
-import com.android.systemui.tracing.TraceContextElement
+import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
+import com.android.app.tracing.TraceContextElement
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
index 2e355bd..eefd849 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.util.AttributeSet
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
/** LottieAnimationView that traces each call to invalidate. */
open class LottieViewWrapper : LottieAnimationView {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index 8f320a3..059ff56 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -21,7 +21,7 @@
import com.android.internal.view.RotationPolicy
import com.android.internal.view.RotationPolicy.RotationPolicyListener
import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.tracing.traceSection
+import com.android.app.tracing.traceSection
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index a453726..d261b08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -57,7 +57,7 @@
public static final String VOLUME_SILENT_DO_NOT_DISTURB = "sysui_do_not_disturb";
private final boolean mDefaultVolumeDownToEnterSilent;
- public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
+ public final boolean mDefaultVolumeUpToExitSilent;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
private static final Intent ZEN_SETTINGS =
@@ -107,10 +107,12 @@
mDefaultVolumeDownToEnterSilent = mContext.getResources()
.getBoolean(R.bool.config_volume_down_to_enter_silent);
+ mDefaultVolumeUpToExitSilent = mContext.getResources()
+ .getBoolean(R.bool.config_volume_up_to_exit_silent);
mVolumePolicy = new VolumePolicy(
mDefaultVolumeDownToEnterSilent, // volumeDownToEnterSilent
- DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent
+ mDefaultVolumeUpToExitSilent, // volumeUpToExitSilent
DEFAULT_DO_NOT_DISTURB_WHEN_SILENT, // doNotDisturbWhenSilent
400 // vibrateToSilentDebounce
);
@@ -133,7 +135,7 @@
TunerService.parseIntegerSwitch(newValue, mDefaultVolumeDownToEnterSilent);
} else if (VOLUME_UP_SILENT.equals(key)) {
volumeUpToExitSilent =
- TunerService.parseIntegerSwitch(newValue, DEFAULT_VOLUME_UP_TO_EXIT_SILENT);
+ TunerService.parseIntegerSwitch(newValue, mDefaultVolumeUpToExitSilent);
} else if (VOLUME_SILENT_DO_NOT_DISTURB.equals(key)) {
doNotDisturbWhenSilent =
TunerService.parseIntegerSwitch(newValue, DEFAULT_DO_NOT_DISTURB_WHEN_SILENT);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 603d548..4a799d8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -41,6 +41,7 @@
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.plugins.ClockAnimations;
import com.android.systemui.plugins.ClockController;
@@ -191,6 +192,7 @@
mClockEventController,
mLogBuffer,
mock(NotificationIconContainerAlwaysOnDisplayViewModel.class),
+ mock(KeyguardRootViewModel.class),
mock(ConfigurationState.class),
mock(DozeParameters.class),
mock(AlwaysOnDisplayNotificationIconViewStore.class),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6099ece1..776799e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -870,6 +870,23 @@
}
@Test
+ public void whenDetectFingerprint_detectError() {
+ ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
+ ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);
+
+ givenDetectFingerprintWithClearingFingerprintManagerInvocations();
+ verify(mFingerprintManager).detectFingerprint(
+ any(), fpDetectCallbackCaptor.capture(), any());
+ fpDetectCallbackCaptor.getValue().onDetectionError(/* msgId */ 10);
+
+ // THEN verify keyguardUpdateMonitorCallback receives a biometric error
+ verify(mTestCallback).onBiometricError(
+ eq(10), eq(""), eq(BiometricSourceType.FINGERPRINT));
+ verify(mTestCallback, never()).onBiometricAuthenticated(
+ anyInt(), any(), anyBoolean());
+ }
+
+ @Test
public void whenDetectFace_biometricDetectCallback() throws RemoteException {
ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor =
ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class);
@@ -1212,6 +1229,34 @@
}
@Test
+ public void fpStopsListeningWhenBiometricPromptShows_resumesOnBpHidden() {
+ // verify AuthController.Callback is added:
+ ArgumentCaptor<AuthController.Callback> captor = ArgumentCaptor.forClass(
+ AuthController.Callback.class);
+ verify(mAuthController).addCallback(captor.capture());
+ AuthController.Callback callback = captor.getValue();
+
+ // GIVEN keyguard showing
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+
+ // THEN fingerprint should listen
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+
+ // WHEN biometric prompt is shown
+ callback.onBiometricPromptShown();
+
+ // THEN shouldn't listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
+
+ // WHEN biometric prompt is dismissed
+ callback.onBiometricPromptDismissed();
+
+ // THEN we should listen for fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+ }
+
+ @Test
public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 728102d..0b38c4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -43,6 +43,7 @@
import android.view.animation.AccelerateInterpolator;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -67,6 +68,7 @@
@LargeTest
@RunWith(AndroidTestingRunner.class)
+@FlakyTest(bugId = 308501761)
public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index f899e2f..5e7b857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -505,7 +505,6 @@
this.authenticators = authenticators
}
},
- featureFlags,
testScope.backgroundScope,
fingerprintProps,
faceProps,
@@ -519,9 +518,7 @@
PromptViewModel(
displayStateInteractor,
promptSelectorInteractor,
- vibrator,
context,
- featureFlags
),
{ credentialViewModel },
Handler(TestableLooper.get(this).looper),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 885abcb..11c5d3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -88,7 +88,6 @@
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.VibratorHelper;
@@ -195,7 +194,6 @@
private Handler mHandler;
private DelayableExecutor mBackgroundExecutor;
private TestableAuthController mAuthController;
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Before
public void setup() throws RemoteException {
@@ -1023,7 +1021,7 @@
private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context) {
- super(context, mFeatureFlags, null /* applicationCoroutineScope */,
+ super(context, null /* applicationCoroutineScope */,
mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
mFingerprintManager, mFaceManager, () -> mUdfpsController,
() -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 99501c42..3fbdeec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -26,6 +26,7 @@
import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_0
@@ -35,11 +36,14 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags.REST_TO_UNLOCK
+import com.android.systemui.log.SideFpsLogger
import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -62,7 +66,7 @@
class SideFpsSensorInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- private lateinit var testScope: TestScope
+ private val testScope = TestScope(StandardTestDispatcher())
private val fingerprintRepository = FakeFingerprintPropertyRepository()
@@ -70,32 +74,38 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var displayStateInteractor: DisplayStateInteractor
-
+ @Mock
+ private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+ private val isRestToUnlockEnabled = MutableStateFlow(false)
private val contextDisplayInfo = DisplayInfo()
private val displayChangeEvent = MutableStateFlow(0)
private val currentRotation = MutableStateFlow(ROTATION_0)
@Before
fun setup() {
- testScope = TestScope(StandardTestDispatcher())
mContext = spy(mContext)
- val displayManager = mock(DisplayManagerGlobal::class.java)
val resources = mContext.resources
whenever(mContext.display)
- .thenReturn(Display(displayManager, 1, contextDisplayInfo, resources))
+ .thenReturn(
+ Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+ )
whenever(displayStateInteractor.displayChanges).thenReturn(displayChangeEvent)
whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation)
contextDisplayInfo.uniqueId = "current-display"
-
+ val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) }
+ whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+ .thenReturn(isRestToUnlockEnabled)
underTest =
SideFpsSensorInteractor(
mContext,
fingerprintRepository,
windowManager,
displayStateInteractor,
- FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) }
+ featureFlags,
+ Optional.of(fingerprintInteractiveToAuthProvider),
+ SideFpsLogger(logcatLogBuffer("SfpsLogger"))
)
}
@@ -155,7 +165,7 @@
assertThat(sensorLocation!!.left).isEqualTo(1000)
assertThat(sensorLocation!!.top).isEqualTo(200)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -183,7 +193,7 @@
assertThat(sensorLocation!!.left).isEqualTo(500)
assertThat(sensorLocation!!.top).isEqualTo(1000)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -211,7 +221,7 @@
assertThat(sensorLocation!!.left).isEqualTo(200)
assertThat(sensorLocation!!.top).isEqualTo(0)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -264,7 +274,7 @@
assertThat(sensorLocation!!.left).isEqualTo(500)
assertThat(sensorLocation!!.top).isEqualTo(0)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -291,7 +301,7 @@
assertThat(sensorLocation!!.left).isEqualTo(0)
assertThat(sensorLocation!!.top).isEqualTo(400)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -318,7 +328,7 @@
assertThat(sensorLocation!!.left).isEqualTo(400)
assertThat(sensorLocation!!.top).isEqualTo(800)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
}
@Test
@@ -345,7 +355,22 @@
assertThat(sensorLocation!!.left).isEqualTo(800)
assertThat(sensorLocation!!.top).isEqualTo(500)
assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
- assertThat(sensorLocation!!.width).isEqualTo(100)
+ assertThat(sensorLocation!!.length).isEqualTo(100)
+ }
+
+ @Test
+ fun isProlongedTouchRequiredForAuthentication_dependsOnSettingsToggle() =
+ testScope.runTest {
+ val isEnabled by collectLastValue(underTest.isProlongedTouchRequiredForAuthentication)
+ setupFingerprint(FingerprintSensorType.POWER_BUTTON)
+
+ isRestToUnlockEnabled.value = true
+ runCurrent()
+ assertThat(isEnabled).isTrue()
+
+ isRestToUnlockEnabled.value = false
+ runCurrent()
+ assertThat(isEnabled).isFalse()
}
private suspend fun TestScope.setupFPLocationAndDisplaySize(
@@ -356,10 +381,14 @@
rotation: DisplayRotation,
sensorWidth: Int
) {
- overrideResource(R.integer.config_sfpsSensorWidth, sensorWidth)
setupDisplayDimensions(width, height)
currentRotation.value = rotation
- setupFingerprint(x = sensorLocationX, y = sensorLocationY, displayId = "expanded_display")
+ setupFingerprint(
+ x = sensorLocationX,
+ y = sensorLocationY,
+ displayId = "expanded_display",
+ sensorRadius = sensorWidth / 2
+ )
}
private fun setupDisplayDimensions(displayWidth: Int, displayHeight: Int) {
@@ -367,7 +396,7 @@
.thenReturn(
WindowMetrics(
Rect(0, 0, displayWidth, displayHeight),
- mock(WindowInsets::class.java)
+ mock(WindowInsets::class.java),
)
)
}
@@ -376,7 +405,8 @@
fingerprintSensorType: FingerprintSensorType = FingerprintSensorType.POWER_BUTTON,
x: Int = 0,
y: Int = 0,
- displayId: String = "display_id_1"
+ displayId: String = "display_id_1",
+ sensorRadius: Int = 150
) {
contextDisplayInfo.uniqueId = displayId
fingerprintRepository.setProperties(
@@ -390,14 +420,14 @@
"someOtherDisplayId",
x + 100,
y + 100,
- 0,
+ sensorRadius,
),
displayId to
SensorLocationInternal(
displayId,
x,
y,
- 0,
+ sensorRadius,
)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index b695a0e..d06cbbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -44,18 +44,16 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -64,9 +62,7 @@
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.mockito.Mock
-import org.mockito.Mockito.never
import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
private const val USER_ID = 4
@@ -95,7 +91,6 @@
private lateinit var selector: PromptSelectorInteractor
private lateinit var viewModel: PromptViewModel
private lateinit var iconViewModel: PromptIconViewModel
- private val featureFlags = FakeFeatureFlags()
@Before
fun setup() {
@@ -125,10 +120,8 @@
PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
selector.resetPrompt()
- viewModel =
- PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags)
+ viewModel = PromptViewModel(displayStateInteractor, selector, mContext)
iconViewModel = viewModel.iconViewModel
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@Test
@@ -180,26 +173,29 @@
}
@Test
- fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- verify(vibrator, if (expectConfirmation) never() else times(1))
- .vibrateAuthSuccess(any())
+ val confirmConstant by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmConstant)
+ .isEqualTo(
+ if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
+ else HapticFeedbackConstants.CONFIRM
+ )
if (expectConfirmation) {
viewModel.confirmAuthenticated()
}
- verify(vibrator).vibrateAuthSuccess(any())
- verify(vibrator, never()).vibrateAuthError(any())
+ val confirmedConstant by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
}
@Test
- fun playSuccessHaptic_onwayHapticsEnabled_SetsConfirmConstant() = runGenericTest {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
+ fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
@@ -212,8 +208,7 @@
}
@Test
- fun playErrorHaptic_onwayHapticsEnabled_SetsRejectConstant() = runGenericTest {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
+ fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
viewModel.showTemporaryError("test", "messageAfterError", false)
val currentConstant by collectLastValue(viewModel.hapticsToPlay)
@@ -251,7 +246,8 @@
DisplayRotation.ROTATION_180 ->
R.raw.biometricprompt_fingerprint_to_error_landscape
DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ R.raw
+ .biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
else -> throw Exception("invalid rotation")
}
assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
@@ -496,7 +492,8 @@
DisplayRotation.ROTATION_180 ->
R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+ R.raw
+ .biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
else -> throw Exception("invalid rotation")
}
assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
@@ -733,7 +730,7 @@
}
@Test
- fun plays_haptic_on_errors() = runGenericTest {
+ fun set_haptic_on_errors() = runGenericTest {
viewModel.showTemporaryError(
"so sad",
messageAfterError = "",
@@ -741,8 +738,8 @@
hapticFeedback = true,
)
- verify(vibrator).vibrateAuthError(any())
- verify(vibrator, never()).vibrateAuthSuccess(any())
+ val constant by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT)
}
@Test
@@ -754,8 +751,8 @@
hapticFeedback = false,
)
- verify(vibrator, never()).vibrateAuthError(any())
- verify(vibrator, never()).vibrateAuthSuccess(any())
+ val constant by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
private suspend fun TestScope.showTemporaryErrors(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 80fe9e7..fcb18f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
@@ -35,13 +36,14 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -54,8 +56,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import kotlinx.coroutines.flow.StateFlowKt;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class FalsingCollectorImplTest extends SysuiTestCase {
private FalsingCollectorImpl mFalsingCollector;
@@ -73,7 +78,9 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private ShadeExpansionStateManager mShadeExpansionStateManager;
+ private ShadeInteractor mShadeInteractor;
+ @Mock
+ private JavaAdapter mJavaAdapter;
@Mock
private BatteryController mBatteryController;
@Mock
@@ -88,12 +95,16 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
- mStatusBarStateController, mKeyguardStateController, mShadeExpansionStateManager,
- mBatteryController,
- mDockManager, mFakeExecutor, mFakeSystemClock, () -> mSelectedUserInteractor);
+ mStatusBarStateController, mKeyguardStateController,
+ () -> mShadeInteractor, mBatteryController,
+ mDockManager, mFakeExecutor,
+ mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor
+ );
+ mFalsingCollector.init();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 8e21f29..2f17b6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.coroutines.collectLastValue
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -86,4 +87,48 @@
val interactor = CommunalInteractor(communalRepository, widgetRepository)
assertThat(interactor.isCommunalEnabled).isFalse()
}
+
+ @Test
+ fun listensToSceneChange() =
+ testScope.runTest {
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ var desiredScene = collectLastValue(interactor.desiredScene)
+ runCurrent()
+ assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank)
+
+ val targetScene = CommunalSceneKey.Communal
+ communalRepository.setDesiredScene(targetScene)
+ desiredScene = collectLastValue(interactor.desiredScene)
+ runCurrent()
+ assertThat(desiredScene()).isEqualTo(targetScene)
+ }
+
+ @Test
+ fun updatesScene() =
+ testScope.runTest {
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+ val targetScene = CommunalSceneKey.Communal
+
+ interactor.onSceneChanged(targetScene)
+
+ val desiredScene = collectLastValue(communalRepository.desiredScene)
+ runCurrent()
+ assertThat(desiredScene()).isEqualTo(targetScene)
+ }
+
+ @Test
+ fun isCommunalShowing() =
+ testScope.runTest {
+ val interactor = CommunalInteractor(communalRepository, widgetRepository)
+
+ var isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+ runCurrent()
+ assertThat(isCommunalShowing()).isEqualTo(false)
+
+ interactor.onSceneChanged(CommunalSceneKey.Communal)
+
+ isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+ runCurrent()
+ assertThat(isCommunalShowing()).isEqualTo(true)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 0a9a15e..61d1502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -21,16 +21,23 @@
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -46,18 +53,28 @@
@Mock private lateinit var userTracker: UserTracker
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
+ private lateinit var testScope: TestScope
private lateinit var underTest: CommunalTutorialInteractor
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var keyguardInteractor: KeyguardInteractor
private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+ private lateinit var sceneContainerFlags: FakeSceneContainerFlags
+ private lateinit var communalInteractor: CommunalInteractor
+ private lateinit var communalRepository: FakeCommunalRepository
+
+ private val utils = SceneTestUtils(this)
+ private lateinit var sceneInteractor: SceneInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ sceneInteractor = utils.sceneInteractor()
+ testScope = utils.testScope
+ sceneContainerFlags = utils.sceneContainerFlags.apply { enabled = false }
+ communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
+ communalInteractor = CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
+
val withDeps = KeyguardInteractorFactory.create()
keyguardInteractor = withDeps.keyguardInteractor
keyguardRepository = withDeps.repository
@@ -65,8 +82,12 @@
underTest =
CommunalTutorialInteractor(
- keyguardInteractor = keyguardInteractor,
+ scope = testScope.backgroundScope,
communalTutorialRepository = communalTutorialRepository,
+ keyguardInteractor = keyguardInteractor,
+ communalInteractor = communalInteractor,
+ sceneContainerFlags = sceneContainerFlags,
+ sceneInteractor = sceneInteractor,
)
whenever(userTracker.userHandle).thenReturn(mock())
@@ -87,6 +108,7 @@
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
assertThat(isTutorialAvailable).isFalse()
}
@@ -97,6 +119,7 @@
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
@@ -107,7 +130,188 @@
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
keyguardRepository.setKeyguardShowing(true)
keyguardRepository.setKeyguardOccluded(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
+
+ /* Testing tutorial states with transitions when flexiglass off */
+ @Test
+ fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(communalInteractor.desiredScene)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+
+ assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ /* Testing tutorial states with transitions when flexiglass on */
+ @Test
+ fun tutorialState_notStartedCommunalSceneShowingAndFlexiglassOn_tutorialStarted() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_completedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_notStartedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedCommunalSceneNotShowingAndFlexiglassOn_tutorialCompleted() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_completedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
+ testScope.runTest {
+ sceneContainerFlags.enabled = true
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ val currentScene by collectLastValue(sceneInteractor.desiredScene)
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
+
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 8416c46..6a79ee8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -24,8 +24,6 @@
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.settings.ControlsSettingsDialogManager
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -83,8 +81,6 @@
private lateinit var action: ControlActionCoordinatorImpl.Action
private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
- private val featureFlags = FakeFeatureFlags()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -104,7 +100,6 @@
metricsLogger,
vibratorHelper,
controlsSettingsRepository,
- featureFlags
))
coordinator.activityContext = mContext
@@ -200,31 +195,7 @@
}
@Test
- fun drag_isEdge_oneWayHapticsDisabled_usesVibrate() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
-
- coordinator.drag(cvh, true)
-
- verify(vibratorHelper).vibrate(
- Vibrations.rangeEdgeEffect
- )
- }
-
- @Test
- fun drag_isNotEdge_oneWayHapticsDisabled_usesVibrate() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
-
- coordinator.drag(cvh, false)
-
- verify(vibratorHelper).vibrate(
- Vibrations.rangeMiddleEffect
- )
- }
-
- @Test
- fun drag_isEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
-
+ fun drag_isEdge_performsSegmentTickHaptics() {
coordinator.drag(cvh, true)
verify(vibratorHelper).performHapticFeedback(
@@ -234,9 +205,7 @@
}
@Test
- fun drag_isNotEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
-
+ fun drag_isNotEdge_performsFrequentTickHaptics() {
coordinator.drag(cvh, false)
verify(vibratorHelper).performHapticFeedback(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index d80dd76..806930d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -380,6 +380,32 @@
}
@Test
+ fun pendingDisplay_afterConfigChanged_doesNotChange() =
+ testScope.runTest {
+ val pendingDisplay by lastPendingDisplay()
+
+ sendOnDisplayConnected(1, TYPE_EXTERNAL)
+ val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ assertThat(pendingDisplay).isNotNull()
+ sendOnDisplayChanged(1)
+
+ assertThat(initialPendingDisplay).isEqualTo(pendingDisplay)
+ }
+
+ @Test
+ fun pendingDisplay_afterNewHigherDisplayConnected_changes() =
+ testScope.runTest {
+ val pendingDisplay by lastPendingDisplay()
+
+ sendOnDisplayConnected(1, TYPE_EXTERNAL)
+ val initialPendingDisplay: DisplayRepository.PendingDisplay? = pendingDisplay
+ assertThat(pendingDisplay).isNotNull()
+ sendOnDisplayConnected(2, TYPE_EXTERNAL)
+
+ assertThat(initialPendingDisplay).isNotEqualTo(pendingDisplay)
+ }
+
+ @Test
fun onPendingDisplay_OneInternalAndOneExternalDisplay_internalIgnored() =
testScope.runTest {
val pendingDisplay by lastPendingDisplay()
@@ -466,6 +492,10 @@
connectedDisplayListener.value.onDisplayConnected(id)
}
+ private fun sendOnDisplayChanged(id: Int) {
+ connectedDisplayListener.value.onDisplayChanged(id)
+ }
+
private fun setDisplays(displays: List<Display>) {
whenever(displayManager.displays).thenReturn(displays.toTypedArray())
displays.forEach { display ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 3af444a..27fd3b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -165,12 +165,28 @@
int maxBrightness = 3;
when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+ when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)))
+ .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
assertEquals(maxBrightness, mServiceFake.screenBrightness);
}
@Test
+ public void testAod_usesLightSensorNotClampingToAutoBrightnessValue() {
+ int maxBrightness = 3;
+ when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+ eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+ when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+ eq(UserHandle.USER_CURRENT)))
+ .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void doze_doesNotUseLightSensor() {
// GIVEN the device is DOZE and the display state changes to ON
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
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 2b280c0..814a317 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -253,7 +253,6 @@
mKeyguardStateController,
mScreenOffAnimationController,
mAuthController,
- mShadeExpansionStateManager,
() -> mShadeInteractor,
mShadeWindowLogger,
() -> mSelectedUserInteractor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 4f545cb..4a1386e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -15,16 +15,24 @@
*
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.viewmodel
import android.view.View
import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
@@ -33,14 +41,25 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.ClockController
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.ui.isAnimating
+import com.android.systemui.util.ui.stopAnimating
+import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -48,7 +67,10 @@
import org.junit.runners.JUnit4
import org.mockito.Answers
import org.mockito.Mock
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.withSettings
import org.mockito.MockitoAnnotations
@SmallTest
@@ -81,7 +103,7 @@
MockitoAnnotations.initMocks(this)
val featureFlags =
- FakeFeatureFlags().apply {
+ FakeFeatureFlagsClassic().apply {
set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true)
set(Flags.FACE_AUTH_REFACTOR, true)
}
@@ -107,11 +129,21 @@
underTest =
KeyguardRootViewModel(
context,
+ deviceEntryInteractor =
+ mock { whenever(isBypassEnabled).thenReturn(MutableStateFlow(false)) },
+ dozeParameters = mock(),
+ featureFlags,
keyguardInteractor,
+ keyguardTransitionInteractor,
+ notificationsKeyguardInteractor =
+ mock {
+ whenever(areNotificationsFullyHidden).thenReturn(emptyFlow())
+ whenever(isPulseExpanding).thenReturn(emptyFlow())
+ },
burnInInteractor,
goneToAodTransitionViewModel,
aodToLockscreenTransitionViewModel,
- keyguardTransitionInteractor,
+ screenOffAnimationController = mock(),
)
underTest.clockControllerProvider = Provider { clockController }
}
@@ -179,6 +211,8 @@
val translationY by collectLastValue(underTest.translationY)
val scale by collectLastValue(underTest.scale)
+ underTest.statusViewTop = 100
+
// Set to dozing (on AOD)
dozeAmountTransitionStep.emit(TransitionStep(value = 1f))
// Trigger a change to the burn-in model
@@ -200,6 +234,37 @@
}
@Test
+ fun translationAndScaleFromBurnFullyDozingStaysOutOfTopInset() =
+ testScope.runTest {
+ val translationX by collectLastValue(underTest.translationX)
+ val translationY by collectLastValue(underTest.translationY)
+ val scale by collectLastValue(underTest.scale)
+
+ underTest.statusViewTop = 100
+ underTest.topInset = 80
+
+ // Set to dozing (on AOD)
+ dozeAmountTransitionStep.emit(TransitionStep(value = 1f))
+ // Trigger a change to the burn-in model
+ burnInFlow.value =
+ BurnInModel(
+ translationX = 20,
+ translationY = -30,
+ scale = 0.5f,
+ )
+ assertThat(translationX).isEqualTo(20)
+ // -20 instead of -30, due to inset of 80
+ assertThat(translationY).isEqualTo(-20)
+ assertThat(scale).isEqualTo(Pair(0.5f, true /* scaleClockOnly */))
+
+ // Set to the beginning of GONE->AOD transition
+ goneToAodTransitionStep.emit(TransitionStep(value = 0f))
+ assertThat(translationX).isEqualTo(0)
+ assertThat(translationY).isEqualTo(0)
+ assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */))
+ }
+
+ @Test
fun translationAndScaleFromBurnInUseScaleOnly() =
testScope.runTest {
whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true)
@@ -248,3 +313,169 @@
assertThat(burnInLayerAlpha).isEqualTo(1f)
}
}
+
+@SmallTest
+class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {
+
+ @Component(modules = [SysUITestModule::class])
+ @SysUISingleton
+ interface TestComponent {
+ val underTest: KeyguardRootViewModel
+ val deviceEntryRepository: FakeDeviceEntryRepository
+ val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
+ val repository: FakeKeyguardRepository
+ val testScope: TestScope
+ val transitionRepository: FakeKeyguardTransitionRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+
+ private val clockController: ClockController =
+ mock(withSettings().defaultAnswer(RETURNS_DEEP_STUBS))
+ private val dozeParams: DozeParameters = mock()
+ private val screenOffAnimController: ScreenOffAnimationController = mock()
+
+ private fun runTest(block: suspend TestComponent.() -> Unit): Unit =
+ DaggerKeyguardRootViewModelTestWithFakes_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ setDefault(Flags.NEW_AOD_TRANSITION)
+ set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParams,
+ screenOffAnimationController = screenOffAnimController,
+ )
+ )
+ .run {
+ reset(clockController)
+ underTest.clockControllerProvider = Provider { clockController }
+ testScope.runTest {
+ runCurrent()
+ block()
+ }
+ }
+
+ @Test
+ fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.OFF,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isFalse()
+ assertThat(isVisible?.isAnimating).isFalse()
+ }
+
+ @Test
+ fun iconContainer_isVisible_bypassEnabled() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ deviceEntryRepository.setBypassEnabled(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ }
+
+ @Test
+ fun iconContainer_isNotVisible_pulseExpanding_notBypassing() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(true)
+ deviceEntryRepository.setBypassEnabled(false)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isEqualTo(false)
+ }
+
+ @Test
+ fun iconContainer_isVisible_notifsFullyHidden_bypassEnabled() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(true)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ assertThat(isVisible?.isAnimating).isTrue()
+ }
+
+ @Test
+ fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ assertThat(isVisible?.isAnimating).isFalse()
+ }
+
+ @Test
+ fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(true)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ assertThat(isVisible?.isAnimating).isFalse()
+ }
+
+ @Test
+ fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ assertThat(isVisible?.isAnimating).isTrue()
+ }
+
+ @Test
+ fun isIconContainerVisible_stopAnimation() = runTest {
+ val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
+ testScope.runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ testScope.runCurrent()
+
+ assertThat(isVisible?.isAnimating).isEqualTo(true)
+ isVisible?.stopAnimating()
+ testScope.runCurrent()
+
+ assertThat(isVisible?.isAnimating).isEqualTo(false)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 5296f1a..5bfe569 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -25,6 +25,10 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -46,6 +50,11 @@
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
@@ -63,6 +72,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@@ -71,6 +81,7 @@
@Mock private lateinit var lockHost: MediaHost
@Mock private lateinit var qsHost: MediaHost
@Mock private lateinit var qqsHost: MediaHost
+ @Mock private lateinit var hubModeHost: MediaHost
@Mock private lateinit var bypassController: KeyguardBypassController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@@ -93,10 +104,15 @@
private lateinit var mediaHierarchyManager: MediaHierarchyManager
private lateinit var mediaFrame: ViewGroup
private val configurationController = FakeConfigurationController()
+ private val communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
+ private val communalInteractor =
+ CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
private val notifPanelEvents = ShadeExpansionStateManager()
private val settings = FakeSettings()
private lateinit var testableLooper: TestableLooper
private lateinit var fakeHandler: FakeHandler
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
@Before
fun setup() {
@@ -117,11 +133,13 @@
mediaDataManager,
keyguardViewController,
dreamOverlayStateController,
+ communalInteractor,
configurationController,
wakefulnessLifecycle,
notifPanelEvents,
settings,
fakeHandler,
+ testScope.backgroundScope,
ResourcesSplitShadeStateController(),
logger,
)
@@ -131,6 +149,7 @@
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
+ setupHost(hubModeHost, MediaHierarchyManager.LOCATION_COMMUNAL_HUB, COMMUNAL_TOP)
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
whenever(mediaDataManager.hasActiveMedia()).thenReturn(true)
whenever(mediaCarouselController.mediaCarouselScrollHandler)
@@ -475,6 +494,33 @@
}
@Test
+ fun testCommunalLocation() =
+ testScope.runTest {
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong()
+ )
+ clearInvocations(mediaCarouselController)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
+ runCurrent()
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong()
+ )
+ }
+
+ @Test
fun testQsExpandedChanged_noQqsMedia() {
// When we are looking at QQS with active media
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
@@ -538,5 +584,6 @@
private const val QQS_TOP = 123
private const val QS_TOP = 456
private const val LOCKSCREEN_TOP = 789
+ private const val COMMUNAL_TOP = 111
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
new file mode 100644
index 0000000..89ba69f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileDefaultsRepositoryTest : SysuiTestCase() {
+
+ @Mock private lateinit var sysuiContext: Context
+ @Mock private lateinit var user1Context: Context
+ @Mock private lateinit var user2Context: Context
+ @Mock private lateinit var packageManager1: PackageManager
+ @Mock private lateinit var packageManager2: PackageManager
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: CustomTileDefaultsRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(sysuiContext.createContextAsUser(eq(USER_1), any())).thenReturn(user1Context)
+ whenever(user1Context.packageManager).thenReturn(packageManager1)
+ packageManager1.setupApp1()
+
+ whenever(sysuiContext.createContextAsUser(eq(USER_2), any())).thenReturn(user2Context)
+ whenever(user2Context.packageManager).thenReturn(packageManager2)
+ packageManager2.setupApp2()
+
+ underTest =
+ CustomTileDefaultsRepositoryImpl(
+ sysuiContext,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun regularRequestingEmitsTheNewDefault() =
+ testScope.runTest {
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+
+ runCurrent()
+
+ val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result
+ assertThat(default.label).isEqualTo(APP_LABEL_1)
+ assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_1)
+ assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName)
+ }
+
+ @Test
+ fun requestingSystemAppEmitsTheNewDefault() =
+ testScope.runTest {
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+
+ runCurrent()
+
+ val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result
+ assertThat(default.label).isEqualTo(APP_LABEL_1)
+ assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_1)
+ assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName)
+ }
+
+ @Test
+ fun requestingForcesTheNewEmit() =
+ testScope.runTest {
+ val defaults = mutableListOf<CustomTileDefaults.Result>()
+ backgroundScope.launch {
+ underTest
+ .defaults(USER_1)
+ .map { it as CustomTileDefaults.Result }
+ .collect { defaults.add(it) }
+ }
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ // the same request should be skipped. This leads to 2 result in assertions
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ runCurrent()
+
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, true)
+ runCurrent()
+
+ assertThat(defaults).hasSize(2)
+ assertThat(defaults.last().label).isEqualTo(APP_LABEL_1)
+ assertThat(defaults.last().icon.resId).isEqualTo(SERVICE_ICON_1)
+ assertThat(defaults.last().icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName)
+ }
+
+ @Test
+ fun userChangeForcesTheNewEmit() =
+ testScope.runTest {
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ runCurrent()
+
+ underTest.requestNewDefaults(USER_2, COMPONENT_NAME_2, false)
+ runCurrent()
+
+ val default = underTest.defaults(USER_2).first() as CustomTileDefaults.Result
+ assertThat(default.label).isEqualTo(APP_LABEL_2)
+ assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_2)
+ assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_2.packageName)
+ }
+
+ @Test
+ fun componentNameChangeForcesTheNewEmit() =
+ testScope.runTest {
+ packageManager1.setupApp2(false)
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+ runCurrent()
+
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_2, false)
+ runCurrent()
+
+ val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result
+ assertThat(default.label).isEqualTo(APP_LABEL_2)
+ assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_2)
+ assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_2.packageName)
+ }
+
+ @Test
+ fun noIconIsAnError() =
+ testScope.runTest {
+ packageManager1.setupApp(
+ componentName = COMPONENT_NAME_1,
+ appLabel = "",
+ serviceIcon = 0,
+ appInfoIcon = 0,
+ isSystemApp = false,
+ )
+ underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false)
+
+ runCurrent()
+
+ assertThat(underTest.defaults(USER_1).first())
+ .isInstanceOf(CustomTileDefaults.Error::class.java)
+ }
+
+ @Test
+ fun applicationScopeIsFreedWhileNotSubscribed() =
+ testScope.runTest {
+ val listenJob = underTest.defaults(USER_1).launchIn(backgroundScope)
+ listenJob.cancel()
+ assertThat(this.coroutineContext[Job]!!.children.toList()).isEmpty()
+ }
+
+ private fun PackageManager.setupApp1(isSystemApp: Boolean = false) =
+ setupApp(
+ componentName = COMPONENT_NAME_1,
+ serviceIcon = SERVICE_ICON_1,
+ appLabel = APP_LABEL_1,
+ appInfoIcon = APP_INFO_ICON_1,
+ isSystemApp = isSystemApp,
+ )
+ private fun PackageManager.setupApp2(isSystemApp: Boolean = false) =
+ setupApp(
+ componentName = COMPONENT_NAME_2,
+ serviceIcon = SERVICE_ICON_2,
+ appLabel = APP_LABEL_2,
+ appInfoIcon = APP_INFO_ICON_2,
+ isSystemApp = isSystemApp,
+ )
+
+ private fun PackageManager.setupApp(
+ componentName: ComponentName,
+ serviceIcon: Int,
+ appLabel: CharSequence,
+ appInfoIcon: Int = serviceIcon,
+ isSystemApp: Boolean = false,
+ ) {
+ val appInfo =
+ object : ApplicationInfo() {
+ override fun isSystemApp(): Boolean = isSystemApp
+ }
+ .apply { icon = appInfoIcon }
+ whenever(getApplicationInfo(eq(componentName.packageName), any<Int>())).thenReturn(appInfo)
+
+ // set of desired flags is different for system and a regular app.
+ var serviceFlags =
+ (PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE)
+ if (isSystemApp) {
+ serviceFlags = serviceFlags or PackageManager.MATCH_DISABLED_COMPONENTS
+ }
+
+ val serviceInfo =
+ object : ServiceInfo() {
+ override fun loadLabel(pm: PackageManager): CharSequence = appLabel
+ }
+ .apply {
+ applicationInfo = appInfo
+ icon = serviceIcon
+ }
+ whenever(getServiceInfo(eq(componentName), eq(serviceFlags))).thenReturn(serviceInfo)
+ }
+
+ private companion object {
+ val USER_1 = UserHandle(1)
+ val USER_2 = UserHandle(2)
+
+ val COMPONENT_NAME_1 = ComponentName("pkg.test_1", "cls")
+ const val SERVICE_ICON_1 = 11
+ const val APP_INFO_ICON_1 = 12
+ const val APP_LABEL_1 = "app_1"
+
+ val COMPONENT_NAME_2 = ComponentName("pkg.test_2", "cls")
+ const val SERVICE_ICON_2 = 21
+ const val APP_INFO_ICON_2 = 22
+ const val APP_LABEL_2 = "app_2"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
new file mode 100644
index 0000000..4a22113
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom
+
+import android.content.ComponentName
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.external.TileLifecycleManager
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository.DefaultsRequest
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() {
+
+ @Mock private lateinit var tileServiceManager: TileServiceManager
+
+ @Captor
+ private lateinit var listenerCaptor: ArgumentCaptor<TileLifecycleManager.TileChangeListener>
+
+ private val defaultsRepository = FakeCustomTileDefaultsRepository()
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: CustomTilePackageUpdatesRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ CustomTilePackageUpdatesRepositoryImpl(
+ TileSpec.create(COMPONENT_1),
+ USER,
+ tileServiceManager,
+ defaultsRepository,
+ testScope.backgroundScope,
+ )
+ }
+
+ @Test
+ fun packageChangesUpdatesDefaults() =
+ testScope.runTest {
+ val events = mutableListOf<Unit>()
+ underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+ verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor))
+
+ emitPackageChange()
+ runCurrent()
+
+ assertThat(events).hasSize(1)
+ assertThat(defaultsRepository.defaultsRequests).isNotEmpty()
+ assertThat(defaultsRepository.defaultsRequests.last())
+ .isEqualTo(DefaultsRequest(USER, COMPONENT_1, true))
+ }
+
+ @Test
+ fun packageChangesEmittedOnlyForTheTile() =
+ testScope.runTest {
+ val events = mutableListOf<Unit>()
+ underTest.packageChanges.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+ verify(tileServiceManager).setTileChangeListener(capture(listenerCaptor))
+
+ emitPackageChange(COMPONENT_2)
+ runCurrent()
+
+ assertThat(events).isEmpty()
+ }
+
+ private fun emitPackageChange(componentName: ComponentName = COMPONENT_1) {
+ listenerCaptor.value.onTileChanged(componentName)
+ }
+
+ private companion object {
+ val USER = UserHandle(0)
+ val COMPONENT_1 = ComponentName("pkg.test.1", "cls.test")
+ val COMPONENT_2 = ComponentName("pkg.test.2", "cls.test")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 432bd0f..456f1bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -51,6 +51,7 @@
SceneKey.Lockscreen,
SceneKey.Bouncer,
SceneKey.Gone,
+ SceneKey.Communal,
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 2ce4b04..446a0b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -99,7 +99,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -148,7 +147,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
@@ -335,7 +333,6 @@
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
- @Mock private KeyguardRootView mKeyguardRootView;
@Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
@Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
@@ -575,14 +572,13 @@
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
mContext,
coordinator,
- mKeyguardBypassController, mHeadsUpManager,
- mock(NotificationRoundnessManager.class),
+ mKeyguardBypassController,
+ mHeadsUpManager,
mConfigurationController,
mStatusBarStateController,
mFalsingManager,
- mShadeExpansionStateManager,
+ mShadeInteractor,
mLockscreenShadeTransitionController,
- new FalsingCollectorFake(),
mDumpManager);
when(mKeyguardStatusViewComponentFactory.build(any(), any()))
.thenReturn(mKeyguardStatusViewComponent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 4ba850c..8e0cf7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -254,7 +254,6 @@
mKeyguardStateController,
mScreenOffAnimationController,
mAuthController,
- mShadeExpansionStateManager,
() -> mShadeInteractor,
mShadeWindowLogger,
() -> mSelectedUserInteractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 4e3e165..5459779 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -97,6 +97,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -111,9 +112,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.Optional
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -140,6 +140,7 @@
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+ @Mock private lateinit var quickSettingsController: QuickSettingsController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var lockIconViewController: LockIconViewController
@@ -166,7 +167,7 @@
@Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private val notificationLaunchAnimationRepository = NotificationLaunchAnimationRepository()
private val notificationLaunchAnimationInteractor =
- NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
+ NotificationLaunchAnimationInteractor(notificationLaunchAnimationRepository)
private lateinit var fakeClock: FakeSystemClock
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
@@ -274,6 +275,7 @@
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
sysUIKeyEventHandler,
+ quickSettingsController,
primaryBouncerInteractor,
alternateBouncerInteractor,
mSelectedUserInteractor,
@@ -460,9 +462,11 @@
// AND alternate bouncer doesn't want the touch
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
.thenReturn(false)
+ // AND quick settings controller doesn't want it
+ whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+ .thenReturn(false)
// AND the lock icon wants the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
- .thenReturn(true)
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
featureFlagsClassic.set(MIGRATE_NSSL, true)
@@ -476,10 +480,31 @@
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
// AND alternate bouncer doesn't want the touch
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(false)
+ .thenReturn(false)
// AND the lock icon does NOT want the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
- .thenReturn(false)
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+ // AND quick settings controller doesn't want it
+ whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+ .thenReturn(false)
+
+ featureFlagsClassic.set(MIGRATE_NSSL, true)
+
+ // THEN touch should NOT be intercepted by NotificationShade
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
+ }
+
+ @Test
+ fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
+ // GIVEN dozing
+ whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+ // AND alternate bouncer doesn't want the touch
+ whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+ .thenReturn(false)
+ // AND the lock icon does NOT want the touch
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+ // AND quick settings controller DOES want it
+ whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+ .thenReturn(true)
featureFlagsClassic.set(MIGRATE_NSSL, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 3d5d26a..a6ab6a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -121,6 +121,7 @@
@Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var quickSettingsController: QuickSettingsController
@Mock
private lateinit var notificationStackScrollLayoutController:
NotificationStackScrollLayoutController
@@ -264,6 +265,7 @@
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
Mockito.mock(SysUIKeyEventHandler::class.java),
+ quickSettingsController,
primaryBouncerInteractor,
alternateBouncerInteractor,
mSelectedUserInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index e70dbc7..778cfa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -18,7 +18,6 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.testing.TestableResources
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
@@ -27,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -38,6 +36,8 @@
import com.android.systemui.plugins.qs.QS
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -46,6 +46,7 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -66,7 +67,7 @@
/** Uses Flags.MIGRATE_NSSL set to false. If all goes well, this set of tests will be deleted. */
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@@ -74,7 +75,7 @@
@Mock lateinit var navigationModeController: NavigationModeController
@Mock lateinit var overviewProxyService: OverviewProxyService
@Mock lateinit var shadeHeaderController: ShadeHeaderController
- @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var fragmentService: FragmentService
@Mock lateinit var fragmentHostManager: FragmentHostManager
@Mock
@@ -88,7 +89,6 @@
lateinit var underTest: NotificationsQSContainerController
- private lateinit var fakeResources: TestableResources
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var navigationModeCallback: ModeChangedListener
private lateinit var taskbarVisibilityCallback: OverviewProxyListener
@@ -111,6 +111,7 @@
whenever(view.resources).thenReturn(mContext.resources)
whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
+ whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
underTest =
NotificationsQSContainerController(
@@ -118,7 +119,7 @@
navigationModeController,
overviewProxyService,
shadeHeaderController,
- shadeExpansionStateManager,
+ shadeInteractor,
fragmentService,
delayableExecutor,
featureFlags,
@@ -475,7 +476,7 @@
navigationModeController,
overviewProxyService,
shadeHeaderController,
- shadeExpansionStateManager,
+ shadeInteractor,
fragmentService,
delayableExecutor,
featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index ac8c924..2342003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -18,7 +18,6 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
-import android.testing.TestableResources
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
@@ -27,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -38,6 +36,8 @@
import com.android.systemui.plugins.qs.QS
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -46,6 +46,7 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,7 +66,7 @@
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class NotificationsQSContainerControllerTest : SysuiTestCase() {
@@ -73,7 +74,7 @@
@Mock lateinit var navigationModeController: NavigationModeController
@Mock lateinit var overviewProxyService: OverviewProxyService
@Mock lateinit var shadeHeaderController: ShadeHeaderController
- @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock lateinit var shadeInteractor: ShadeInteractor
@Mock lateinit var fragmentService: FragmentService
@Mock lateinit var fragmentHostManager: FragmentHostManager
@Mock
@@ -87,7 +88,6 @@
lateinit var underTest: NotificationsQSContainerController
- private lateinit var fakeResources: TestableResources
private lateinit var featureFlags: FakeFeatureFlags
private lateinit var navigationModeCallback: ModeChangedListener
private lateinit var taskbarVisibilityCallback: OverviewProxyListener
@@ -111,13 +111,15 @@
whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
+ whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
+
underTest =
NotificationsQSContainerController(
view,
navigationModeController,
overviewProxyService,
shadeHeaderController,
- shadeExpansionStateManager,
+ shadeInteractor,
fragmentService,
delayableExecutor,
featureFlags,
@@ -458,7 +460,7 @@
navigationModeController,
overviewProxyService,
shadeHeaderController,
- shadeExpansionStateManager,
+ shadeInteractor,
fragmentService,
delayableExecutor,
featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 6eeafefd..e920687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -189,4 +189,13 @@
underTest.setUdfpsTransitionToFullShadeProgress(1f)
assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(1f)
}
+
+ @Test
+ fun updateLegacyIsQsExpanded() =
+ testScope.runTest {
+ assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(false)
+
+ underTest.setLegacyIsQsExpanded(true)
+ assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(true)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index 20e5c43..49e5c45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -21,18 +21,17 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,19 +52,18 @@
private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
private val bypassController: KeyguardBypassController = mock()
private val headsUpManager: HeadsUpManager = mock()
- private val roundnessManager: NotificationRoundnessManager = mock()
private val configurationController: ConfigurationController = mock()
private val statusBarStateController: StatusBarStateController = mock()
private val falsingManager: FalsingManager = mock()
- private val shadeExpansionStateManager: ShadeExpansionStateManager = mock()
+ private val shadeInteractor: ShadeInteractor = mock()
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock()
- private val falsingCollector: FalsingCollector = mock()
private val dumpManager: DumpManager = mock()
private val expandableView: ExpandableView = mock()
@Before
fun setUp() {
whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+ whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
pulseExpansionHandler =
PulseExpansionHandler(
@@ -73,13 +71,11 @@
wakeUpCoordinator,
bypassController,
headsUpManager,
- roundnessManager,
configurationController,
statusBarStateController,
falsingManager,
- shadeExpansionStateManager,
+ shadeInteractor,
lockscreenShadeTransitionController,
- falsingCollector,
dumpManager
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 2e223f6..df257ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -27,7 +27,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
-import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.window.StatusBarWindowController
@@ -88,8 +87,7 @@
SystemEventChipAnimationController(
context = mContext,
statusBarWindowController = sbWindowController,
- contentInsetsProvider = insetsProvider,
- featureFlags = FakeFeatureFlags(),
+ contentInsetsProvider = insetsProvider
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
index c289ff3..bbc63f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State.CONNECTED
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.mockito.any
@@ -48,7 +48,6 @@
class SystemEventCoordinatorTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
- private val featureFlags = FakeFeatureFlags()
private val testScope = TestScope(UnconfinedTestDispatcher())
private val connectedDisplayInteractor = FakeConnectedDisplayInteractor()
@@ -66,7 +65,6 @@
batteryController,
privacyController,
context,
- featureFlags,
TestScope(UnconfinedTestDispatcher()),
connectedDisplayInteractor
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index fee8b82..5f01b5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -26,8 +26,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.statusbar.BatteryStatusChip
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -76,7 +74,6 @@
private lateinit var systemClock: FakeSystemClock
private lateinit var chipAnimationController: SystemEventChipAnimationController
private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler
- private val fakeFeatureFlags = FakeFeatureFlags()
@get:Rule val animatorTestRule = AnimatorTestRule()
@@ -84,15 +81,12 @@
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeFeatureFlags.set(Flags.PLUG_IN_STATUS_BAR_CHIP, true)
-
systemClock = FakeSystemClock()
chipAnimationController =
SystemEventChipAnimationController(
mContext,
statusBarWindowController,
- statusBarContentInsetProvider,
- fakeFeatureFlags
+ statusBarContentInsetProvider
)
// StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index ca8ea4e..b86f841 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -19,6 +19,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.util.mockito.mock
@@ -40,9 +41,19 @@
@Test
fun setRenderedList_preservesOrdering() = runTest {
- val notifs by collectLastValue(notifsInteractor.notifications)
+ val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
val keys = (1..50).shuffled().map { "$it" }
- val entries = keys.map { mock<ListEntry> { whenever(key).thenReturn(it) } }
+ val entries =
+ keys.map {
+ mock<ListEntry> {
+ val mockRep = mock<NotificationEntry> {
+ whenever(key).thenReturn(it)
+ whenever(sbn).thenReturn(mock())
+ whenever(icons).thenReturn(mock())
+ }
+ whenever(representativeEntry).thenReturn(mockRep)
+ }
+ }
underTest.setRenderedList(entries)
assertThat(notifs).comparingElementsUsing(byKey).containsExactlyElementsIn(keys).inOrder()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index ec80e5f..f8252a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.notification.shared.activeNotificationModel
import com.android.systemui.statusbar.notification.shared.byIsAmbient
@@ -77,7 +78,9 @@
fun setup() =
with(testComponent) {
activeNotificationListRepository.activeNotifications.value =
- testIcons.associateBy { it.key }
+ ActiveNotificationsStore.Builder()
+ .apply { testIcons.forEach(::addIndividualNotif) }
+ .build()
}
@Test
@@ -196,7 +199,9 @@
fun setup() =
with(testComponent) {
activeNotificationListRepository.activeNotifications.value =
- testIcons.associateBy { it.key }
+ ActiveNotificationsStore.Builder()
+ .apply { testIcons.forEach(::addIndividualNotif) }
+ .build()
}
@Test
@@ -318,7 +323,9 @@
fun setup() =
with(testComponent) {
activeNotificationListRepository.activeNotifications.value =
- testIcons.associateBy { it.key }
+ ActiveNotificationsStore.Builder()
+ .apply { testIcons.forEach(::addIndividualNotif) }
+ .build()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 14d188c..49e1493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -25,7 +25,6 @@
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -38,7 +37,6 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
@@ -70,16 +68,12 @@
private lateinit var testComponent: TestComponent
private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
get() = testComponent.underTest
- private val deviceEntryRepository: FakeDeviceEntryRepository
- get() = testComponent.deviceEntryRepository
private val deviceProvisioningRepository: FakeDeviceProvisioningRepository
get() = testComponent.deviceProvisioningRepository
private val keyguardRepository: FakeKeyguardRepository
get() = testComponent.keyguardRepository
private val keyguardTransitionRepository: FakeKeyguardTransitionRepository
get() = testComponent.keyguardTransitionRepository
- private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
- get() = testComponent.notifsKeyguardRepository
private val powerRepository: FakePowerRepository
get() = testComponent.powerRepository
private val scope: TestScope
@@ -354,137 +348,6 @@
assertThat(isDozing?.isAnimating).isEqualTo(false)
}
- @Test
- fun isNotVisible_pulseExpanding() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(true)
- runCurrent()
-
- assertThat(isVisible?.value).isFalse()
- }
-
- @Test
- fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.OFF,
- to = KeyguardState.GONE,
- scope,
- )
- whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false)
- runCurrent()
-
- assertThat(isVisible?.value).isFalse()
- assertThat(isVisible?.isAnimating).isFalse()
- }
-
- @Test
- fun isVisible_bypassEnabled() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- deviceEntryRepository.setBypassEnabled(true)
- runCurrent()
-
- assertThat(isVisible?.value).isTrue()
- }
-
- @Test
- fun isNotVisible_pulseExpanding_notBypassing() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(true)
- deviceEntryRepository.setBypassEnabled(false)
- runCurrent()
-
- assertThat(isVisible?.value).isEqualTo(false)
- }
-
- @Test
- fun isVisible_notifsFullyHidden_bypassEnabled() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(false)
- deviceEntryRepository.setBypassEnabled(true)
- notifsKeyguardRepository.setNotificationsFullyHidden(true)
- runCurrent()
-
- assertThat(isVisible?.value).isTrue()
- assertThat(isVisible?.isAnimating).isTrue()
- }
-
- @Test
- fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(false)
- deviceEntryRepository.setBypassEnabled(false)
- whenever(dozeParams.alwaysOn).thenReturn(false)
- notifsKeyguardRepository.setNotificationsFullyHidden(true)
- runCurrent()
-
- assertThat(isVisible?.value).isTrue()
- assertThat(isVisible?.isAnimating).isFalse()
- }
-
- @Test
- fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(false)
- deviceEntryRepository.setBypassEnabled(false)
- whenever(dozeParams.alwaysOn).thenReturn(true)
- whenever(dozeParams.displayNeedsBlanking).thenReturn(true)
- notifsKeyguardRepository.setNotificationsFullyHidden(true)
- runCurrent()
-
- assertThat(isVisible?.value).isTrue()
- assertThat(isVisible?.isAnimating).isFalse()
- }
-
- @Test
- fun isVisible_notifsFullyHidden_bypassDisabled() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(false)
- deviceEntryRepository.setBypassEnabled(false)
- whenever(dozeParams.alwaysOn).thenReturn(true)
- whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
- notifsKeyguardRepository.setNotificationsFullyHidden(true)
- runCurrent()
-
- assertThat(isVisible?.value).isTrue()
- assertThat(isVisible?.isAnimating).isTrue()
- }
-
- @Test
- fun isVisible_stopAnimation() =
- scope.runTest {
- val isVisible by collectLastValue(underTest.isVisible)
- runCurrent()
- notifsKeyguardRepository.setPulseExpanding(false)
- deviceEntryRepository.setBypassEnabled(false)
- whenever(dozeParams.alwaysOn).thenReturn(true)
- whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
- notifsKeyguardRepository.setNotificationsFullyHidden(true)
- runCurrent()
-
- assertThat(isVisible?.isAnimating).isEqualTo(true)
- isVisible?.stopAnimating()
- runCurrent()
-
- assertThat(isVisible?.isAnimating).isEqualTo(false)
- }
-
@SysUISingleton
@Component(
modules =
@@ -498,11 +361,9 @@
val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
- val deviceEntryRepository: FakeDeviceEntryRepository
val deviceProvisioningRepository: FakeDeviceProvisioningRepository
val keyguardRepository: FakeKeyguardRepository
val keyguardTransitionRepository: FakeKeyguardTransitionRepository
- val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
val powerRepository: FakePowerRepository
val scope: TestScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index ba68fbb..44acac8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -42,6 +42,7 @@
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationIconViewStateRepository
import com.android.systemui.statusbar.notification.shared.activeNotificationModel
import com.android.systemui.statusbar.phone.DozeParameters
@@ -342,14 +343,17 @@
val icon: Icon = mock()
shadeRepository.setLegacyShadeExpansion(0f)
activeNotificationsRepository.activeNotifications.value =
- listOf(
- activeNotificationModel(
- key = "notif1",
- groupKey = "group",
- statusBarIcon = icon
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ groupKey = "group",
+ statusBarIcon = icon
+ )
)
- )
- .associateBy { it.key }
+ }
+ .build()
val isolatedIcon by collectLastValue(underTest.isolatedIcon)
runCurrent()
@@ -368,14 +372,17 @@
val icon: Icon = mock()
shadeRepository.setLegacyShadeExpansion(.5f)
activeNotificationsRepository.activeNotifications.value =
- listOf(
- activeNotificationModel(
- key = "notif1",
- groupKey = "group",
- statusBarIcon = icon
+ ActiveNotificationsStore.Builder()
+ .apply {
+ addIndividualNotif(
+ activeNotificationModel(
+ key = "notif1",
+ groupKey = "group",
+ statusBarIcon = icon
+ )
)
- )
- .associateBy { it.key }
+ }
+ .build()
val isolatedIcon by collectLastValue(underTest.isolatedIcon)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 50ce265..1c62161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.interruption;
+package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -27,6 +27,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -61,9 +63,9 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -74,6 +76,8 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -120,6 +124,8 @@
UserTracker mUserTracker;
@Mock
DeviceProvisionedController mDeviceProvisionedController;
+ FakeSystemClock mSystemClock;
+ FakeGlobalSettings mGlobalSettings;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -129,10 +135,12 @@
when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
mUiEventLoggerFake = new UiEventLoggerFake();
+ mSystemClock = new FakeSystemClock();
+ mGlobalSettings = new FakeGlobalSettings();
+ mGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
mNotifInterruptionStateProvider =
new NotificationInterruptStateProviderImpl(
- mContext.getContentResolver(),
mPowerManager,
mAmbientDisplayConfiguration,
mBatteryController,
@@ -145,7 +153,9 @@
mKeyguardNotificationVisibilityProvider,
mUiEventLoggerFake,
mUserTracker,
- mDeviceProvisionedController);
+ mDeviceProvisionedController,
+ mSystemClock,
+ mGlobalSettings);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
@@ -426,7 +436,7 @@
}
private long makeWhenHoursAgo(long hoursAgo) {
- return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
+ return mSystemClock.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 947bcfb..1d2055e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2023 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.interruption
import android.testing.AndroidTestingRunner
@@ -19,27 +35,27 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
- override val provider: VisualInterruptionDecisionProvider
- get() =
- NotificationInterruptStateProviderWrapper(
- NotificationInterruptStateProviderImpl(
- context.contentResolver,
- powerManager,
- ambientDisplayConfiguration,
- batteryController,
- statusBarStateController,
- keyguardStateController,
- headsUpManager,
- logger,
- mainHandler,
- flags,
- keyguardNotificationVisibilityProvider,
- uiEventLogger,
- userTracker,
- deviceProvisionedController
- )
- .also { it.mUseHeadsUp = true }
+ override val provider by lazy {
+ NotificationInterruptStateProviderWrapper(
+ NotificationInterruptStateProviderImpl(
+ powerManager,
+ ambientDisplayConfiguration,
+ batteryController,
+ statusBarStateController,
+ keyguardStateController,
+ headsUpManager,
+ logger,
+ mainHandler,
+ flags,
+ keyguardNotificationVisibilityProvider,
+ uiEventLogger,
+ userTracker,
+ deviceProvisionedController,
+ systemClock,
+ globalSettings,
)
+ )
+ }
// Tests of internals of the wrapper:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
new file mode 100644
index 0000000..80d941a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2023 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.interruption
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() {
+ override val provider by lazy {
+ VisualInterruptionDecisionProviderImpl(
+ ambientDisplayConfiguration,
+ batteryController,
+ globalSettings,
+ headsUpManager,
+ keyguardNotificationVisibilityProvider,
+ logger,
+ mainHandler,
+ powerManager,
+ statusBarStateController,
+ systemClock,
+ userTracker,
+ )
+ }
+
+ @Test
+ fun testNothingCondition_suppressesNothing() {
+ withCondition(TestCondition(types = emptySet()) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testNothingFilter_suppressesNothing() {
+ withFilter(TestFilter(types = emptySet()) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testPeekCondition_suppressesOnlyPeek() {
+ withCondition(TestCondition(types = setOf(PEEK)) { true }) {
+ assertPeekSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testPeekFilter_suppressesOnlyPeek() {
+ withFilter(TestFilter(types = setOf(PEEK)) { true }) {
+ assertPeekSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testPulseCondition_suppressesOnlyPulse() {
+ withCondition(TestCondition(types = setOf(PULSE)) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testPulseFilter_suppressesOnlyPulse() {
+ withFilter(TestFilter(types = setOf(PULSE)) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseSuppressed()
+ assertBubbleNotSuppressed()
+ }
+ }
+
+ @Test
+ fun testBubbleCondition_suppressesOnlyBubble() {
+ withCondition(TestCondition(types = setOf(BUBBLE)) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleSuppressed()
+ }
+ }
+
+ @Test
+ fun testBubbleFilter_suppressesOnlyBubble() {
+ withFilter(TestFilter(types = setOf(BUBBLE)) { true }) {
+ assertPeekNotSuppressed()
+ assertPulseNotSuppressed()
+ assertBubbleSuppressed()
+ }
+ }
+
+ @Test
+ fun testCondition_differentState() {
+ ensurePeekState()
+ val entry = buildPeekEntry()
+
+ var stateShouldSuppress = false
+ withCondition(TestCondition(types = setOf(PEEK)) { stateShouldSuppress }) {
+ assertShouldHeadsUp(entry)
+
+ stateShouldSuppress = true
+ assertShouldNotHeadsUp(entry)
+
+ stateShouldSuppress = false
+ assertShouldHeadsUp(entry)
+ }
+ }
+
+ @Test
+ fun testFilter_differentState() {
+ ensurePeekState()
+ val entry = buildPeekEntry()
+
+ var stateShouldSuppress = false
+ withFilter(TestFilter(types = setOf(PEEK)) { stateShouldSuppress }) {
+ assertShouldHeadsUp(entry)
+
+ stateShouldSuppress = true
+ assertShouldNotHeadsUp(entry)
+
+ stateShouldSuppress = false
+ assertShouldHeadsUp(entry)
+ }
+ }
+
+ @Test
+ fun testFilter_differentNotif() {
+ ensurePeekState()
+
+ val suppressedEntry = buildPeekEntry()
+ val unsuppressedEntry = buildPeekEntry()
+
+ withFilter(TestFilter(types = setOf(PEEK)) { it == suppressedEntry }) {
+ assertShouldNotHeadsUp(suppressedEntry)
+ assertShouldHeadsUp(unsuppressedEntry)
+ }
+ }
+
+ private fun assertPeekSuppressed() {
+ ensurePeekState()
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ private fun assertPeekNotSuppressed() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry())
+ }
+
+ private fun assertPulseSuppressed() {
+ ensurePulseState()
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+
+ private fun assertPulseNotSuppressed() {
+ ensurePulseState()
+ assertShouldHeadsUp(buildPulseEntry())
+ }
+
+ private fun assertBubbleSuppressed() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+
+ private fun assertBubbleNotSuppressed() {
+ ensureBubbleState()
+ assertShouldBubble(buildBubbleEntry())
+ }
+
+ private fun withCondition(condition: VisualInterruptionCondition, block: () -> Unit) {
+ provider.addCondition(condition)
+ block()
+ provider.removeCondition(condition)
+ }
+
+ private fun withFilter(filter: VisualInterruptionFilter, block: () -> Unit) {
+ provider.addFilter(filter)
+ block()
+ provider.removeFilter(filter)
+ }
+
+ private class TestCondition(
+ types: Set<VisualInterruptionType>,
+ val onShouldSuppress: () -> Boolean
+ ) : VisualInterruptionCondition(types = types, reason = "") {
+ override fun shouldSuppress(): Boolean = onShouldSuppress()
+ }
+
+ private class TestFilter(
+ types: Set<VisualInterruptionType>,
+ val onShouldSuppress: (NotificationEntry) -> Boolean = { true }
+ ) : VisualInterruptionFilter(types = types, reason = "") {
+ override fun shouldSuppress(entry: NotificationEntry) = onShouldSuppress(entry)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 6f4bbd5..7f12b22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -1,20 +1,48 @@
+/*
+ * Copyright (C) 2023 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.interruption
import android.app.ActivityManager
import android.app.Notification
import android.app.Notification.BubbleMetadata
+import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.GROUP_ALERT_ALL
+import android.app.Notification.GROUP_ALERT_CHILDREN
+import android.app.Notification.GROUP_ALERT_SUMMARY
+import android.app.Notification.VISIBILITY_PRIVATE
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.IMPORTANCE_LOW
+import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT
+import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK
import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_MUTABLE
+import android.content.Context
import android.content.Intent
import android.content.pm.UserInfo
import android.graphics.drawable.Icon
import android.hardware.display.FakeAmbientDisplayConfiguration
-import android.os.Handler
+import android.os.Looper
import android.os.PowerManager
+import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
+import android.provider.Settings.Global.HEADS_UP_OFF
+import android.provider.Settings.Global.HEADS_UP_ON
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -23,16 +51,22 @@
import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.leaks.FakeBatteryController
import com.android.systemui.utils.leaks.LeakCheckedTest
+import com.android.systemui.utils.os.FakeHandler
+import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -45,172 +79,662 @@
protected val batteryController = FakeBatteryController(leakCheck)
protected val deviceProvisionedController: DeviceProvisionedController = mock()
protected val flags: NotifPipelineFlags = mock()
+ protected val globalSettings = FakeGlobalSettings()
protected val headsUpManager: HeadsUpManager = mock()
protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
mock()
protected val keyguardStateController: KeyguardStateController = mock()
protected val logger: NotificationInterruptLogger = mock()
- protected val mainHandler: Handler = mock()
+ protected val mainHandler = FakeHandler(Looper.getMainLooper())
protected val powerManager: PowerManager = mock()
protected val statusBarStateController = FakeStatusBarStateController()
+ protected val systemClock = FakeSystemClock()
protected val uiEventLogger = UiEventLoggerFake()
protected val userTracker = FakeUserTracker()
protected abstract val provider: VisualInterruptionDecisionProvider
+ private val neverSuppresses = object : NotificationInterruptSuppressor {}
+
+ private val alwaysSuppressesInterruptions =
+ object : NotificationInterruptSuppressor {
+ override fun suppressInterruptions(entry: NotificationEntry?) = true
+ }
+
+ private val alwaysSuppressesAwakeInterruptions =
+ object : NotificationInterruptSuppressor {
+ override fun suppressAwakeInterruptions(entry: NotificationEntry?) = true
+ }
+
+ private val alwaysSuppressesAwakeHeadsUp =
+ object : NotificationInterruptSuppressor {
+ override fun suppressAwakeHeadsUp(entry: NotificationEntry?) = true
+ }
+
@Before
fun setUp() {
+ globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON)
+
val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
userTracker.set(listOf(user), /* currentUserIndex = */ 0)
- whenever(headsUpManager.isSnoozed(any())).thenReturn(false)
whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
.thenReturn(false)
+
+ provider.start()
}
@Test
fun testShouldPeek() {
- ensureStateForPeek()
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry())
+ }
- assertTrue(provider.makeUnloggedHeadsUpDecision(createPeekEntry()).shouldInterrupt)
+ @Test
+ fun testShouldNotPeek_settingDisabled() {
+ ensurePeekState { hunSettingEnabled = false }
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_packageSnoozed() {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldPeek_packageSnoozedButFsi() {
+ ensurePeekState { hunSnoozed = true }
+ assertShouldHeadsUp(buildFsiEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_alreadyBubbled() {
+ ensurePeekState { statusBarState = SHADE }
+ assertShouldNotHeadsUp(buildPeekEntry { isBubble = true })
+ }
+
+ @Test
+ fun testShouldPeek_isBubble_shadeLocked() {
+ ensurePeekState { statusBarState = SHADE_LOCKED }
+ assertShouldHeadsUp(buildPeekEntry { isBubble = true })
+ }
+
+ @Test
+ fun testShouldPeek_isBubble_keyguard() {
+ ensurePeekState { statusBarState = KEYGUARD }
+ assertShouldHeadsUp(buildPeekEntry { isBubble = true })
+ }
+
+ @Test
+ fun testShouldNotPeek_dnd() {
+ ensurePeekState()
+ assertShouldNotHeadsUp(buildPeekEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK })
+ }
+
+ @Test
+ fun testShouldNotPeek_notImportant() {
+ ensurePeekState()
+ assertShouldNotHeadsUp(buildPeekEntry { importance = IMPORTANCE_DEFAULT })
+ }
+
+ @Test
+ fun testShouldNotPeek_screenOff() {
+ ensurePeekState { isScreenOn = false }
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_dreaming() {
+ ensurePeekState { isDreaming = true }
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_oldWhen() {
+ ensurePeekState()
+ assertShouldNotHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) })
+ }
+
+ @Test
+ fun testShouldPeek_notQuiteOldEnoughWhen() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS - 1) })
+ }
+
+ @Test
+ fun testShouldPeek_zeroWhen() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildPeekEntry { whenMs = 0L })
+ }
+
+ @Test
+ fun testShouldPeek_oldWhenButFsi() {
+ ensurePeekState()
+ assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) })
+ }
+
+ @Test
+ fun testShouldPeek_defaultLegacySuppressor() {
+ ensurePeekState()
+ provider.addLegacySuppressor(neverSuppresses)
+ assertShouldHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_legacySuppressInterruptions() {
+ ensurePeekState()
+ provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_legacySuppressAwakeInterruptions() {
+ ensurePeekState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+ assertShouldNotHeadsUp(buildPeekEntry())
+ }
+
+ @Test
+ fun testShouldNotPeek_legacySuppressAwakeHeadsUp() {
+ ensurePeekState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+ assertShouldNotHeadsUp(buildPeekEntry())
}
@Test
fun testShouldPulse() {
- ensureStateForPulse()
-
- assertTrue(provider.makeUnloggedHeadsUpDecision(createPulseEntry()).shouldInterrupt)
+ ensurePulseState()
+ assertShouldHeadsUp(buildPulseEntry())
}
@Test
- fun testShouldFsi_awake() {
- ensureStateForAwakeFsi()
+ fun testShouldPulse_defaultLegacySuppressor() {
+ ensurePulseState()
+ provider.addLegacySuppressor(neverSuppresses)
+ assertShouldHeadsUp(buildPulseEntry())
+ }
- assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+ @Test
+ fun testShouldNotPulse_legacySuppressInterruptions() {
+ ensurePulseState()
+ provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldPulse_legacySuppressAwakeInterruptions() {
+ ensurePulseState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+ assertShouldHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldPulse_legacySuppressAwakeHeadsUp() {
+ ensurePulseState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+ assertShouldHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldNotPulse_disabled() {
+ ensurePulseState { pulseOnNotificationsEnabled = false }
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldNotPulse_batterySaver() {
+ ensurePulseState { isAodPowerSave = true }
+ assertShouldNotHeadsUp(buildPulseEntry())
+ }
+
+ @Test
+ fun testShouldNotPulse_effectSuppressed() {
+ ensurePulseState()
+ assertShouldNotHeadsUp(
+ buildPulseEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_AMBIENT }
+ )
+ }
+
+ @Test
+ fun testShouldNotPulse_visibilityOverridePrivate() {
+ ensurePulseState()
+ assertShouldNotHeadsUp(buildPulseEntry { visibilityOverride = VISIBILITY_PRIVATE })
+ }
+
+ @Test
+ fun testShouldNotPulse_importanceLow() {
+ ensurePulseState()
+ assertShouldNotHeadsUp(buildPulseEntry { importance = IMPORTANCE_LOW })
+ }
+
+ private fun withPeekAndPulseEntry(
+ extendEntry: EntryBuilder.() -> Unit,
+ block: (NotificationEntry) -> Unit
+ ) {
+ ensurePeekState()
+ block(buildPeekEntry(extendEntry))
+
+ ensurePulseState()
+ block(buildPulseEntry(extendEntry))
+ }
+
+ @Test
+ fun testShouldHeadsUp_groupedSummaryNotif_groupAlertAll() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_ALL
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_groupedSummaryNotif_groupAlertSummary() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldNotHeadsUp_groupedSummaryNotif_groupAlertChildren() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }) {
+ assertShouldNotHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_ungroupedSummaryNotif_groupAlertChildren() {
+ withPeekAndPulseEntry({
+ isGrouped = false
+ isGroupSummary = true
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_groupedChildNotif_groupAlertAll() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_ALL
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_groupedChildNotif_groupAlertChildren() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_CHILDREN
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldNotHeadsUp_groupedChildNotif_groupAlertSummary() {
+ withPeekAndPulseEntry({
+ isGrouped = true
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
+ }) {
+ assertShouldNotHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldHeadsUp_ungroupedChildNotif_groupAlertSummary() {
+ withPeekAndPulseEntry({
+ isGrouped = false
+ isGroupSummary = false
+ groupAlertBehavior = GROUP_ALERT_SUMMARY
+ }) {
+ assertShouldHeadsUp(it)
+ }
+ }
+
+ @Test
+ fun testShouldNotHeadsUp_justLaunchedFsi() {
+ withPeekAndPulseEntry({ hasJustLaunchedFsi = true }) { assertShouldNotHeadsUp(it) }
+ }
+
+ @Test
+ fun testShouldBubble_withIntentAndIcon() {
+ ensureBubbleState()
+ assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = false })
+ }
+
+ @Test
+ fun testShouldBubble_withShortcut() {
+ ensureBubbleState()
+ assertShouldBubble(buildBubbleEntry { bubbleIsShortcut = true })
+ }
+
+ @Test
+ fun testShouldNotBubble_notAllowed() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { canBubble = false })
+ }
+
+ @Test
+ fun testShouldNotBubble_noBubbleMetadata() {
+ ensureBubbleState()
+ assertShouldNotBubble(buildBubbleEntry { hasBubbleMetadata = false })
+ }
+
+ @Test
+ fun testShouldBubble_defaultLegacySuppressor() {
+ ensureBubbleState()
+ provider.addLegacySuppressor(neverSuppresses)
+ assertShouldBubble(buildBubbleEntry())
+ }
+
+ @Test
+ fun testShouldNotBubble_legacySuppressInterruptions() {
+ ensureBubbleState()
+ provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+
+ @Test
+ fun testShouldNotBubble_legacySuppressAwakeInterruptions() {
+ ensureBubbleState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+
+ @Test
+ fun testShouldBubble_legacySuppressAwakeHeadsUp() {
+ ensureBubbleState()
+ provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+ assertShouldBubble(buildBubbleEntry())
+ }
+
+ @Test
+ fun testShouldNotAlert_hiddenOnKeyguard() {
+ ensurePeekState({ keyguardShouldHideNotification = true })
+ assertShouldNotHeadsUp(buildPeekEntry())
+
+ ensurePulseState({ keyguardShouldHideNotification = true })
+ assertShouldNotHeadsUp(buildPulseEntry())
+
+ ensureBubbleState({ keyguardShouldHideNotification = true })
+ assertShouldNotBubble(buildBubbleEntry())
+ }
+
+ @Test
+ fun testShouldFsi_notInteractive() {
+ ensureNotInteractiveFsiState()
+ assertShouldFsi(buildFsiEntry())
}
@Test
fun testShouldFsi_dreaming() {
- ensureStateForDreamingFsi()
-
- assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+ ensureDreamingFsiState()
+ assertShouldFsi(buildFsiEntry())
}
@Test
fun testShouldFsi_keyguard() {
- ensureStateForKeyguardFsi()
-
- assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+ ensureKeyguardFsiState()
+ assertShouldFsi(buildFsiEntry())
}
- @Test
- fun testShouldBubble() {
- assertTrue(provider.makeAndLogBubbleDecision(createBubbleEntry()).shouldInterrupt)
- }
+ protected data class State(
+ var hunSettingEnabled: Boolean? = null,
+ var hunSnoozed: Boolean? = null,
+ var isAodPowerSave: Boolean? = null,
+ var isDozing: Boolean? = null,
+ var isDreaming: Boolean? = null,
+ var isInteractive: Boolean? = null,
+ var isScreenOn: Boolean? = null,
+ var keyguardShouldHideNotification: Boolean? = null,
+ var pulseOnNotificationsEnabled: Boolean? = null,
+ var statusBarState: Int? = null,
+ )
- private fun ensureStateForPeek() {
- whenever(powerManager.isScreenOn).thenReturn(true)
- statusBarStateController.dozing = false
- statusBarStateController.dreaming = false
- }
-
- private fun ensureStateForPulse() {
- ambientDisplayConfiguration.fakePulseOnNotificationEnabled = true
- batteryController.setIsAodPowerSave(false)
- statusBarStateController.dozing = true
- }
-
- private fun ensureStateForAwakeFsi() {
- whenever(powerManager.isInteractive).thenReturn(false)
- statusBarStateController.dreaming = false
- statusBarStateController.state = SHADE
- }
-
- private fun ensureStateForDreamingFsi() {
- whenever(powerManager.isInteractive).thenReturn(true)
- statusBarStateController.dreaming = true
- statusBarStateController.state = SHADE
- }
-
- private fun ensureStateForKeyguardFsi() {
- whenever(powerManager.isInteractive).thenReturn(true)
- statusBarStateController.dreaming = false
- statusBarStateController.state = KEYGUARD
- }
-
- private fun createNotif(
- hasFsi: Boolean = false,
- bubbleMetadata: BubbleMetadata? = null
- ): Notification {
- return Notification.Builder(context, TEST_CHANNEL_ID)
- .apply {
- setContentTitle(TEST_CONTENT_TITLE)
- setContentText(TEST_CONTENT_TEXT)
-
- if (hasFsi) {
- setFullScreenIntent(mock(), /* highPriority = */ true)
- }
-
- if (bubbleMetadata != null) {
- setBubbleMetadata(bubbleMetadata)
- }
+ protected fun setState(state: State): Unit =
+ state.run {
+ hunSettingEnabled?.let {
+ val newSetting = if (it) HEADS_UP_ON else HEADS_UP_OFF
+ globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, newSetting)
}
- .setContentTitle(TEST_CONTENT_TITLE)
- .setContentText(TEST_CONTENT_TEXT)
- .build()
- }
- private fun createBubbleMetadata(): BubbleMetadata {
- val pendingIntent =
- PendingIntent.getActivity(
- context,
- /* requestCode = */ 0,
- Intent().setPackage(context.packageName),
- FLAG_MUTABLE
- )
+ hunSnoozed?.let { whenever(headsUpManager.isSnoozed(TEST_PACKAGE)).thenReturn(it) }
- val icon = Icon.createWithResource(context.resources, R.drawable.android)
+ isAodPowerSave?.let { batteryController.setIsAodPowerSave(it) }
- return BubbleMetadata.Builder(pendingIntent, icon).build()
- }
+ isDozing?.let { statusBarStateController.dozing = it }
- private fun createEntry(
- notif: Notification,
- importance: Int = IMPORTANCE_DEFAULT,
- canBubble: Boolean? = null
- ): NotificationEntry {
- return NotificationEntryBuilder()
- .apply {
- setPkg(TEST_PACKAGE)
- setOpPkg(TEST_PACKAGE)
- setTag(TEST_TAG)
- setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
- setNotification(notif)
- setImportance(importance)
+ isDreaming?.let { statusBarStateController.dreaming = it }
- if (canBubble != null) {
- setCanBubble(canBubble)
- }
+ isInteractive?.let { whenever(powerManager.isInteractive).thenReturn(it) }
+
+ isScreenOn?.let { whenever(powerManager.isScreenOn).thenReturn(it) }
+
+ keyguardShouldHideNotification?.let {
+ whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
+ .thenReturn(it)
}
- .build()
- }
- private fun createPeekEntry() = createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH)
+ pulseOnNotificationsEnabled?.let {
+ ambientDisplayConfiguration.fakePulseOnNotificationEnabled = it
+ }
- private fun createPulseEntry() =
- createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH).also {
- modifyRanking(it).setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()
+ statusBarState?.let { statusBarStateController.state = it }
}
- private fun createFsiEntry() =
- createEntry(notif = createNotif(hasFsi = true), importance = IMPORTANCE_HIGH)
+ protected fun ensureState(block: State.() -> Unit) =
+ State()
+ .apply {
+ keyguardShouldHideNotification = false
+ apply(block)
+ }
+ .run(this::setState)
- private fun createBubbleEntry() =
- createEntry(
- notif = createNotif(bubbleMetadata = createBubbleMetadata()),
- importance = IMPORTANCE_HIGH,
- canBubble = true
- )
+ protected fun ensurePeekState(block: State.() -> Unit = {}) = ensureState {
+ hunSettingEnabled = true
+ hunSnoozed = false
+ isDozing = false
+ isDreaming = false
+ isScreenOn = true
+ run(block)
+ }
+
+ protected fun ensurePulseState(block: State.() -> Unit = {}) = ensureState {
+ isAodPowerSave = false
+ isDozing = true
+ pulseOnNotificationsEnabled = true
+ run(block)
+ }
+
+ protected fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block)
+
+ protected fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState {
+ isDreaming = false
+ isInteractive = false
+ statusBarState = SHADE
+ run(block)
+ }
+
+ protected fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState {
+ isDreaming = true
+ isInteractive = true
+ statusBarState = SHADE
+ run(block)
+ }
+
+ protected fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
+ isDreaming = false
+ isInteractive = true
+ statusBarState = KEYGUARD
+ run(block)
+ }
+
+ protected fun assertShouldHeadsUp(entry: NotificationEntry) =
+ provider.makeUnloggedHeadsUpDecision(entry).let {
+ assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected fun assertShouldNotHeadsUp(entry: NotificationEntry) =
+ provider.makeUnloggedHeadsUpDecision(entry).let {
+ assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected fun assertShouldBubble(entry: NotificationEntry) =
+ provider.makeAndLogBubbleDecision(entry).let {
+ assertTrue("unexpected suppressed bubble: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected fun assertShouldNotBubble(entry: NotificationEntry) =
+ provider.makeAndLogBubbleDecision(entry).let {
+ assertFalse("unexpected unsuppressed bubble: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected fun assertShouldFsi(entry: NotificationEntry) =
+ provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected fun assertShouldNotFsi(entry: NotificationEntry) =
+ provider.makeUnloggedFullScreenIntentDecision(entry).let {
+ assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt)
+ }
+
+ protected class EntryBuilder(val context: Context) {
+ var importance = IMPORTANCE_DEFAULT
+ var suppressedVisualEffects: Int? = null
+ var whenMs: Long? = null
+ var visibilityOverride: Int? = null
+ var hasFsi = false
+ var canBubble: Boolean? = null
+ var isBubble = false
+ var hasBubbleMetadata = false
+ var bubbleIsShortcut = false
+ var bubbleSuppressesNotification: Boolean? = null
+ var isGrouped = false
+ var isGroupSummary: Boolean? = null
+ var groupAlertBehavior: Int? = null
+ var hasJustLaunchedFsi = false
+
+ private fun buildBubbleMetadata(): BubbleMetadata {
+ val builder =
+ if (bubbleIsShortcut) {
+ BubbleMetadata.Builder(context.packageName + ":test_shortcut_id")
+ } else {
+ BubbleMetadata.Builder(
+ PendingIntent.getActivity(
+ context,
+ /* requestCode = */ 0,
+ Intent().setPackage(context.packageName),
+ FLAG_MUTABLE
+ ),
+ Icon.createWithResource(context.resources, R.drawable.android)
+ )
+ }
+
+ bubbleSuppressesNotification?.let { builder.setSuppressNotification(it) }
+
+ return builder.build()
+ }
+
+ fun build() =
+ Notification.Builder(context, TEST_CHANNEL_ID)
+ .apply {
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+
+ if (hasFsi) {
+ setFullScreenIntent(mock(), /* highPriority = */ true)
+ }
+
+ whenMs?.let { setWhen(it) }
+
+ if (hasBubbleMetadata) {
+ setBubbleMetadata(buildBubbleMetadata())
+ }
+
+ if (isGrouped) {
+ setGroup(TEST_GROUP_KEY)
+ }
+
+ isGroupSummary?.let { setGroupSummary(it) }
+
+ groupAlertBehavior?.let { setGroupAlertBehavior(it) }
+ }
+ .build()
+ .apply {
+ if (isBubble) {
+ flags = flags or FLAG_BUBBLE
+ }
+ }
+ .let { NotificationEntryBuilder().setNotification(it) }
+ .apply {
+ setPkg(TEST_PACKAGE)
+ setOpPkg(TEST_PACKAGE)
+ setTag(TEST_TAG)
+
+ setImportance(importance)
+ setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
+
+ canBubble?.let { setCanBubble(it) }
+ }
+ .build()!!
+ .also {
+ if (hasJustLaunchedFsi) {
+ it.notifyFullScreenIntentLaunched()
+ }
+
+ modifyRanking(it)
+ .apply {
+ suppressedVisualEffects?.let { setSuppressedVisualEffects(it) }
+ visibilityOverride?.let { setVisibilityOverride(it) }
+ }
+ .build()
+ }
+ }
+
+ protected fun buildEntry(block: EntryBuilder.() -> Unit) =
+ EntryBuilder(context).also(block).build()
+
+ protected fun buildPeekEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ importance = IMPORTANCE_HIGH
+ run(block)
+ }
+
+ protected fun buildPulseEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ importance = IMPORTANCE_DEFAULT
+ visibilityOverride = VISIBILITY_NO_OVERRIDE
+ run(block)
+ }
+
+ protected fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ canBubble = true
+ hasBubbleMetadata = true
+ run(block)
+ }
+
+ protected fun buildFsiEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+ importance = IMPORTANCE_HIGH
+ hasFsi = true
+ run(block)
+ }
+
+ private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
}
private const val TEST_CONTENT_TITLE = "Test Content Title"
@@ -219,3 +743,4 @@
private const val TEST_CHANNEL_NAME = "Test Channel"
private const val TEST_PACKAGE = "test_package"
private const val TEST_TAG = "test_tag"
+private const val TEST_GROUP_KEY = "test_group_key"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 60421c98..3a9d111 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -314,7 +314,26 @@
sharedNotificationContainerInteractor.setTopPosition(10f)
assertThat(position)
- .isEqualTo(SharedNotificationContainerPosition(top = 10f, bottom = 0f))
+ .isEqualTo(
+ SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = true)
+ )
+ }
+
+ @Test
+ fun positionOnQS() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen with shade expanded
+ showLockscreenWithQSExpanded()
+
+ // When not in split shade
+ sharedNotificationContainerInteractor.setTopPosition(10f)
+
+ assertThat(position)
+ .isEqualTo(
+ SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = false)
+ )
}
@Test
@@ -390,6 +409,17 @@
)
}
+ private suspend fun TestScope.showLockscreenWithQSExpanded() {
+ shadeRepository.setLockscreenShadeExpansion(0f)
+ shadeRepository.setQsExpansion(1f)
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ this,
+ )
+ }
+
@SysUISingleton
@Component(
modules =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 05fd6d2..4a20f83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -20,6 +20,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -51,7 +53,6 @@
import android.app.WallpaperManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.IntentFilter;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -180,11 +181,16 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.SystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -198,8 +204,6 @@
import javax.inject.Provider;
-import dagger.Lazy;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -319,6 +323,7 @@
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
private final FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -349,8 +354,10 @@
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
+ mFakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
+
mNotificationInterruptStateProvider =
- new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ new TestableNotificationInterruptStateProviderImpl(
mPowerManager,
mAmbientDisplayConfiguration,
mStatusBarStateController,
@@ -363,7 +370,9 @@
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
mUserTracker,
- mDeviceProvisionedController);
+ mDeviceProvisionedController,
+ mFakeSystemClock,
+ mFakeGlobalSettings);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1162,7 +1171,6 @@
NotificationInterruptStateProviderImpl {
TestableNotificationInterruptStateProviderImpl(
- ContentResolver contentResolver,
PowerManager powerManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
StatusBarStateController controller,
@@ -1175,9 +1183,10 @@
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
UserTracker userTracker,
- DeviceProvisionedController deviceProvisionedController) {
+ DeviceProvisionedController deviceProvisionedController,
+ SystemClock systemClock,
+ GlobalSettings globalSettings) {
super(
- contentResolver,
powerManager,
ambientDisplayConfiguration,
batteryController,
@@ -1190,7 +1199,9 @@
keyguardNotificationVisibilityProvider,
uiEventLogger,
userTracker,
- deviceProvisionedController
+ deviceProvisionedController,
+ systemClock,
+ globalSettings
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
index 8fb5ff8..ba34ce6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.tracing
+package com.android.app.tracing
import android.os.Handler
import android.os.Looper
@@ -76,7 +76,7 @@
@Test
fun testLongTraceSection_doesNotThrow_whenUsingHelper() {
traceSection(SECTION_NAME_THATS_TOO_LONG) {
- Log.v(TAG, "com.android.systemui.tracing.traceSection() block.")
+ Log.v(TAG, "com.android.app.tracing.traceSection() block.")
}
}
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 ec808c7..8309b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -18,6 +18,8 @@
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
@@ -157,6 +159,8 @@
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -476,7 +480,6 @@
mKeyguardStateController,
mScreenOffAnimationController,
mAuthController,
- mShadeExpansionStateManager,
() -> mShadeInteractor,
mShadeWindowLogger,
() -> mSelectedUserInteractor
@@ -507,8 +510,11 @@
when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
Collections.singletonList(mock(UserInfo.class)));
+ final FakeGlobalSettings fakeGlobalSettings = new FakeGlobalSettings();
+ fakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
+
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
- new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ new TestableNotificationInterruptStateProviderImpl(
mock(PowerManager.class),
mock(AmbientDisplayConfiguration.class),
mock(StatusBarStateController.class),
@@ -521,7 +527,9 @@
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
mock(UserTracker.class),
- mock(DeviceProvisionedController.class)
+ mock(DeviceProvisionedController.class),
+ mock(SystemClock.class),
+ fakeGlobalSettings
);
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 0df235d..975555c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.wmshell;
-import android.content.ContentResolver;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
@@ -32,12 +31,13 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
public class TestableNotificationInterruptStateProviderImpl
extends NotificationInterruptStateProviderImpl {
TestableNotificationInterruptStateProviderImpl(
- ContentResolver contentResolver,
PowerManager powerManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
StatusBarStateController statusBarStateController,
@@ -50,8 +50,10 @@
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
UserTracker userTracker,
- DeviceProvisionedController deviceProvisionedController) {
- super(contentResolver,
+ DeviceProvisionedController deviceProvisionedController,
+ SystemClock systemClock,
+ GlobalSettings globalSettings) {
+ super(
powerManager,
ambientDisplayConfiguration,
batteryController,
@@ -64,7 +66,9 @@
keyguardNotificationVisibilityProvider,
uiEventLogger,
userTracker,
- deviceProvisionedController);
+ deviceProvisionedController,
+ systemClock,
+ globalSettings);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index e1c6dde..799bb40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,8 +1,17 @@
package com.android.systemui.communal.data.repository
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import kotlinx.coroutines.flow.MutableStateFlow
+
/** Fake implementation of [CommunalRepository]. */
-class FakeCommunalRepository : CommunalRepository {
- override var isCommunalEnabled = false
+class FakeCommunalRepository(
+ override var isCommunalEnabled: Boolean = false,
+ override val desiredScene: MutableStateFlow<CommunalSceneKey> =
+ MutableStateFlow(CommunalSceneKey.Blank)
+) : CommunalRepository {
+ override fun setDesiredScene(desiredScene: CommunalSceneKey) {
+ this.desiredScene.value = desiredScene
+ }
fun setIsCommunalEnabled(value: Boolean) {
isCommunalEnabled = value
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
new file mode 100644
index 0000000..13910fd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.data.repository
+
+import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
+
+ private val defaults: MutableMap<DefaultsKey, CustomTileDefaults> = mutableMapOf()
+ private val defaultsFlow = MutableSharedFlow<DefaultsRequest>()
+
+ private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
+ val defaultsRequests: List<DefaultsRequest> = mutableDefaultsRequests
+
+ override fun defaults(user: UserHandle): Flow<CustomTileDefaults> =
+ defaultsFlow
+ .distinctUntilChanged { old, new ->
+ if (new.force) {
+ false
+ } else {
+ old == new
+ }
+ }
+ .map { defaults[DefaultsKey(it.user, it.componentName)]!! }
+
+ override fun requestNewDefaults(
+ user: UserHandle,
+ componentName: ComponentName,
+ force: Boolean
+ ) {
+ val request = DefaultsRequest(user, componentName, force)
+ mutableDefaultsRequests.add(request)
+ defaultsFlow.tryEmit(request)
+ }
+
+ fun putDefaults(
+ user: UserHandle,
+ componentName: ComponentName,
+ customTileDefaults: CustomTileDefaults,
+ ) {
+ defaults[DefaultsKey(user, componentName)] = customTileDefaults
+ }
+
+ fun removeDefaults(user: UserHandle, componentName: ComponentName) {
+ defaults.remove(DefaultsKey(user, componentName))
+ }
+
+ data class DefaultsRequest(
+ val user: UserHandle,
+ val componentName: ComponentName,
+ val force: Boolean = false,
+ )
+
+ private data class DefaultsKey(val user: UserHandle, val componentName: ComponentName)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt
new file mode 100644
index 0000000..8f972f5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTilePackageUpdatesRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.tiles.impl.custom.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeCustomTilePackageUpdatesRepository : CustomTilePackageUpdatesRepository {
+
+ private val mutablePackageChanges = MutableSharedFlow<Unit>()
+
+ override val packageChanges: Flow<Unit>
+ get() = mutablePackageChanges
+
+ suspend fun emitPackageChange() {
+ mutablePackageChanges.emit(Unit)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index bdddc04..6beb513 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -131,6 +131,7 @@
SceneKey.Lockscreen,
SceneKey.Bouncer,
SceneKey.Gone,
+ SceneKey.Communal,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 3c49c58..800593f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -56,6 +56,14 @@
@Deprecated("Use ShadeInteractor instead")
override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer
+ private val _legacyIsQsExpanded = MutableStateFlow(false)
+ @Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+
+ @Deprecated("Use ShadeInteractor instead")
+ override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
+ _legacyIsQsExpanded.value = legacyIsQsExpanded
+ }
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyExpandedOrAwaitingInputTransfer(
legacyExpandedOrAwaitingInputTransfer: Boolean
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
index 19fdb6d..a65813a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
@@ -104,7 +104,7 @@
override fun isDreaming() = dreaming
- override fun setIsDreaming(drreaming: Boolean): Boolean {
+ override fun setIsDreaming(dreaming: Boolean): Boolean {
dreaming != this.dreaming || return false
this.dreaming = dreaming
callbacks.forEach { it.onDreamingChanged(dreaming) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 7e0632b..efccafc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -22,47 +22,47 @@
* be null"). To fix this, we can use methods that modify the return type to be nullable. This
* causes Kotlin to skip the null checks.
*/
-
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
+import org.mockito.MockSettings
import org.mockito.Mockito
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.withSettings
import org.mockito.stubbing.OngoingStubbing
import org.mockito.stubbing.Stubber
/**
- * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> eq(obj: T): T = Mockito.eq<T>(obj) ?: obj
/**
- * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
+
inline fun <reified T> any(): T = any(T::class.java)
/**
- * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when
- * null is returned.
+ * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when null is
+ * returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
fun <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher)
-/**
- * Kotlin type-inferred version of Mockito.nullable()
- */
+/** Kotlin type-inferred version of Mockito.nullable() */
inline fun <reified T> nullable(): T? = Mockito.nullable(T::class.java)
/**
- * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException
- * when null is returned.
+ * Returns ArgumentCaptor.capture() as nullable type to avoid java.lang.IllegalStateException when
+ * null is returned.
*
* Generic T is nullable because implicitly bounded by Any?.
*/
@@ -74,7 +74,7 @@
* Generic T is nullable because implicitly bounded by Any?.
*/
inline fun <reified T : Any> argumentCaptor(): ArgumentCaptor<T> =
- ArgumentCaptor.forClass(T::class.java)
+ ArgumentCaptor.forClass(T::class.java)
/**
* Helper function for creating new mocks, without the need to pass in a [Class] instance.
@@ -83,8 +83,8 @@
*
* @param apply builder function to simplify stub configuration by improving type inference.
*/
-inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java)
- .apply(apply)
+inline fun <reified T : Any> mock(settings: MockSettings? = null, apply: T.() -> Unit = {}): T =
+ Mockito.mock(T::class.java, settings ?: withSettings()).apply(apply)
/**
* Helper function for stubbing methods without the need to use backticks.
@@ -92,6 +92,7 @@
* @see Mockito.when
*/
fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
+
fun <T> Stubber.whenever(mock: T): T = `when`(mock)
/**
@@ -115,34 +116,32 @@
* Generic T is nullable because implicitly bounded by Any?.
*/
inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
- KotlinArgumentCaptor(T::class.java)
+ KotlinArgumentCaptor(T::class.java)
/**
* Helper function for creating and using a single-use ArgumentCaptor in kotlin.
*
- * val captor = argumentCaptor<Foo>()
- * verify(...).someMethod(captor.capture())
- * val captured = captor.value
+ * val captor = argumentCaptor<Foo>() verify(...).someMethod(captor.capture()) val captured =
+ * captor.value
*
* becomes:
*
- * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
+ * val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
*
* NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
*/
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
- kotlinArgumentCaptor<T>().apply { block() }.value
+ kotlinArgumentCaptor<T>().apply { block() }.value
/**
* Variant of [withArgCaptor] for capturing multiple arguments.
*
- * val captor = argumentCaptor<Foo>()
- * verify(...).someMethod(captor.capture())
- * val captured: List<Foo> = captor.allValues
+ * val captor = argumentCaptor<Foo>() verify(...).someMethod(captor.capture()) val captured:
+ * List<Foo> = captor.allValues
*
* becomes:
*
- * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
+ * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
*/
inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
- kotlinArgumentCaptor<T>().apply{ block() }.allValues
+ kotlinArgumentCaptor<T>().apply { block() }.allValues
diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml
index 301c9b8..43f8300 100644
--- a/packages/SystemUI/tools/lint/baseline.xml
+++ b/packages/SystemUI/tools/lint/baseline.xml
@@ -1271,17 +1271,6 @@
</issue>
<issue
- id="MergeRootFrame"
- message="This `<FrameLayout>` can be replaced with a `<merge>` tag"
- errorLine1="<FrameLayout"
- errorLine2="^">
- <location
- file="res/layout/volume_dnd_icon.xml"
- line="16"
- column="1"/>
- </issue>
-
- <issue
id="InefficientWeight"
message="Use a `layout_height` of `0dp` instead of `wrap_content` for better performance"
errorLine1=" android:layout_height="wrap_content""
@@ -88853,17 +88842,6 @@
errorLine1=" <ImageView"
errorLine2=" ^">
<location
- file="res/layout/volume_dnd_icon.xml"
- line="23"
- column="5"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="[Accessibility] Missing `contentDescription` attribute on image"
- errorLine1=" <ImageView"
- errorLine2=" ^">
- <location
file="res/layout/wireless_charging_layout.xml"
line="26"
column="5"/>
@@ -89783,15 +89761,4 @@
column="22"/>
</issue>
- <issue
- id="RtlHardcoded"
- message="Use "`end`" instead of "`right`" to ensure correct behavior in right-to-left locales"
- errorLine1=" android:layout_gravity="right|top""
- errorLine2=" ~~~~~~~~~">
- <location
- file="res/layout/volume_dnd_icon.xml"
- line="26"
- column="33"/>
- </issue>
-
</issues>
diff --git a/services/Android.bp b/services/Android.bp
index aca8409..f1534b4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -41,7 +41,10 @@
name: "system_optimized_java_defaults",
module_type: "java_defaults",
config_namespace: "ANDROID",
- bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
+ bool_variables: [
+ "SYSTEM_OPTIMIZE_JAVA",
+ "FULL_SYSTEM_OPTIMIZE_JAVA",
+ ],
properties: [
"optimize",
"dxflags",
@@ -56,6 +59,7 @@
enabled: true,
// TODO(b/210510433): Enable optimizations after improving
// retracing infra.
+ // See also FULL_SYSTEM_OPTIMIZE_JAVA.
optimize: false,
shrink: true,
ignore_warnings: false,
@@ -81,6 +85,12 @@
dxflags: ["--debug"],
},
},
+ // Allow form factors to opt-in full system java optimization
+ FULL_SYSTEM_OPTIMIZE_JAVA: {
+ optimize: {
+ optimize: true,
+ },
+ },
},
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 32666e7..bb50a99 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3406,7 +3406,8 @@
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()
- || userState.isMagnificationTwoFingerTripleTapEnabledLocked()
+ || (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ && userState.isMagnificationTwoFingerTripleTapEnabledLocked())
|| userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
@@ -3435,7 +3436,9 @@
return;
}
final boolean connect = (userState.isShortcutMagnificationEnabledLocked()
- || userState.isMagnificationSingleFingerTripleTapEnabledLocked())
+ || userState.isMagnificationSingleFingerTripleTapEnabledLocked()
+ || (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ && userState.isMagnificationTwoFingerTripleTapEnabledLocked()))
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
@@ -5133,6 +5136,8 @@
// Remove magnification button UI when the magnification capability is not all mode or
// magnification is disabled.
if (!(userState.isMagnificationSingleFingerTripleTapEnabledLocked()
+ || (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ && userState.isMagnificationTwoFingerTripleTapEnabledLocked())
|| userState.isShortcutMagnificationEnabledLocked())
|| userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index cfe2af9..5953d0d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -39,9 +39,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
+import com.android.server.accessibility.gestures.GestureMatcher;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -453,20 +456,45 @@
private final MagnificationGesturesObserver mGesturesObserver;
DetectingState(@UiContext Context context) {
- final MultiTap multiTap = new MultiTap(context, mDetectSingleFingerTripleTap ? 3 : 1,
- mDetectSingleFingerTripleTap
- ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
- : MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
- final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
- mDetectSingleFingerTripleTap ? 3 : 1,
- mDetectSingleFingerTripleTap
- ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
- : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
- mGesturesObserver = new MagnificationGesturesObserver(this,
- new SimpleSwipe(context),
- multiTap,
- multiTapAndHold,
- new TwoFingersDownOrSwipe(context));
+ if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+ final List<GestureMatcher> mGestureMatchers = new ArrayList<>();
+
+ mGestureMatchers.add(new SimpleSwipe(context));
+ // Observe single tap and single tap and hold to reduce response time when the
+ // user performs these two gestures inside the window magnifier.
+ mGestureMatchers.add(new MultiTap(context,
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP,
+ null));
+ mGestureMatchers.add(new MultiTapAndHold(context,
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD,
+ null));
+ mGestureMatchers.add(new TwoFingersDownOrSwipe(context));
+
+ mGesturesObserver = new MagnificationGesturesObserver(this,
+ mGestureMatchers.toArray(new GestureMatcher[mGestureMatchers.size()]));
+ } else {
+ final MultiTap multiTap = new MultiTap(context,
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
+ final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
+ ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
+ : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
+ mGesturesObserver = new MagnificationGesturesObserver(this,
+ new SimpleSwipe(context),
+ multiTap,
+ multiTapAndHold,
+ new TwoFingersDownOrSwipe(context));
+ }
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index a036383..ae8fddf 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -23,6 +23,7 @@
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -665,6 +666,13 @@
}
}
break;
+ case POLICY_TYPE_CLIPBOARD:
+ if (Flags.crossDeviceClipboard()) {
+ synchronized (mVirtualDeviceLock) {
+ mDevicePolicies.put(policyType, devicePolicy);
+ }
+ }
+ break;
default:
throw new IllegalArgumentException("Device policy " + policyType
+ " cannot be changed at runtime. ");
diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java
index f77430d..bfb3a38 100644
--- a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java
+++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionAllowlistManager.java
@@ -22,6 +22,8 @@
import android.annotation.Nullable;
import android.os.Handler;
import android.os.UserHandle;
+import android.service.contentcapture.IContentProtectionAllowlistCallback;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -52,6 +54,10 @@
@NonNull
final PackageMonitor mPackageMonitor;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull
+ final IContentProtectionAllowlistCallback mAllowlistCallback;
+
private final Object mHandlerToken = new Object();
private final Object mLock = new Object();
@@ -74,6 +80,7 @@
mHandler = handler;
mTimeoutMs = timeoutMs;
mPackageMonitor = createPackageMonitor();
+ mAllowlistCallback = createAllowlistCallback();
}
/** Starts the manager. */
@@ -107,7 +114,7 @@
return allowedPackages.contains(packageName);
}
- private void setAllowlist(@NonNull List<String> packages) {
+ private void handleUpdateAllowlistResponse(@NonNull List<String> packages) {
synchronized (mLock) {
mAllowedPackages = packages.stream().collect(Collectors.toUnmodifiableSet());
}
@@ -115,14 +122,14 @@
}
private void handleInitialUpdate() {
- handleUpdate();
+ handlePackagesChanged();
// Initial update done, start listening to package updates now
mPackageMonitor.register(
mContentCaptureManagerService.getContext(), UserHandle.ALL, mHandler);
}
- private void handleUpdate() {
+ private void handlePackagesChanged() {
if (!blocklistUpdateEnabled()) {
return;
}
@@ -145,6 +152,12 @@
// If there are any pending updates queued already, they can be removed immediately
mHandler.removeCallbacksAndMessages(mHandlerToken);
mUpdatePendingUntil = Instant.now().plusMillis(mTimeoutMs);
+
+ try {
+ remoteContentProtectionService.onUpdateAllowlistRequest(mAllowlistCallback);
+ } catch (Exception ex) {
+ Slog.e(TAG, "Failed to call remote service", ex);
+ }
}
/** @hide */
@@ -154,12 +167,28 @@
return new ContentProtectionPackageMonitor();
}
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected IContentProtectionAllowlistCallback createAllowlistCallback() {
+ return new ContentProtectionAllowlistCallback();
+ }
+
private final class ContentProtectionPackageMonitor extends PackageMonitor {
// This callback might be invoked multiple times, for more info refer to the comment above
@Override
public void onSomePackagesChanged() {
- handleUpdate();
+ handlePackagesChanged();
+ }
+ }
+
+ private final class ContentProtectionAllowlistCallback
+ extends IContentProtectionAllowlistCallback.Stub {
+
+ @Override
+ public void setAllowlist(List<String> packages) {
+ mHandler.post(() -> handleUpdateAllowlistResponse(packages));
}
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7dbf61b..a14f3fe 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -183,6 +183,7 @@
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
"cbor-java",
+ "com.android.media.audio-aconfig-java",
"dropbox_flags_lib",
"icu4j_calendar_astronomer",
"android.security.aaid_aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 638abdb..4f32220 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -300,14 +300,6 @@
@UserIdInt int userId, @NonNull String[] packageNames, boolean suspended);
/**
- * Suspend or unsuspend packages in a profile when quiet mode is toggled.
- *
- * @param userId The target user.
- * @param suspended Whether the packages should be suspended or unsuspended.
- */
- public abstract void setPackagesSuspendedForQuietMode(@UserIdInt int userId, boolean suspended);
-
- /**
* Get the information describing the dialog to be shown to the user when they try to launch a
* suspended application.
*
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index db89cac..c4cb816 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -179,6 +179,15 @@
3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(short_lived|1|1),(max_build_time|1|3),(cumm_build_time|2|3)
# Snapshot rebuild instance
3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
+# Caller information to clear application data
+1003160 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
+# ---------------------------
+# Installer.java
+# ---------------------------
+# Caller Information to clear application data
+1003200 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1)
+# Call stack to clear application data
+1003201 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1)
# ---------------------------
# InputMethodManagerService.java
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 5a15f17..2b30c01 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -88,6 +88,9 @@
mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
|| intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
+ // This is ONLY used by TestHarnessService within System Server, so we don't add a proper
+ // API constant in Intent for this.
+ final boolean keepMemtagMode = intent.getBooleanExtra("keep_memtag_mode", false);
// TODO(b/189938391): properly handle factory reset on headless system user mode.
final int sendingUserId = getSendingUserId();
@@ -110,9 +113,11 @@
try {
Slog.i(TAG, "Calling RecoverySystem.rebootWipeUserData(context, "
+ "shutdown=" + shutdown + ", reason=" + reason
- + ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims + ")");
+ + ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims
+ + ", keepMemtagMode=" + keepMemtagMode + ")");
RecoverySystem
- .rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
+ .rebootWipeUserData(
+ context, shutdown, reason, forceWipe, mWipeEsims, keepMemtagMode);
Slog.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index bdda95e..8a801d8 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -93,6 +93,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.EventLog;
@@ -2346,6 +2347,18 @@
}
return;
}
+ if (isFirstAccountRemovalDisabled(account)) {
+ try {
+ response.onError(
+ AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+ "User cannot remove the first "
+ + account.type
+ + " account on the device.");
+ } catch (RemoteException re) {
+ Log.w(TAG, "RemoteException while removing account", re);
+ }
+ return;
+ }
final long identityToken = clearCallingIdentity();
UserAccounts accounts = getUserAccounts(userId);
cancelNotification(getSigninRequiredNotificationId(accounts, account), accounts);
@@ -2395,6 +2408,10 @@
account.type);
throw new SecurityException(msg);
}
+ if (isFirstAccountRemovalDisabled(account)) {
+ Log.e(TAG, "Cannot remove the first " + account.type + " account on the device.");
+ return false;
+ }
UserAccounts accounts = getUserAccountsForCaller();
final long accountId = accounts.accountsDb.findDeAccountId(account);
logRecord(
@@ -6426,6 +6443,48 @@
}
}
+ /**
+ * Returns true if the config_canRemoveOrRenameFirstUser is false, and the given account type
+ * matches the one provided by config_accountTypeToKeepFirstUser.
+ */
+ private boolean isFirstAccountRemovalDisabled(Account account) {
+ // Skip if not targeting the first user.
+ int userId = UserHandle.getCallingUserId();
+ if (userId != 0) {
+ return false;
+ }
+
+ // Skip if we are allowed to remove/rename first account.
+ if (mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_canRemoveFirstAccount)) {
+ return false;
+ }
+
+ // Skip if needed for testing.
+ if (Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS,
+ 0 /* default */,
+ 0 /* userHandle */) != 0) {
+ return false;
+ }
+
+ // Skip if not targeting desired account.
+ String typeToKeep =
+ mContext.getResources()
+ .getString(
+ com.android.internal.R.string.config_accountTypeToKeepFirstAccount);
+ if (typeToKeep.isEmpty() || !typeToKeep.equals(account.type)) {
+ return false;
+ }
+
+ // Only restrict first account.
+ UserAccounts accounts = getUserAccounts(0 /* userId */);
+ Account[] accountsOfType = getAccountsFromCache(accounts, typeToKeep,
+ Process.SYSTEM_UID, "android" /* packageName */, false);
+ return accountsOfType.length > 0 && accountsOfType[0].equals(account);
+ }
+
private final class AccountManagerInternalImpl extends AccountManagerInternal {
private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d61dd75..1566113 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1886,7 +1886,7 @@
int exitInfoReason = (int) args.arg3;
args.recycle();
forceStopPackageLocked(pkg, appId, false, false, true, false,
- false, userId, reason, exitInfoReason);
+ false, false, userId, reason, exitInfoReason);
}
} break;
@@ -3548,6 +3548,7 @@
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.AM_CLEAR_APP_DATA_CALLER, pid, uid, packageName);
final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
ALLOW_FULL_ONLY, "clearApplicationUserData", null);
@@ -3914,7 +3915,10 @@
+ packageName + ": " + e);
}
if (mUserController.isUserRunning(user, userRunningFlags)) {
- forceStopPackageLocked(packageName, pkgUid,
+ forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid),
+ false /* callerWillRestart */, false /* purgeCache */,
+ true /* doIt */, false /* evenPersistent */,
+ false /* uninstalling */, true /* packageStateStopped */, user,
reason == null ? ("from pid " + callingPid) : reason);
finishForceStopPackageLocked(packageName, pkgUid);
}
@@ -4163,7 +4167,7 @@
@GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
- false, true, false, false, UserHandle.getUserId(uid), reason);
+ false, true, false, false, false, UserHandle.getUserId(uid), reason);
}
@GuardedBy("this")
@@ -4349,20 +4353,20 @@
@GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
- boolean evenPersistent, boolean uninstalling, int userId, String reasonString) {
-
+ boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
+ int userId, String reasonString) {
int reason = packageName == null ? ApplicationExitInfo.REASON_USER_STOPPED
: ApplicationExitInfo.REASON_USER_REQUESTED;
return forceStopPackageLocked(packageName, appId, callerWillRestart, purgeCache, doit,
- evenPersistent, uninstalling, userId, reasonString, reason);
+ evenPersistent, uninstalling, packageStateStopped, userId, reasonString, reason);
}
@GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
- boolean evenPersistent, boolean uninstalling, int userId, String reasonString,
- int reason) {
+ boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
+ int userId, String reasonString, int reason) {
int i;
if (userId == UserHandle.USER_ALL && packageName == null) {
@@ -4443,7 +4447,7 @@
}
}
- if (packageName == null || uninstalling) {
+ if (packageName == null || uninstalling || packageStateStopped) {
didSomething |= mPendingIntentController.removePendingIntentsForPackage(
packageName, userId, appId, doit);
}
@@ -5148,7 +5152,7 @@
for (String pkg : pkgs) {
synchronized (ActivityManagerService.this) {
if (forceStopPackageLocked(pkg, -1, false, false, false, false, false,
- 0, "query restart")) {
+ false, 0, "query restart")) {
setResultCode(Activity.RESULT_OK);
return;
}
@@ -7342,7 +7346,7 @@
mDebugTransient = !persistent;
if (packageName != null) {
forceStopPackageLocked(packageName, -1, false, false, true, true,
- false, UserHandle.USER_ALL, "set debug app");
+ false, false, UserHandle.USER_ALL, "set debug app");
}
}
} finally {
@@ -14918,7 +14922,7 @@
if (list != null && list.length > 0) {
for (int i = 0; i < list.length; i++) {
forceStopPackageLocked(list[i], -1, false, true, true,
- false, false, userId, "storage unmount");
+ false, false, false, userId, "storage unmount");
}
mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
sendPackageBroadcastLocked(
@@ -14945,8 +14949,8 @@
if (killProcess) {
forceStopPackageLocked(ssp, UserHandle.getAppId(
intent.getIntExtra(Intent.EXTRA_UID, -1)),
- false, true, true, false, fullUninstall, userId,
- "pkg removed");
+ false, true, true, false, fullUninstall, false,
+ userId, "pkg removed");
getPackageManagerInternal()
.onPackageProcessKilledForUninstall(ssp);
} else {
@@ -15864,7 +15868,7 @@
} else {
// Instrumentation can kill and relaunch even persistent processes
forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false,
- userId, "start instr");
+ false, userId, "start instr");
// Inform usage stats to make the target package active
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(ii.targetPackage, userId,
@@ -15947,7 +15951,7 @@
try {
sdkSandboxInfo =
sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation(
- sdkSandboxClientAppInfo, userId, isSdkInSandbox);
+ sdkSandboxClientAppInfo, isSdkInSandbox);
} catch (NameNotFoundException e) {
reportStartInstrumentationFailureLocked(
watcher, className, "Can't find SdkSandbox package");
@@ -15993,6 +15997,7 @@
/* doIt= */ true,
/* evenPersistent= */ true,
/* uninstalling= */ false,
+ /* packageStateStopped= */ false,
userId,
"start instr");
@@ -16163,8 +16168,7 @@
}
} else if (!instr.mNoRestart) {
forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
- app.userId,
- "finished inst");
+ false, app.userId, "finished inst");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 26b23ff..a95ddf3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -205,7 +205,7 @@
private boolean mAsync;
private BroadcastOptions mBroadcastOptions;
private boolean mShowSplashScreen;
- private boolean mDismissKeyguard;
+ private boolean mDismissKeyguardIfInsecure;
final boolean mDumping;
@@ -552,8 +552,8 @@
mAsync = true;
} else if (opt.equals("--splashscreen-show-icon")) {
mShowSplashScreen = true;
- } else if (opt.equals("--dismiss-keyguard")) {
- mDismissKeyguard = true;
+ } else if (opt.equals("--dismiss-keyguard-if-insecure")) {
+ mDismissKeyguardIfInsecure = true;
} else {
return false;
}
@@ -714,11 +714,11 @@
}
options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
}
- if (mDismissKeyguard) {
+ if (mDismissKeyguardIfInsecure) {
if (options == null) {
options = ActivityOptions.makeBasic();
}
- options.setDismissKeyguard();
+ options.setDismissKeyguardIfInsecure();
}
if (mWaitOption) {
result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9e9db6a..931914f 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -129,3 +129,6 @@
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
+
+# Caller information to clear application data
+1030002 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 4380b42..507fd9e 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -44,6 +44,7 @@
private static final int DIRECT_RECL_AND_THRASHING = 5;
private static final int LOW_MEM_AND_SWAP_UTIL = 6;
private static final int LOW_FILECACHE_AFTER_THRASHING = 7;
+ private static final int LOW_MEM = 8;
/**
* Processes the LMK_KILL_OCCURRED packet data
@@ -106,6 +107,8 @@
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
case LOW_FILECACHE_AFTER_THRASHING:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_FILECACHE_AFTER_THRASHING;
+ case LOW_MEM:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM;
default:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 614caffe..2efac12 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -36,6 +36,7 @@
import static android.os.Process.startWebView;
import static android.system.OsConstants.*;
+import static com.android.sdksandbox.flags.Flags.selinuxSdkSandboxAudit;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -183,6 +184,7 @@
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
+ private static final String APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS = ":isSdkSandboxAudit";
private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
// OOM adjustments for processes in various states:
@@ -549,6 +551,10 @@
ActivityManagerGlobalLock mProcLock;
+ private static final String PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS =
+ "apply_sdk_sandbox_audit_restrictions";
+ private static final boolean DEFAULT_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS = false;
+
private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
"apply_sdk_sandbox_next_restrictions";
private static final boolean DEFAULT_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = false;
@@ -573,6 +579,13 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
+ private boolean mSdkSandboxApplyRestrictionsAudit =
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS,
+ DEFAULT_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS);
+
+ @GuardedBy("mLock")
private boolean mSdkSandboxApplyRestrictionsNext =
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ADSERVICES,
@@ -593,6 +606,12 @@
DeviceConfig.removeOnPropertiesChangedListener(this);
}
+ boolean applySdkSandboxRestrictionsAudit() {
+ synchronized (mLock) {
+ return mSdkSandboxApplyRestrictionsAudit;
+ }
+ }
+
boolean applySdkSandboxRestrictionsNext() {
synchronized (mLock) {
return mSdkSandboxApplyRestrictionsNext;
@@ -608,6 +627,12 @@
}
switch (name) {
+ case PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS:
+ mSdkSandboxApplyRestrictionsAudit =
+ properties.getBoolean(
+ PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS,
+ DEFAULT_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS);
+ break;
case PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS:
mSdkSandboxApplyRestrictionsNext =
properties.getBoolean(
@@ -2015,7 +2040,7 @@
// the package was initially frozen through KILL_APPLICATION_MSG, so
// it doesn't hurt to use it again.)
mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
+ false, false, true, false, false, false, app.userId, "start failure");
return false;
}
}
@@ -2025,10 +2050,14 @@
String updateSeInfo(ProcessRecord app) {
String extraInfo = "";
// By the time the first the SDK sandbox process is started, device config service
- // should be available.
- if (app.isSdkSandbox
- && getProcessListSettingsListener().applySdkSandboxRestrictionsNext()) {
- extraInfo = APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS;
+ // should be available. If both Next and Audit are enabled, Next takes precedence.
+ if (app.isSdkSandbox) {
+ if (getProcessListSettingsListener().applySdkSandboxRestrictionsNext()) {
+ extraInfo = APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS;
+ } else if (selinuxSdkSandboxAudit()
+ && getProcessListSettingsListener().applySdkSandboxRestrictionsAudit()) {
+ extraInfo = APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS;
+ }
}
return app.info.seInfo
@@ -2086,7 +2115,7 @@
+ app.processName, e);
app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
+ false, false, true, false, false, false, app.userId, "start failure");
}
return app.getPid() > 0;
}
@@ -2119,7 +2148,7 @@
app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName,
UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
+ false, false, true, false, false, false, app.userId, "start failure");
}
}
};
@@ -2414,6 +2443,18 @@
allowlistedAppDataInfoMap = null;
}
+ boolean bindOverrideSysprops = false;
+ String[] syspropOverridePkgNames = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_APP_COMPAT,
+ "appcompat_sysprop_override_pkgs", "").split(",");
+ String[] pkgs = app.getPackageList();
+ for (int i = 0; i < pkgs.length; i++) {
+ if (ArrayUtils.contains(syspropOverridePkgNames, pkgs[i])) {
+ bindOverrideSysprops = true;
+ break;
+ }
+ }
+
AppStateTracker ast = mService.mServices.mAppStateTracker;
if (ast != null) {
final boolean inBgRestricted = ast.isAppBackgroundRestricted(
@@ -2436,6 +2477,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
app.getDisabledCompatChanges(),
+ bindOverrideSysprops,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -2447,7 +2489,7 @@
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
- false, false,
+ false, false, bindOverrideSysprops,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
regularZygote = true;
@@ -2457,6 +2499,7 @@
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
+ bindOverrideSysprops,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
// By now the process group should have been created by zygote.
app.mProcessGroupCreated = true;
@@ -3265,12 +3308,17 @@
// Check if we should mark the processrecord for first launch after force-stopping
if ((r.getApplicationInfo().flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- final boolean wasPackageEverLaunched = mService.getPackageManagerInternal()
- .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
- // If the package was launched in the past but is currently stopped, only then it
- // should be considered as stopped after use. Do not mark it if it's the first launch.
- if (wasPackageEverLaunched) {
- r.setWasForceStopped(true);
+ try {
+ final boolean wasPackageEverLaunched = mService.getPackageManagerInternal()
+ .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
+ // If the package was launched in the past but is currently stopped, only then it
+ // should be considered as stopped after use. Do not mark it if it's the
+ // first launch.
+ if (wasPackageEverLaunched) {
+ r.setWasForceStopped(true);
+ }
+ } catch (IllegalArgumentException e) {
+ // App doesn't have state yet, so wasn't forcestopped
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index c1a2482..e6cdbb5 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -128,6 +128,7 @@
"biometrics",
"biometrics_framework",
"biometrics_integration",
+ "camera_hal",
"camera_platform",
"car_framework",
"car_perception",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 728bace..87633e9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -76,7 +76,6 @@
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageEvents;
import android.appwidget.AppWidgetManagerInternal;
import android.content.Context;
@@ -1497,10 +1496,8 @@
private boolean shouldStartWithParent(UserInfo user) {
final UserProperties properties = getUserProperties(user.id);
- DevicePolicyManagerInternal dpmi =
- LocalServices.getService(DevicePolicyManagerInternal.class);
return (properties != null && properties.getStartWithParent())
- && (!user.isQuietModeEnabled() || dpmi.isKeepProfilesRunningEnabled());
+ && !user.isQuietModeEnabled();
}
/**
@@ -1999,25 +1996,26 @@
EventLog.writeEvent(EventLogTags.UC_SWITCH_USER, targetUserId);
int currentUserId = getCurrentUserId();
UserInfo targetUserInfo = getUserInfo(targetUserId);
- if (targetUserId == currentUserId) {
- Slogf.i(TAG, "user #" + targetUserId + " is already the current user");
- return true;
- }
- if (targetUserInfo == null) {
- Slogf.w(TAG, "No user info for user #" + targetUserId);
- return false;
- }
- if (!targetUserInfo.supportsSwitchTo()) {
- Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
- return false;
- }
- if (FactoryResetter.isFactoryResetting()) {
- Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": factory reset in progress");
- return false;
- }
-
boolean userSwitchUiEnabled;
synchronized (mLock) {
+ if (targetUserId == currentUserId && mTargetUserId == UserHandle.USER_NULL) {
+ Slogf.i(TAG, "user #" + targetUserId + " is already the current user");
+ return true;
+ }
+ if (targetUserInfo == null) {
+ Slogf.w(TAG, "No user info for user #" + targetUserId);
+ return false;
+ }
+ if (!targetUserInfo.supportsSwitchTo()) {
+ Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
+ return false;
+ }
+ if (FactoryResetter.isFactoryResetting()) {
+ Slogf.w(TAG, "Cannot switch to User #" + targetUserId
+ + ": factory reset in progress");
+ return false;
+ }
+
if (!mInitialized) {
Slogf.e(TAG, "Cannot switch to User #" + targetUserId
+ ": UserController not ready yet");
@@ -3628,7 +3626,7 @@
void activityManagerForceStopPackage(@UserIdInt int userId, String reason) {
synchronized (mService) {
- mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
+ mService.forceStopPackageLocked(null, -1, false, false, true, false, false, false,
userId, reason);
}
};
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2336753..2897075 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1765,6 +1765,14 @@
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ if (btInfo.mState == BluetoothProfile.STATE_CONNECTED
+ && !mBtHelper.isProfilePoxyConnected(btInfo.mProfile)) {
+ AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
+ "msg: MSG_L_SET_BT_ACTIVE_DEVICE "
+ + "received with null profile proxy: "
+ + btInfo)).printLog(TAG));
+ return;
+ }
@AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
mBtHelper.getA2dpCodecWithFallbackToSBC(
btInfo.mDevice, "MSG_L_SET_BT_ACTIVE_DEVICE");
@@ -1909,7 +1917,7 @@
final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
if (btInfo.mDevice == null) break;
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "msg: onBluetoothActiveDeviceChange " + btInfo)).printLog(TAG));
+ "msg: MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT " + btInfo)).printLog(TAG));
synchronized (mDeviceStateLock) {
mDeviceInventory.setBluetoothActiveDevice(btInfo);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index d707689..e59fd77 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1628,11 +1628,6 @@
Log.i(TAG, "setBluetoothActiveDevice " + info.toString() + " delay(ms): " + delay);
}
mDeviceBroker.postBluetoothActiveDevice(info, delay);
- if (info.mProfile == BluetoothProfile.HEARING_AID
- && info.mState == BluetoothProfile.STATE_CONNECTED) {
- mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
- "HEARING_AID set to CONNECTED");
- }
}
return delay;
}
@@ -2013,6 +2008,9 @@
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
+
+ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
mAudioSystem.setDeviceConnectionState(ada,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a8fa313..b209fb0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -37,6 +37,7 @@
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
+import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -203,7 +204,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.media.audio.flags.Flags;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -10509,7 +10509,7 @@
}
private boolean isBluetoothPrividged() {
- if (!Flags.bluetoothMacAddressAnonymization()) {
+ if (!bluetoothMacAddressAnonymization()) {
return true;
}
return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 7d7e6d0..cce6bd2 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -43,7 +43,6 @@
import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -288,6 +287,13 @@
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE,
android.bluetooth.BluetoothDevice.class);
+ if (btDevice != null && !isProfilePoxyConnected(BluetoothProfile.HEADSET)) {
+ AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
+ "onReceiveBtEvent ACTION_ACTIVE_DEVICE_CHANGED "
+ + "received with null profile proxy for device: "
+ + btDevice)).printLog(TAG));
+ return;
+ }
onSetBtScoActiveDevice(btDevice);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
@@ -465,7 +471,8 @@
@GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileDisconnected(int profile) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "BT profile " + BluetoothProfile.getProfileName(profile) + " disconnected"));
+ "BT profile " + BluetoothProfile.getProfileName(profile)
+ + " disconnected").printLog(TAG));
switch (profile) {
case BluetoothProfile.HEADSET:
mBluetoothHeadset = null;
@@ -496,7 +503,11 @@
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
- + proxy));
+ + proxy).printLog(TAG));
+ if (proxy == null) {
+ Log.e(TAG, "onBtProfileConnected: null proxy for profile: " + profile);
+ return;
+ }
switch (profile) {
case BluetoothProfile.HEADSET:
onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -522,36 +533,64 @@
}
// this part is only for A2DP, LE Audio unicast and Hearing aid
- final List<BluetoothDevice> deviceList = proxy.getConnectedDevices();
- if (deviceList.isEmpty()) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null) {
+ Log.e(TAG, "onBtProfileConnected: Null BluetoothAdapter when connecting profile: "
+ + BluetoothProfile.getProfileName(profile));
return;
}
- final BluetoothDevice btDevice = deviceList.get(0);
- if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
- mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
- new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
- new BluetoothProfileConnectionInfo(profile),
- "mBluetoothProfileServiceListener"));
- } else {
- mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
- new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
- new BluetoothProfileConnectionInfo(profile),
- "mBluetoothProfileServiceListener"));
+ List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile);
+ if (activeDevices.isEmpty() || activeDevices.get(0) == null) {
+ return;
+ }
+ AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData(
+ activeDevices.get(0), null, new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener");
+ AudioDeviceBroker.BtDeviceInfo info =
+ mDeviceBroker.createBtDeviceInfo(data, activeDevices.get(0),
+ BluetoothProfile.STATE_CONNECTED);
+ mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */);
+ }
+
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ /*package*/ synchronized boolean isProfilePoxyConnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.HEADSET:
+ return mBluetoothHeadset != null;
+ case BluetoothProfile.A2DP:
+ return mA2dp != null;
+ case BluetoothProfile.HEARING_AID:
+ return mHearingAid != null;
+ case BluetoothProfile.LE_AUDIO:
+ return mLeAudio != null;
+ case BluetoothProfile.A2DP_SINK:
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ default:
+ // return true for profiles that are not managed by the BtHelper because
+ // the fact that the profile proxy is not connected does not affect
+ // the device connection handling.
+ return true;
}
}
// @GuardedBy("mDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
- private void onHeadsetProfileConnected(BluetoothHeadset headset) {
+ private void onHeadsetProfileConnected(@NonNull BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = headset;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- List<BluetoothDevice> activeDevices = Collections.emptyList();
if (adapter != null) {
- activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET);
+ List<BluetoothDevice> activeDevices =
+ adapter.getActiveDevices(BluetoothProfile.HEADSET);
+ if (activeDevices.size() > 0 && activeDevices.get(0) != null) {
+ onSetBtScoActiveDevice(activeDevices.get(0));
+ }
+ } else {
+ Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter");
}
- onSetBtScoActiveDevice((activeDevices.size() > 0) ? activeDevices.get(0) : null);
+
// Refresh SCO audio state
checkScoAudioState();
if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
@@ -559,7 +598,7 @@
return;
}
boolean status = false;
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
+ if (mBluetoothHeadsetDevice != null) {
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
status = connectBluetoothScoAudioHelper(
@@ -715,7 +754,8 @@
case BluetoothProfile.LE_AUDIO_BROADCAST:
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile service: connecting "
- + BluetoothProfile.getProfileName(profile) + " profile"));
+ + BluetoothProfile.getProfileName(profile)
+ + " profile").printLog(TAG));
mDeviceBroker.postBtProfileConnected(profile, proxy);
break;
@@ -734,7 +774,8 @@
case BluetoothProfile.LE_AUDIO_BROADCAST:
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile service: disconnecting "
- + BluetoothProfile.getProfileName(profile) + " profile"));
+ + BluetoothProfile.getProfileName(profile)
+ + " profile").printLog(TAG));
mDeviceBroker.postBtProfileDisconnected(profile);
break;
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index c7556da..4ceb83b2 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -15,7 +15,7 @@
*/
package com.android.server.audio;
-import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import android.Manifest;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 4538cad..0629e637 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -39,6 +39,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
@@ -333,6 +334,33 @@
}
@Override
+ public long getLastAuthenticationTime(int userId,
+ @Authenticators.Types int authenticators) throws RemoteException {
+ // Only allow internal clients to call getLastAuthenticationTime with a different
+ // userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // We can't do this above because we need the READ_DEVICE_CONFIG permission, which
+ // the calling user may not possess.
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ return mBiometricService.getLastAuthenticationTime(userId, authenticators);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName)
throws RemoteException {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 1898b80..91a68ea 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -20,6 +20,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
@@ -39,6 +40,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -53,6 +55,7 @@
import android.hardware.camera2.CameraManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -62,10 +65,16 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.Authorization;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.security.authorization.ResponseCode;
+import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
@@ -79,6 +88,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.utils.Slogf;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -116,6 +126,10 @@
KeyStore mKeyStore;
@VisibleForTesting
ITrustManager mTrustManager;
+ @VisibleForTesting
+ IKeystoreAuthorization mKeystoreAuthorization;
+ @VisibleForTesting
+ IGateKeeperService mGateKeeper;
// Get and cache the available biometric authenticators and their associated info.
final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
@@ -616,6 +630,64 @@
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
+ public long getLastAuthenticationTime(
+ int userId, @Authenticators.Types int authenticators) {
+ super.getLastAuthenticationTime_enforcePermission();
+
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ Slogf.d(TAG, "getLastAuthenticationTime(userId=%d, authenticators=0x%x)",
+ userId, authenticators);
+
+ final long secureUserId;
+ try {
+ secureUserId = mGateKeeper.getSecureUserId(userId);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, "Failed to get secure user id for " + userId, e);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+ Slogf.w(TAG, "No secure user id for " + userId);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ ArrayList<Integer> hardwareAuthenticators = new ArrayList<>(2);
+
+ if ((authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.PASSWORD);
+ }
+
+ if ((authenticators & Authenticators.BIOMETRIC_STRONG) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.FINGERPRINT);
+ }
+
+ if (hardwareAuthenticators.isEmpty()) {
+ throw new IllegalArgumentException("authenticators must not be empty");
+ }
+
+ int[] authTypesArray = hardwareAuthenticators.stream()
+ .mapToInt(Integer::intValue)
+ .toArray();
+ try {
+ return mKeystoreAuthorization.getLastAuthTime(secureUserId, authTypesArray);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error getting last auth time: " + e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ // This is returned when the feature flag test fails in keystore2
+ if (e.errorCode == ResponseCode.PERMISSION_DENIED) {
+ throw new UnsupportedOperationException();
+ }
+
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName) {
@@ -937,6 +1009,14 @@
return ActivityManager.getService();
}
+ public IKeystoreAuthorization getKeystoreAuthorizationService() {
+ return Authorization.getService();
+ }
+
+ public IGateKeeperService getGateKeeperService() {
+ return GateKeeper.getService();
+ }
+
public ITrustManager getTrustManager() {
return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE));
}
@@ -1050,6 +1130,8 @@
mBiometricContext = injector.getBiometricContext(context);
mUserManager = injector.getUserManager(context);
mBiometricCameraManager = injector.getBiometricCameraManager(context);
+ mKeystoreAuthorization = injector.getKeystoreAuthorizationService();
+ mGateKeeper = injector.getGateKeeperService();
try {
injector.getActivityManagerService().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index e3c0cf7..b394fb5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -19,6 +19,8 @@
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
+import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.Context.DEVICE_ID_INVALID;
@@ -409,7 +411,7 @@
/**
* Determines which deviceId to use for selecting a Clipboard, depending on where a given app
- * is running.
+ * is running and the device's clipboard policy.
*
* @param requestedDeviceId the requested deviceId passed in from the client side
* @param uid the intended app uid
@@ -431,28 +433,47 @@
}
}
- if (requestedDeviceId != DEVICE_ID_DEFAULT) {
- // Privileged apps that own the VirtualDevices, or regular apps running on it, can
- // request it by id.
- if (mVdmInternal.getDeviceOwnerUid(requestedDeviceId) == uid
- || virtualDeviceIds.contains(requestedDeviceId)) {
- return requestedDeviceId;
+ // If an app is running on any VirtualDevice, it isn't clear which clipboard they
+ // should use, unless all of the devices share the default device's clipboard.
+ boolean allDevicesHaveDefaultClipboard = true;
+ for (int deviceId : virtualDeviceIds) {
+ if (!deviceUsesDefaultClipboard(deviceId)) {
+ allDevicesHaveDefaultClipboard = false;
+ break;
}
- return DEVICE_ID_INVALID;
}
- // The common case is apps running normally (not on a VirtualDevice).
- if (virtualDeviceIds.isEmpty()) {
- return DEVICE_ID_DEFAULT;
+ // Apps running on a virtual device may get the default clipboard if all the devices the app
+ // runs on share that clipboard. Otherwise it's not clear which clipboard to use.
+ if (requestedDeviceId == DEVICE_ID_DEFAULT) {
+ return allDevicesHaveDefaultClipboard ? DEVICE_ID_DEFAULT : DEVICE_ID_INVALID;
}
- // If an app is running on more than one VirtualDevice, it isn't clear which clipboard they
- // should use.
- if (virtualDeviceIds.size() > 1) {
- return DEVICE_ID_INVALID;
+ // At this point the app wants to access a virtual device clipboard. It may do so if:
+ // 1. The app owns the VirtualDevice
+ // 2. The app is present on the VirtualDevice
+ // 3. The VirtualDevice shares the default device clipboard and all virtual devices that
+ // the app is running on do the same.
+ int clipboardDeviceId = deviceUsesDefaultClipboard(requestedDeviceId)
+ ? DEVICE_ID_DEFAULT
+ : requestedDeviceId;
+
+ if (mVdmInternal.getDeviceOwnerUid(requestedDeviceId) == uid
+ || virtualDeviceIds.contains(requestedDeviceId)
+ || (clipboardDeviceId == DEVICE_ID_DEFAULT && allDevicesHaveDefaultClipboard)) {
+ return clipboardDeviceId;
}
- return virtualDeviceIds.valueAt(0);
+ // Fallback to the device where the app is running, unless it uses the default clipboard.
+ int fallbackDeviceId = virtualDeviceIds.valueAt(0);
+ return deviceUsesDefaultClipboard(fallbackDeviceId) ? DEVICE_ID_DEFAULT : fallbackDeviceId;
+ }
+
+ private boolean deviceUsesDefaultClipboard(int deviceId) {
+ if (deviceId == DEVICE_ID_DEFAULT || mVdm == null) {
+ return true;
+ }
+ return mVdm.getDevicePolicy(deviceId, POLICY_TYPE_CLIPBOARD) == DEVICE_POLICY_CUSTOM;
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e475fe6..ff12ca2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -102,6 +102,7 @@
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -239,6 +240,10 @@
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
+
+ @VisibleForTesting
+ static final String ENABLE_ON_CONNECT =
+ "persist.sys.display.enable_on_connect.external";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
// This value needs to be in sync with the threshold
// in RefreshRateConfigs::getFrameRateDivisor.
@@ -1530,8 +1535,8 @@
throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
+ "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
+ "MediaProjection token in order to create a screen sharing virtual "
- + "display. In order to create a virtual display that does not perform"
- + "screen sharing (mirroring), please use the flag"
+ + "display. In order to create a virtual display that does not perform "
+ + "screen sharing (mirroring), please use the flag "
+ "VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY.");
}
}
@@ -1942,10 +1947,14 @@
}
setupLogicalDisplay(display);
-
// TODO(b/292196201) Remove when the display can be disabled before DPC is created.
if (display.getDisplayInfoLocked().type == Display.TYPE_EXTERNAL) {
- display.setEnabledLocked(false);
+ if ((Build.IS_ENG || Build.IS_USERDEBUG)
+ && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
+ Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
+ } else {
+ display.setEnabledLocked(false);
+ }
}
sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 915f5db..d8ac52e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1978,6 +1978,11 @@
|| !isDisplayContentVisible || brightnessIsTemporary;
if (!skipAnimation && BrightnessSynchronizer.floatEquals(
sdrAnimateValue, currentSdrBrightness)) {
+ // SDR brightness is unchanged, so animate quickly as this is only impacting
+ // a likely minority amount of display content
+ // ie, the highlights of an HDR video or UltraHDR image
+ slowChange = false;
+
// Going from HDR to no HDR; visually this should be a "no-op" anyway
// as the remaining SDR content's brightness should be holding steady
// due to the sdr brightness not shifting
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index fc596dc..a6155da 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1601,6 +1601,11 @@
|| !isDisplayContentVisible || brightnessIsTemporary;
if (!skipAnimation && BrightnessSynchronizer.floatEquals(
sdrAnimateValue, currentSdrBrightness)) {
+ // SDR brightness is unchanged, so animate quickly as this is only impacting
+ // a likely minority amount of display content
+ // ie, the highlights of an HDR video or UltraHDR image
+ slowChange = false;
+
// Going from HDR to no HDR; visually this should be a "no-op" anyway
// as the remaining SDR content's brightness should be holding steady
// due to the sdr brightness not shifting
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 08503cb..3e46ee2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -50,6 +50,8 @@
/**
* Called by the power manager to tell the input method manager whether it
* should start watching for wake events.
+ *
+ * @param interactive the interactive mode parameter
*/
public abstract void setInteractive(boolean interactive);
@@ -61,16 +63,16 @@
/**
* Returns the list of installed input methods for the specified user.
*
- * @param userId The user ID to be queried.
- * @return A list of {@link InputMethodInfo}. VR-only IMEs are already excluded.
+ * @param userId the user ID to be queried
+ * @return a list of {@link InputMethodInfo}. VR-only IMEs are already excluded
*/
public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
/**
* Returns the list of installed input methods that are enabled for the specified user.
*
- * @param userId The user ID to be queried.
- * @return A list of {@link InputMethodInfo} that are enabled for {@code userId}.
+ * @param userId the user ID to be queried
+ * @return a list of {@link InputMethodInfo} that are enabled for {@code userId}
*/
public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
@@ -78,8 +80,10 @@
* Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from
* the input method.
*
+ * @param userId the user ID to be queried
* @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
- * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
+ * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request
+ * object
*/
public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
@@ -88,8 +92,8 @@
* Force switch to the enabled input method by {@code imeId} for current user. If the input
* method with {@code imeId} is not enabled or not installed, do nothing.
*
- * @param imeId The input method ID to be switched to.
- * @param userId The user ID to be queried.
+ * @param imeId the input method ID to be switched to
+ * @param userId the user ID to be queried
* @return {@code true} if the current input method was successfully switched to the input
* method by {@code imeId}; {@code false} the input method with {@code imeId} is not available
* to be switched.
@@ -100,28 +104,30 @@
* Force enable or disable the input method associated with {@code imeId} for given user. If
* the input method associated with {@code imeId} is not installed, do nothing.
*
- * @param imeId The input method ID to be enabled or disabled.
+ * @param imeId the input method ID to be enabled or disabled
* @param enabled {@code true} if the input method associated with {@code imeId} should be
- * enabled.
- * @param userId The user ID to be queried.
+ * enabled
+ * @param userId the user ID to be queried
* @return {@code true} if the input method associated with {@code imeId} was successfully
- * enabled or disabled, {@code false} if the input method specified is not installed
- * or was unable to be enabled/disabled for some other reason.
+ * enabled or disabled, {@code false} if the input method specified is not installed
+ * or was unable to be enabled/disabled for some other reason.
*/
public abstract boolean setInputMethodEnabled(String imeId, boolean enabled,
@UserIdInt int userId);
/**
* Registers a new {@link InputMethodListListener}.
+ *
+ * @param listener the listener to add
*/
public abstract void registerInputMethodListListener(InputMethodListListener listener);
/**
* Transfers input focus from a given input token to that of the IME window.
*
- * @param sourceInputToken The source token.
- * @param displayId The display hosting the IME window.
- * @return {@code true} if the transfer is successful.
+ * @param sourceInputToken the source token.
+ * @param displayId the display hosting the IME window
+ * @return {@code true} if the transfer is successful
*/
public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
int displayId);
@@ -132,7 +138,7 @@
* or SystemUI).
*
* @param windowToken the window token that is now in control, or {@code null} if no client
- * window is in control of the IME.
+ * window is in control of the IME
*/
public abstract void reportImeControl(@Nullable IBinder windowToken);
@@ -163,8 +169,8 @@
* Callback when the IInputMethodSession from the accessibility service with the specified
* accessibilityConnectionId is created.
*
- * @param accessibilityConnectionId The connection id of the accessibility service.
- * @param session The session passed back from the accessibility service.
+ * @param accessibilityConnectionId the connection id of the accessibility service
+ * @param session the session passed back from the accessibility service
*/
public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
IAccessibilityInputMethodSession session);
@@ -173,7 +179,7 @@
* Unbind the accessibility service with the specified accessibilityConnectionId from current
* client.
*
- * @param accessibilityConnectionId The connection id of the accessibility service.
+ * @param accessibilityConnectionId the connection id of the accessibility service
*/
public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
@@ -181,7 +187,7 @@
* Switch the keyboard layout in response to a keyboard shortcut.
*
* @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the
- * previous subtype.
+ * previous subtype
*/
public abstract void switchKeyboardLayout(int direction);
@@ -192,7 +198,7 @@
public abstract boolean isAnyInputConnectionActive();
/**
- * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
+ * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
private static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@@ -282,7 +288,7 @@
};
/**
- * @return Global instance if exists. Otherwise, a fallback no-op instance.
+ * @return Global instance if exists. Otherwise, a fallback no-op instance.
*/
@NonNull
public static InputMethodManagerInternal get() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 744238f..d456a74 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -192,6 +192,7 @@
// Start of methods that implement MediaRouter2 operations.
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@NonNull
public boolean verifyPackageExists(@NonNull String clientPackageName) {
final int pid = Binder.getCallingPid();
@@ -199,11 +200,7 @@
final long token = Binder.clearCallingIdentity();
try {
- mContext.enforcePermission(
- Manifest.permission.MEDIA_CONTENT_CONTROL,
- pid,
- uid,
- "Must hold MEDIA_CONTENT_CONTROL permission.");
+ enforcePrivilegedRoutingPermissions(uid, pid);
PackageManager pm = mContext.getPackageManager();
pm.getPackageInfo(clientPackageName, PackageManager.PackageInfoFlags.of(0));
return true;
@@ -482,6 +479,7 @@
}
}
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void registerManager(@NonNull IMediaRouter2Manager manager,
@NonNull String callerPackageName) {
Objects.requireNonNull(manager, "manager must not be null");
@@ -729,6 +727,15 @@
return hasBluetoothRoutingPermission;
}
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ private void enforcePrivilegedRoutingPermissions(int callerUid, int callerPid) {
+ mContext.enforcePermission(
+ Manifest.permission.MEDIA_CONTENT_CONTROL,
+ callerPid,
+ callerUid,
+ "Must hold MEDIA_CONTENT_CONTROL permission.");
+ }
+
// End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -1164,6 +1171,7 @@
return sessionInfos;
}
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@GuardedBy("mLock")
private void registerManagerLocked(
@NonNull IMediaRouter2Manager manager,
@@ -1187,8 +1195,7 @@
+ " callerUserId: %d",
callerUid, callerPid, callerPackageName, callerUserId));
- mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
- "Must hold MEDIA_CONTENT_CONTROL permission.");
+ enforcePrivilegedRoutingPermissions(callerUid, callerPid);
UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId);
managerRecord = new ManagerRecord(
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 44719f8..6df4a95 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -409,6 +409,7 @@
}
// Binder call
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@Override
public boolean verifyPackageExists(String clientPackageName) {
return mService2.verifyPackageExists(clientPackageName);
@@ -536,6 +537,7 @@
}
// Binder call
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
@Override
public void registerManager(IMediaRouter2Manager manager, String callerPackageName) {
final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4b5d52f..1bdd402 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -128,12 +128,12 @@
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static com.android.server.notification.Flags.expireBitmaps;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
-import static com.android.server.notification.Flags.expireBitmaps;
import android.Manifest;
import android.Manifest.permission;
@@ -178,7 +178,6 @@
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
@@ -206,8 +205,6 @@
import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -291,6 +288,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.MetricsLogger;
@@ -319,7 +317,6 @@
import com.android.server.job.JobSchedulerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
-import com.android.server.notification.Flags;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.notification.toast.CustomToastRecord;
@@ -1934,7 +1931,7 @@
} else if (
isProfileUnavailable(action)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) {
+ if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
REASON_PROFILE_TURNED_OFF);
mSnoozeHelper.clearData(userHandle);
@@ -2909,7 +2906,16 @@
mPreferencesHelper.updateFixedImportance(mUm.getUsers());
mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers());
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
- if (expireBitmaps()) {
+ if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) {
+ new Thread(() -> {
+ while (true) {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) { }
+ mInternalService.removeBitmaps();
+ }
+ }).start();
+ } else if (expireBitmaps()) {
NotificationBitmapJobService.scheduleJob(getContext());
}
}
@@ -6765,7 +6771,14 @@
final long timePostedMs = r.getSbn().getPostTime();
final long timeNowMs = System.currentTimeMillis();
- if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) {
+ final long bitmapDuration;
+ if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) {
+ bitmapDuration = Duration.ofSeconds(5).toMillis();
+ } else {
+ bitmapDuration = BITMAP_DURATION.toMillis();
+ }
+
+ if (isBitmapExpired(timePostedMs, timeNowMs, bitmapDuration)) {
removeBitmapAndRepost(r);
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2951ef6..a52870e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -112,6 +112,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -159,6 +160,7 @@
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptParams;
import com.android.server.art.model.DexoptResult;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -2524,8 +2526,15 @@
LocalManagerRegistry.getManager(PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
packageManagerLocal.withFilteredSnapshot()) {
- DexoptParams params =
- dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
+ boolean ignoreDexoptProfile =
+ (installRequest.getInstallFlags()
+ & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
+ != 0;
+ /*@DexoptFlags*/ int extraFlags =
+ ignoreDexoptProfile && Flags.useArtServiceV2()
+ ? ArtFlags.FLAG_IGNORE_PROFILE
+ : 0;
+ DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags);
DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 4ed3163..d5471cb0 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -24,6 +24,7 @@
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
+import android.os.Binder;
import android.os.Build;
import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
@@ -35,9 +36,11 @@
import android.os.ServiceManager;
import android.os.storage.CrateMetadata;
import android.text.format.DateUtils;
+import android.util.EventLog;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
import com.android.server.SystemService;
import dalvik.system.BlockGuard;
@@ -441,6 +444,26 @@
if (!checkBeforeRemote()) return;
try {
mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
+
+ final StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+ String className;
+ String methodName;
+ String fileName;
+ int lineNumber;
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALLER, pid, uid, packageName,
+ flags);
+ // Skip the first two elements since they are always the same, ie
+ // Thread#getStackTrace() and VMStack#getThreadStackTrace()
+ for (int i = 2; i < elements.length; i++) {
+ className = elements[i].getClassName();
+ methodName = elements[i].getMethodName();
+ fileName = elements[i].getFileName();
+ lineNumber = elements[i].getLineNumber();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALL_STACK, methodName,
+ className, fileName, lineNumber);
+ }
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index a296160..d5dacce 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -16,9 +16,10 @@
package com.android.server.pm;
+import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
-import static android.content.pm.ArchivedActivity.bytesFromBitmap;
-import static android.content.pm.ArchivedActivity.drawableToBitmap;
+import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
+import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
@@ -106,6 +107,9 @@
@Nullable
private LauncherApps mLauncherApps;
+ @Nullable
+ private AppOpsManager mAppOpsManager;
+
PackageArchiver(Context context, PackageManagerService mPm) {
this.mContext = context;
this.mPm = mPm;
@@ -178,6 +182,8 @@
Binder.getCallingUid(), userId);
String responsibleInstallerPackage = getResponsibleInstallerPackage(ps);
verifyInstaller(responsibleInstallerPackage, userId);
+ verifyOptOutStatus(packageName,
+ UserHandle.getUid(userId, UserHandle.getUid(userId, ps.getAppId())));
List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
userId);
@@ -308,6 +314,59 @@
return intentReceivers != null && !intentReceivers.getList().isEmpty();
}
+ /**
+ * Returns true if the app is archivable.
+ */
+ // TODO(b/299299569) Exclude system apps
+ public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(user);
+
+ Computer snapshot = mPm.snapshotComputer();
+ int userId = user.getIdentifier();
+ int binderUid = Binder.getCallingUid();
+ snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
+ "isAppArchivable");
+ PackageStateInternal ps;
+ try {
+ ps = getPackageState(packageName, mPm.snapshotComputer(),
+ Binder.getCallingUid(), userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new ParcelableException(e);
+ }
+
+ if (isAppOptedOutOfArchiving(packageName, ps.getAppId())) {
+ return false;
+ }
+
+ try {
+ verifyInstaller(getResponsibleInstallerPackage(ps), userId);
+ getLauncherActivityInfos(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if user has opted the app out of archiving through system settings.
+ */
+ // TODO(b/304256918) Switch this to a separate OP code for archiving.
+ private boolean isAppOptedOutOfArchiving(String packageName, int uid) {
+ return Binder.withCleanCallingIdentity(() ->
+ getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid, packageName) == MODE_IGNORED);
+ }
+
+ private void verifyOptOutStatus(String packageName, int uid)
+ throws PackageManager.NameNotFoundException {
+ if (isAppOptedOutOfArchiving(packageName, uid)) {
+ throw new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("The app %s is opted out of archiving.", packageName));
+ }
+ }
+
void requestUnarchive(
@NonNull String packageName,
@NonNull String callerPackageName,
@@ -510,6 +569,13 @@
return mLauncherApps;
}
+ private AppOpsManager getAppOpsManager() {
+ if (mAppOpsManager == null) {
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ }
+ return mAppOpsManager;
+ }
+
private void storeArchiveState(String packageName, ArchiveState archiveState, int userId)
throws PackageManager.NameNotFoundException {
synchronized (mPm.mLock) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 305e353..a4d8632 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -58,6 +58,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
@@ -666,17 +667,22 @@
// App package name and label length is restricted so that really long strings aren't
// written to disk.
- if (params.appPackageName != null
- && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+ if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {
params.appPackageName = null;
}
params.appLabel = TextUtils.trimToSize(params.appLabel,
PackageItemInfo.MAX_SAFE_LABEL_LENGTH);
- String requestedInstallerPackageName = (params.installerPackageName != null
- && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
- ? params.installerPackageName : installerPackageName;
+ // Validate installer package name.
+ if (params.installerPackageName != null && !isValidPackageName(
+ params.installerPackageName)) {
+ params.installerPackageName = null;
+ }
+
+ var requestedInstallerPackageName =
+ params.installerPackageName != null ? params.installerPackageName
+ : installerPackageName;
if (PackageManagerServiceUtils.isRootOrShell(callingUid)
|| PackageInstallerSession.isSystemDataLoaderInstallation(params)
@@ -1105,6 +1111,19 @@
return Integer.parseInt(sessionId);
}
+ private static boolean isValidPackageName(@NonNull String packageName) {
+ if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+ return false;
+ }
+ // "android" is a valid package name
+ var errorMessage = FrameworkParsingPackageUtils.validateName(
+ packageName, /* requireSeparator= */ false, /* requireFilename */ true);
+ if (errorMessage != null) {
+ return false;
+ }
+ return true;
+ }
+
private File getTmpSessionDir(String volumeUuid) {
return Environment.getDataAppDirectory(volumeUuid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c9303f2..e5f7962 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2085,8 +2085,8 @@
mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
injector.getBackgroundHandler());
mDexOptHelper = new DexOptHelper(this);
- mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager,
- mBroadcastHelper, mProtectedPackages);
+ mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
+ mProtectedPackages);
mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
mSuspendPackageHelper);
mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
@@ -4658,6 +4658,9 @@
throw new SecurityException("Cannot clear data for a protected package: "
+ packageName);
}
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
@@ -4791,6 +4794,9 @@
/* checkShell= */ false, "delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
@@ -6145,7 +6151,7 @@
}
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
- false /* forQuietMode */, quarantined);
+ quarantined);
}
@Override
@@ -6310,6 +6316,11 @@
return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user);
}
+ @Override
+ public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) {
+ return mInstallerService.mPackageArchiver.isAppArchivable(packageName, user);
+ }
+
/**
* Wait for the handler to finish handling all pending messages.
* @param timeoutMillis Maximum time in milliseconds to wait.
@@ -6597,12 +6608,6 @@
}
@Override
- public void setPackagesSuspendedForQuietMode(int userId, boolean suspended) {
- mSuspendPackageHelper.setPackagesSuspendedForQuietMode(
- snapshotComputer(), userId, suspended);
- }
-
- @Override
public void setDeviceAndProfileOwnerPackages(
int deviceOwnerUserId, String deviceOwnerPackage,
SparseArray<String> profileOwnerPackages) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7264e2e..d4abad8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3703,6 +3703,9 @@
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
+ case "--ignore-dexopt-profile":
+ sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4799,7 +4802,7 @@
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
- pw.println(" [--staged-ready-timeout TIMEOUT]");
+ pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4839,6 +4842,13 @@
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
+ pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+ pw.println(" during the installation, including the profile in the DM file and");
+ pw.println(" the profile embedded in the APK file. If an invalid profile is");
+ pw.println(" provided during installation, no warning will be reported by `adb");
+ pw.println(" install`.");
+ pw.println(" This option does not affect later dexopt operations (e.g.,");
+ pw.println(" background dexopt and manual `pm compile` invocations).");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index e8cebef..71f6c0d 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -16,8 +16,6 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -27,9 +25,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.SuspendDialogInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -45,7 +41,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.server.LocalServices;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -54,10 +49,8 @@
import com.android.server.utils.WatchedArrayMap;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Predicate;
public final class SuspendPackageHelper {
@@ -68,7 +61,6 @@
private final PackageManagerService mPm;
private final PackageManagerServiceInjector mInjector;
- private final UserManagerService mUserManager;
private final BroadcastHelper mBroadcastHelper;
private final ProtectedPackages mProtectedPackages;
@@ -76,10 +68,8 @@
* Constructor for {@link PackageManagerService}.
*/
SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
- UserManagerService userManager, BroadcastHelper broadcastHelper,
- ProtectedPackages protectedPackages) {
+ BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages) {
mPm = pm;
- mUserManager = userManager;
mInjector = injector;
mBroadcastHelper = broadcastHelper;
mProtectedPackages = protectedPackages;
@@ -102,7 +92,6 @@
* @param callingPackage The caller's package name.
* @param userId The user where packages reside.
* @param callingUid The caller's uid.
- * @param forQuietMode Whether suspension is for quiet mode, in which case no apps are exempt.
* @return The names of failed packages.
*/
@Nullable
@@ -110,11 +99,11 @@
boolean suspended, @Nullable PersistableBundle appExtras,
@Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
@NonNull String callingPackage, @UserIdInt int userId, int callingUid,
- boolean forQuietMode, boolean quarantined) {
+ boolean quarantined) {
if (ArrayUtils.isEmpty(packageNames)) {
return packageNames;
}
- if (suspended && !quarantined && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId,
+ if (suspended && !quarantined && !isSuspendAllowedForUser(snapshot, userId,
callingUid)) {
Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
return packageNames;
@@ -130,7 +119,7 @@
final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
- final boolean[] canSuspend = suspended && !forQuietMode
+ final boolean[] canSuspend = suspended
? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid)
: null;
for (int i = 0; i < packageNames.length; i++) {
@@ -620,92 +609,10 @@
*/
public String[] setPackagesSuspendedByAdmin(
Computer snapshot, int userId, String[] packageNames, boolean suspend) {
- final Set<String> toSuspend = new ArraySet<>(packageNames);
- List<String> unsuspendable = new ArrayList<>();
-
- if (mUserManager.isQuietModeEnabled(userId)) {
- // If the user is in quiet mode, most apps will already be suspended, we shouldn't
- // re-suspend or unsuspend them.
- final Set<String> quiet = packagesToSuspendInQuietMode(snapshot, userId);
- quiet.retainAll(toSuspend);
- if (!quiet.isEmpty()) {
- Slog.i(TAG, "Ignoring quiet packages: " + String.join(", ", quiet));
- toSuspend.removeAll(quiet);
- }
-
- // Some of the already suspended packages might not be suspendable by the admin
- // (e.g. current dialer package), we need to report it back as unsuspendable the same
- // way as if quiet mode wasn't enabled. In that latter case they'd be returned by
- // setPackagesSuspended below after unsuccessful attempt to suspend them.
- if (suspend) {
- unsuspendable = getUnsuspendablePackages(snapshot, userId, quiet);
- }
- }
- if (!toSuspend.isEmpty()) {
- unsuspendable.addAll(Arrays.asList(
- setPackagesSuspended(
- snapshot, toSuspend.toArray(new String[0]), suspend,
- null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
- PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
- false /* forQuietMode */, false /* quarantined */)));
- }
- return unsuspendable.toArray(String[]::new);
- }
-
- private List<String> getUnsuspendablePackages(
- Computer snapshot, int userId, Set<String> packages) {
- final String[] toSuspendArray = packages.toArray(String[]::new);
- final boolean[] mask =
- canSuspendPackageForUser(snapshot, toSuspendArray, userId, Process.SYSTEM_UID);
- final List<String> result = new ArrayList<>();
- for (int i = 0; i < mask.length; i++) {
- if (!mask[i]) {
- result.add(toSuspendArray[i]);
- }
- }
- return result;
- }
-
- /**
- * Suspends or unsuspends all packages in the given user when quiet mode is toggled to prevent
- * usage while quiet mode is enabled.
- */
- public void setPackagesSuspendedForQuietMode(
- Computer snapshot, int userId, boolean suspend) {
- final Set<String> toSuspend = packagesToSuspendInQuietMode(snapshot, userId);
- if (!suspend) {
- final DevicePolicyManagerInternal dpm =
- LocalServices.getService(DevicePolicyManagerInternal.class);
- if (dpm != null) {
- toSuspend.removeAll(dpm.getPackagesSuspendedByAdmin(userId));
- } else {
- Slog.wtf(TAG,
- "DevicePolicyManager unavailable while suspending apps for quiet mode");
- }
- }
-
- if (toSuspend.isEmpty()) {
- return;
- }
-
- setPackagesSuspended(snapshot, toSuspend.toArray(new String[0]),
- suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
+ return setPackagesSuspended(snapshot, packageNames, suspend,
+ null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
- true /* forQuietMode */, false /* quarantined */);
- }
-
- private Set<String> packagesToSuspendInQuietMode(Computer snapshot, int userId) {
- final List<PackageInfo> pkgInfos = snapshot.getInstalledPackages(
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId).getList();
- final Set<String> result = new ArraySet<>();
- for (PackageInfo info : pkgInfos) {
- result.add(info.packageName);
- }
-
- // Role holder may be null, but ArraySet handles it correctly.
- result.remove(mPm.getDevicePolicyManagementRoleHolderPackageName(userId));
-
- return result;
+ false /* quarantined */);
}
private String getKnownPackageName(@NonNull Computer snapshot,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 534f176..81a570f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -46,7 +46,6 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
-import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
@@ -55,7 +54,6 @@
import android.app.StatsManager;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManagerInternal;
-import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -302,19 +300,6 @@
private static final String TRON_USER_CREATED = "users_user_created";
private static final String TRON_DEMO_CREATED = "users_demo_created";
- // App ops that should be restricted in quiet mode
- private static final int[] QUIET_MODE_RESTRICTED_APP_OPS = {
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION,
- AppOpsManager.OP_GPS,
- AppOpsManager.OP_BODY_SENSORS,
- AppOpsManager.OP_ACTIVITY_RECOGNITION,
- AppOpsManager.OP_BLUETOOTH_SCAN,
- AppOpsManager.OP_NEARBY_WIFI_DEVICES,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_CAMERA,
- };
-
private final Context mContext;
private final PackageManagerService mPm;
@@ -339,7 +324,6 @@
private final File mUserListFile;
private final IBinder mUserRestrictionToken = new Binder();
- private final IBinder mQuietModeToken = new Binder();
/** Installs system packages based on user-type. */
private final UserSystemPackageInstaller mSystemPackageInstaller;
@@ -702,7 +686,6 @@
@Override
public void onUserStarting(@NonNull TargetUser targetUser) {
- boolean isProfileInQuietMode = false;
synchronized (mUms.mUsersLock) {
final UserData user = mUms.getUserDataLU(targetUser.getUserIdentifier());
if (user != null) {
@@ -710,14 +693,9 @@
if (targetUser.getUserIdentifier() == UserHandle.USER_SYSTEM
&& targetUser.isFull()) {
mUms.setLastEnteredForegroundTimeToNow(user);
- } else if (user.info.isManagedProfile() && user.info.isQuietModeEnabled()) {
- isProfileInQuietMode = true;
}
}
}
- if (isProfileInQuietMode) {
- mUms.setAppOpsRestrictedForQuietMode(targetUser.getUserIdentifier(), true);
- }
}
@Override
@@ -1516,43 +1494,21 @@
synchronized (mPackagesLock) {
writeUserLP(profileUserData);
}
- if (getDevicePolicyManagerInternal().isKeepProfilesRunningEnabled()) {
- // New behavior: when quiet mode is enabled, profile user is running, but apps are
- // suspended.
- getPackageManagerInternal().setPackagesSuspendedForQuietMode(userId, enableQuietMode);
- setAppOpsRestrictedForQuietMode(userId, enableQuietMode);
- if (enableQuietMode
- && !mLockPatternUtils.isManagedProfileWithUnifiedChallenge(userId)) {
- mContext.getSystemService(TrustManager.class).setDeviceLockedForUser(userId, true);
+ try {
+ if (enableQuietMode) {
+ ActivityManager.getService().stopUser(userId, /* force= */ true, null);
+ LocalServices.getService(ActivityManagerInternal.class)
+ .killForegroundAppsForUser(userId);
+ } else {
+ IProgressListener callback = target != null
+ ? new DisableQuietModeUserUnlockedCallback(target)
+ : null;
+ ActivityManager.getService().startProfileWithListener(userId, callback);
}
-
- if (!enableQuietMode && target != null) {
- try {
- mContext.startIntentSender(target, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- Slog.e(LOG_TAG, "Failed to start intent after disabling quiet mode", e);
- }
- }
- } else {
- // Old behavior: when quiet is enabled, profile user is stopped.
- // Old quiet mode behavior: profile user is stopped.
- // TODO(b/265683382) Remove once rollout complete.
- try {
- if (enableQuietMode) {
- ActivityManager.getService().stopUser(userId, /* force= */ true, null);
- LocalServices.getService(ActivityManagerInternal.class)
- .killForegroundAppsForUser(userId);
- } else {
- IProgressListener callback = target != null
- ? new DisableQuietModeUserUnlockedCallback(target)
- : null;
- ActivityManager.getService().startProfileWithListener(userId, callback);
- }
- } catch (RemoteException e) {
- // Should not happen, same process.
- e.rethrowAsRuntimeException();
- }
+ } catch (RemoteException e) {
+ // Should not happen, same process.
+ e.rethrowAsRuntimeException();
}
logQuietModeEnabled(userId, enableQuietMode, callingPackage);
@@ -1569,17 +1525,6 @@
}
}
- private void setAppOpsRestrictedForQuietMode(@UserIdInt int userId, boolean restrict) {
- for (int opCode : QUIET_MODE_RESTRICTED_APP_OPS) {
- try {
- mAppOpsService.setUserRestriction(
- opCode, restrict, mQuietModeToken, userId, /* excludedPackageTags= */ null);
- } catch (RemoteException e) {
- Slog.w(LOG_TAG, "Unable to limit app ops", e);
- }
- }
- }
-
private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
@Nullable String callingPackage) {
Slogf.i(LOG_TAG,
@@ -3836,7 +3781,8 @@
if (type == XmlPullParser.START_TAG) {
final String name = parser.getName();
if (name.equals(TAG_USER)) {
- UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+ UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID),
+ mUserVersion);
if (userData != null) {
synchronized (mUsersLock) {
@@ -4555,7 +4501,7 @@
}
@GuardedBy({"mPackagesLock"})
- private UserData readUserLP(int id) {
+ private UserData readUserLP(int id, int userVersion) {
try (ResilientAtomicFile file = getUserFile(id)) {
FileInputStream fis = null;
try {
@@ -4564,19 +4510,19 @@
Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
return null;
}
- return readUserLP(id, fis);
+ return readUserLP(id, fis, userVersion);
} catch (Exception e) {
// Remove corrupted file and retry.
Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
file.failRead(fis, e);
- return readUserLP(id);
+ return readUserLP(id, userVersion);
}
}
}
@GuardedBy({"mPackagesLock"})
@VisibleForTesting
- UserData readUserLP(int id, InputStream is) throws IOException,
+ UserData readUserLP(int id, InputStream is, int userVersion) throws IOException,
XmlPullParserException {
int flags = 0;
String userType = null;
@@ -4669,7 +4615,17 @@
} else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) {
- localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ if (userVersion < 10) {
+ // Prior to version 10, the local user restrictions were stored as sub tags
+ // grouped by the user id of the source user. The source is no longer stored
+ // on versions 10+ as this is now stored in the DevicePolicyEngine.
+ RestrictionsSet oldLocalRestrictions =
+ RestrictionsSet.readRestrictions(
+ parser, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
+ localRestrictions = oldLocalRestrictions.mergeAll();
+ } else {
+ localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ }
} else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_GUEST_RESTRICTIONS.equals(tag)) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 2ad8bcf..9e20805 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -379,10 +379,7 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
- if ((flags & PackageManager.MATCH_QUARANTINED_COMPONENTS) == 0
- && state.isQuarantined()) {
- ai.enabled = false;
- } else if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.getEnabledState()
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 077812b..45ca690 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
+import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
@@ -31,6 +32,7 @@
import static android.os.Build.VERSION_CODES.O;
import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
+import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
@@ -2992,12 +2994,16 @@
// Window manager does the checking for this.
outAppOp[0] = OP_TOAST_WINDOW;
return ADD_OKAY;
+ case TYPE_ACCESSIBILITY_OVERLAY:
+ if (createAccessibilityOverlayAppOpEnabled()) {
+ outAppOp[0] = OP_CREATE_ACCESSIBILITY_OVERLAY;
+ return ADD_OKAY;
+ }
case TYPE_INPUT_METHOD:
case TYPE_WALLPAPER:
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
case TYPE_VOICE_INTERACTION:
- case TYPE_ACCESSIBILITY_OVERLAY:
case TYPE_QS_DIALOG:
case TYPE_NAVIGATION_BAR_PANEL:
// The window manager will check these.
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 9a9b836..1f884ba 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -71,6 +71,7 @@
public class TestHarnessModeService extends SystemService {
public static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
private static final String TAG = TestHarnessModeService.class.getSimpleName();
+ private boolean mEnableKeepMemtagMode = false;
private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
@@ -298,6 +299,18 @@
switch (cmd) {
case "enable":
case "restore":
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--keep-memtag":
+ mEnableKeepMemtagMode = true;
+ break;
+ default:
+ getErrPrintWriter().println("Invalid option: " + opt);
+ return 1;
+ }
+ }
+
checkPermissions();
final long originalId = Binder.clearCallingIdentity();
try {
@@ -357,6 +370,7 @@
i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
i.putExtra(Intent.EXTRA_REASON, TAG);
i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
+ i.putExtra("keep_memtag_mode", mEnableKeepMemtagMode);
getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM);
return 0;
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 3c8e630..26f0d34 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1585,7 +1585,7 @@
* the Activities in the Task should be finished when it finishes. Otherwise, return {@code
* false}.
*/
- private boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) {
+ private static boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) {
// Not a relative root if the given Activity is not the root Activity of its TaskFragment.
final TaskFragment taskFragment = r.getTaskFragment();
if (r != taskFragment.getActivity(ar -> !ar.finishing || ar == r,
@@ -1598,7 +1598,7 @@
return taskRoot.getTaskFragment().getCompanionTaskFragment() == taskFragment;
}
- private boolean isTopActivityInTaskFragment(ActivityRecord activity) {
+ private static boolean isTopActivityInTaskFragment(ActivityRecord activity) {
return activity.getTaskFragment().topRunningActivity() == activity;
}
@@ -1614,9 +1614,6 @@
public void onBackPressed(IBinder token, IRequestFinishCallback callback) {
final long origId = Binder.clearCallingIdentity();
try {
- final Intent baseActivityIntent;
- final boolean launchedFromHome;
- final boolean isLastRunningActivity;
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r == null) return;
@@ -1624,39 +1621,16 @@
final Task task = r.getTask();
final ActivityRecord root = task.getRootActivity(false /*ignoreRelinquishIdentity*/,
true /*setToBottomIfNone*/);
- final boolean isTaskRoot = r == root;
- if (isTaskRoot) {
- if (mService.mWindowOrganizerController.mTaskOrganizerController
+ if (r == root && mService.mWindowOrganizerController.mTaskOrganizerController
.handleInterceptBackPressedOnTaskRoot(r.getRootTask())) {
- // This task is handled by a task organizer that has requested the back
- // pressed callback.
- return;
- }
- } else if (!isRelativeTaskRootActivity(r, root)) {
- // Finish the Activity if the activity is not the task root or relative root.
- requestCallbackFinish(callback);
+ // This task is handled by a task organizer that has requested the back
+ // pressed callback.
return;
}
-
- isLastRunningActivity = isTopActivityInTaskFragment(isTaskRoot ? root : r);
-
- final boolean isBaseActivity = root.mActivityComponent.equals(task.realActivity);
- baseActivityIntent = isBaseActivity ? root.intent : null;
-
- launchedFromHome = root.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME);
- }
-
- // If the activity was launched directly from the home screen, then we should
- // refrain from finishing the activity and instead move it to the back to keep it in
- // memory. The requirements for this are:
- // 1. The activity is the last running activity in the task.
- // 2. The current activity is the base activity for the task.
- // 3. The activity was launched by the home process, and is one of the main entry
- // points for the application.
- if (baseActivityIntent != null && isLastRunningActivity
- && launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent)) {
- moveActivityTaskToBack(token, true /* nonRoot */);
- return;
+ if (shouldMoveTaskToBack(r, root)) {
+ moveActivityTaskToBack(token, true /* nonRoot */);
+ return;
+ }
}
// The default option for handling the back button is to finish the Activity.
@@ -1666,6 +1640,27 @@
}
}
+ static boolean shouldMoveTaskToBack(ActivityRecord r, ActivityRecord rootActivity) {
+ if (r != rootActivity && !isRelativeTaskRootActivity(r, rootActivity)) {
+ return false;
+ }
+ final boolean isBaseActivity = rootActivity.mActivityComponent.equals(
+ r.getTask().realActivity);
+ final Intent baseActivityIntent = isBaseActivity ? rootActivity.intent : null;
+
+ // If the activity was launched directly from the home screen, then we should
+ // refrain from finishing the activity and instead move it to the back to keep it in
+ // memory. The requirements for this are:
+ // 1. The activity is the last running activity in the task.
+ // 2. The current activity is the base activity for the task.
+ // 3. The activity was launched by the home process, and is one of the main entry
+ // points for the application.
+ return baseActivityIntent != null
+ && isTopActivityInTaskFragment(r)
+ && rootActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME)
+ && ActivityRecord.isMainIntent(baseActivityIntent);
+ }
+
@Override
public void enableTaskLocaleOverride(IBinder token) {
if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index bdab4d4..b428ed2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -913,7 +913,7 @@
boolean mEnteringAnimation;
boolean mOverrideTaskTransition;
- boolean mDismissKeyguard;
+ boolean mDismissKeyguardIfInsecure;
boolean mShareIdentity;
/** True if the activity has reported stopped; False if the activity becomes visible. */
@@ -2098,7 +2098,7 @@
}
mOverrideTaskTransition = options.getOverrideTaskTransition();
- mDismissKeyguard = options.getDismissKeyguard();
+ mDismissKeyguardIfInsecure = options.getDismissKeyguardIfInsecure();
mShareIdentity = options.isShareIdentityEnabled();
}
@@ -2887,7 +2887,6 @@
}
final StartingSurfaceController.StartingSurface surface;
- final WindowState startingWindow = mStartingWindow;
final boolean animate;
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
@@ -4545,7 +4544,7 @@
mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
}
// Post cleanup after the visibility and animation are transferred.
- fromActivity.postWindowRemoveStartingWindowCleanup();
+ fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
fromActivity.mVisibleSetFromTransferredStartingWindow = false;
mWmService.updateFocusedWindowLocked(
@@ -7461,7 +7460,12 @@
}
}
- void postWindowRemoveStartingWindowCleanup() {
+ void postWindowRemoveStartingWindowCleanup(@NonNull WindowState win) {
+ if (mStartingWindow == win) {
+ // This could only happen when the window is removed from hierarchy. So do not keep its
+ // reference anymore.
+ mStartingWindow = null;
+ }
if (mChildren.size() == 0 && mVisibleSetFromTransferredStartingWindow) {
// We set the visible state to true for the token from a transferred starting
// window. We now reset it back to false since the starting window was the last
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 25c42b4..f9d344b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -285,10 +285,6 @@
return false;
}
- if (isKeepProfilesRunningEnabled() && !isPackageSuspended()) {
- return false;
- }
-
IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
@@ -521,12 +517,6 @@
&& (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0;
}
- private static boolean isKeepProfilesRunningEnabled() {
- DevicePolicyManagerInternal dpmi =
- LocalServices.getService(DevicePolicyManagerInternal.class);
- return dpmi == null || dpmi.isKeepProfilesRunningEnabled();
- }
-
/**
* Called when an activity is successfully launched.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c021785..f462efc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -69,6 +69,7 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -3825,8 +3826,7 @@
}
@Override
- public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution,
- boolean takeSnapshotIfNeeded) {
+ public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -3840,12 +3840,8 @@
}
}
// Don't call this while holding the lock as this operation might hit the disk.
- TaskSnapshot taskSnapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
task.mUserId, true /* restoreFromDisk */, isLowResolution);
- if (taskSnapshot == null && takeSnapshotIfNeeded) {
- taskSnapshot = takeTaskSnapshot(taskId, false /* updateCache */);
- }
- return taskSnapshot;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -7064,8 +7060,7 @@
@Override
public TaskSnapshot getTaskSnapshotBlocking(
int taskId, boolean isLowResolution) {
- return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
- false /* takeSnapshotIfNeeded */);
+ return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution);
}
@Override
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d1728d6..2d37b9b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -43,6 +43,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -64,6 +65,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -141,10 +143,6 @@
// multiple Activities in the Stack.
Task prevTask = null;
- // The previous activity we're going back to. This can be either a child of currentTask
- // if there are more than one Activity in currentTask, or a child of prevTask, if
- // currentActivity is the last child of currentTask.
- ActivityRecord prevActivity;
WindowContainer<?> removedWindowContainer = null;
WindowState window;
@@ -264,14 +262,21 @@
return infoBuilder.build();
}
+ // The previous activity we're going back to. This can be either a child of currentTask
+ // if there are more than one Activity in currentTask, or a child of prevTask, if
+ // currentActivity is the last child of currentTask.
// We don't have an application callback, let's find the destination of the back gesture
// The search logic should align with ActivityClientController#finishActivity
- prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID);
+ final ArrayList<ActivityRecord> prevActivities = new ArrayList<>();
+ final boolean canAnimate = getAnimatablePrevActivities(currentTask, currentActivity,
+ prevActivities);
final boolean isOccluded = isKeyguardOccluded(window);
- // TODO Dialog window does not need to attach on activity, check
- // window.mAttrs.type != TYPE_BASE_APPLICATION
- if ((window.getParent().getChildCount() > 1
+ if (!canAnimate) {
+ backType = BackNavigationInfo.TYPE_CALLBACK;
+ } else if ((window.getParent().getChildCount() > 1
&& window.getParent().getChildAt(0) != window)) {
+ // TODO Dialog window does not need to attach on activity, check
+ // window.mAttrs.type != TYPE_BASE_APPLICATION
// Are we the top window of our parent? If not, we are a window on top of the
// activity, we won't close the activity.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
@@ -279,9 +284,8 @@
} else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) {
// skip if current activity is translucent
backType = BackNavigationInfo.TYPE_CALLBACK;
- removedWindowContainer = window;
- } else if (prevActivity != null) {
- if (!isOccluded || prevActivity.canShowWhenLocked()) {
+ } else if (prevActivities.size() > 0) {
+ if (!isOccluded || prevActivities.get(0).canShowWhenLocked()) {
// We have another Activity in the same currentTask to go to
final WindowContainer parent = currentActivity.getParent();
final boolean canCustomize = parent != null
@@ -303,40 +307,45 @@
}
}
removedWindowContainer = currentActivity;
- prevTask = prevActivity.getTask();
+ prevTask = prevActivities.get(0).getTask();
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
} else {
+ // keyguard locked and activities are unable to show when locked.
backType = BackNavigationInfo.TYPE_CALLBACK;
}
- } else if (currentTask.returnsToHomeRootTask()) {
- if (isOccluded) {
- backType = BackNavigationInfo.TYPE_CALLBACK;
- } else {
- // Our Task should bring back to home
- removedWindowContainer = currentTask;
- prevTask = currentTask.getDisplayArea().getRootHomeTask();
- backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
- mShowWallpaper = true;
- }
- } else if (currentActivity.isRootOfTask()) {
+ } else {
// TODO(208789724): Create single source of truth for this, maybe in
// RootWindowContainer
- prevTask = currentTask.mRootWindowContainer.getTask(Task::showToCurrentUser,
- currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/);
- removedWindowContainer = currentTask;
- // If it reaches the top activity, we will check the below task from parent.
- // If it's null or multi-window, fallback the type to TYPE_CALLBACK.
- // or set the type to proper value when it's return to home or another task.
- if (prevTask == null || prevTask.inMultiWindowMode()) {
+ prevTask = currentTask.mRootWindowContainer.getTask(t -> {
+ if (t.showToCurrentUser() && !t.mChildren.isEmpty()) {
+ final ActivityRecord ar = t.getTopNonFinishingActivity();
+ return ar != null && ar.showToCurrentUser();
+ }
+ return false;
+ }, currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ final ActivityRecord tmpPre = prevTask.getTopNonFinishingActivity();
+ if (tmpPre != null) {
+ prevActivities.add(tmpPre);
+ findAdjacentActivityIfExist(tmpPre, prevActivities);
+ }
+ if (prevActivities.isEmpty()
+ || (isOccluded && !prevActivities.get(0).canShowWhenLocked())) {
backType = BackNavigationInfo.TYPE_CALLBACK;
+ } else if (prevTask.isActivityTypeHome()) {
+ removedWindowContainer = currentTask;
+ backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+ mShowWallpaper = true;
} else {
- prevActivity = prevTask.getTopNonFinishingActivity();
- if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) {
+ // If it reaches the top activity, we will check the below task from parent.
+ // If it's null or multi-window and has different parent task, fallback the type
+ // to TYPE_CALLBACK. Or set the type to proper value when it's return to home or
+ // another task.
+ final Task prevParent = prevTask.getParent().asTask();
+ final Task currParent = currentTask.getParent().asTask();
+ if (prevTask.inMultiWindowMode() && prevParent != currParent) {
backType = BackNavigationInfo.TYPE_CALLBACK;
- } else if (prevTask.isActivityTypeHome()) {
- backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
- mShowWallpaper = true;
} else {
+ removedWindowContainer = prevTask;
backType = BackNavigationInfo.TYPE_CROSS_TASK;
}
}
@@ -345,7 +354,8 @@
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s "
+ "removedContainer:%s, backType=%s",
- prevActivity != null ? prevActivity.mActivityComponent : null,
+ prevActivities.size() > 0 ? TextUtils.join(";", prevActivities.stream()
+ .map(r -> r.mActivityComponent).toArray()) : null,
prevTask != null ? prevTask.getName() : null,
removedWindowContainer,
BackNavigationInfo.typeToString(backType));
@@ -360,7 +370,7 @@
if (prepareAnimation) {
final AnimationHandler.ScheduleAnimationBuilder builder =
mAnimationHandler.prepareAnimation(backType, adapter,
- currentTask, prevTask, currentActivity, prevActivity);
+ currentTask, prevTask, currentActivity, prevActivities);
mBackAnimationInProgress = builder != null;
if (mBackAnimationInProgress) {
if (removedWindowContainer.hasCommittedReparentToAnimationLeash()
@@ -372,8 +382,8 @@
// Current transition is still running, we have to defer the hiding to the
// client process to prevent the unexpected relayout when handling the back
// animation.
- if (prevActivity != null) {
- prevActivity.setDeferHidingClient(true);
+ for (int i = prevActivities.size() - 1; i >= 0; --i) {
+ prevActivities.get(i).setDeferHidingClient(true);
}
} else {
scheduleAnimation(builder);
@@ -381,17 +391,91 @@
}
}
infoBuilder.setPrepareRemoteAnimation(prepareAnimation);
- } // Release wm Lock
- WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
- if (finalRemovedWindowContainer != null) {
- final int finalBackType = backType;
- RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
- result, finalBackType));
- infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
+ if (removedWindowContainer != null) {
+ final int finalBackType = backType;
+ final RemoteCallback onBackNavigationDone = new RemoteCallback(result ->
+ onBackNavigationDone(result, finalBackType));
+ infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
+ } else {
+ mNavigationMonitor.stopMonitorForRemote();
+ }
+ mLastBackType = backType;
+ return infoBuilder.build();
}
- mLastBackType = backType;
- return infoBuilder.build();
+ }
+
+ /**
+ * Gets previous activities from currentActivity.
+ *
+ * @return false if unable to predict what will happen
+ */
+ private static boolean getAnimatablePrevActivities(@NonNull Task currentTask,
+ @NonNull ActivityRecord currentActivity,
+ @NonNull ArrayList<ActivityRecord> outPrevActivities) {
+ if (currentActivity.mAtmService
+ .mTaskOrganizerController.shouldInterceptBackPressedOnRootTask(
+ currentTask.getRootTask())) {
+ // The task organizer will handle back pressed, don't play animation.
+ return false;
+ }
+ final ActivityRecord root = currentTask.getRootActivity(false /*ignoreRelinquishIdentity*/,
+ true /*setToBottomIfNone*/);
+ if (root != null && ActivityClientController.shouldMoveTaskToBack(currentActivity, root)) {
+ return true;
+ }
+
+ // Searching previous
+ final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing,
+ currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ if (prevActivity == null) {
+ // No previous activity in this task, can still predict if previous task exists.
+ return true;
+ }
+ if (currentTask.getActivity((above) -> !above.finishing, currentActivity,
+ false /*includeBoundary*/, false /*traverseTopToBottom*/) != null) {
+ // another activity is above this activity, don't know what will happen
+ return false;
+ }
+
+ final TaskFragment currTF = currentActivity.getTaskFragment();
+ final TaskFragment prevTF = prevActivity.getTaskFragment();
+ if (currTF != prevTF && prevTF != null) {
+ final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment();
+ if (prevTFAdjacent != null) {
+ if (prevTFAdjacent == currTF) {
+ // Cannot predict what will happen when app receive back key, skip animation.
+ outPrevActivities.clear();
+ return false;
+ } else {
+ final ActivityRecord prevActivityAdjacent =
+ prevTFAdjacent.getTopNonFinishingActivity();
+ if (prevActivityAdjacent != null) {
+ outPrevActivities.add(prevActivityAdjacent);
+ } else {
+ // Don't know what will happen.
+ outPrevActivities.clear();
+ return false;
+ }
+ }
+ }
+ }
+ outPrevActivities.add(prevActivity);
+ return true;
+ }
+
+ private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity,
+ @NonNull ArrayList<ActivityRecord> outList) {
+ final TaskFragment mainTF = mainActivity.getTaskFragment();
+ if (mainTF == null || mainTF.getAdjacentTaskFragment() == null) {
+ return;
+ }
+ final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment();
+ final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity();
+ if (topActivity == null) {
+ return;
+ }
+ outList.add(topActivity);
}
boolean isMonitoringTransition() {
@@ -696,7 +780,7 @@
if (!hasTarget) {
// Skip if no target participated in current finished transition.
Slog.w(TAG, "Finished transition didn't include the targets"
- + " open: " + mPendingAnimationBuilder.mOpenTarget
+ + " open: " + Arrays.toString(mPendingAnimationBuilder.mOpenTargets)
+ " close: " + mPendingAnimationBuilder.mCloseTarget);
cancelPendingAnimation();
return false;
@@ -732,7 +816,7 @@
private final boolean mShowWindowlessSurface;
private final WindowManagerService mWindowManagerService;
private BackWindowAnimationAdaptor mCloseAdaptor;
- private BackWindowAnimationAdaptor mOpenAdaptor;
+ private BackWindowAnimationAdaptorWrapper mOpenAnimAdaptor;
private boolean mComposed;
private boolean mWaitTransition;
private int mSwitchType = UNKNOWN;
@@ -741,7 +825,7 @@
// exactly match animating target. When target match, reparent the starting surface to
// the opening target like starting window do.
private boolean mStartingSurfaceTargetMatch;
- private ActivityRecord mOpenActivity;
+ private ActivityRecord[] mOpenActivities;
AnimationHandler(WindowManagerService wms) {
mWindowManagerService = wms;
@@ -753,61 +837,69 @@
private static final int TASK_SWITCH = 1;
private static final int ACTIVITY_SWITCH = 2;
- private static boolean isActivitySwitch(WindowContainer close, WindowContainer open) {
- if (close.asActivityRecord() == null || open.asActivityRecord() == null
- || (close.asActivityRecord().getTask()
- != open.asActivityRecord().getTask())) {
+ private static boolean isActivitySwitch(@NonNull WindowContainer close,
+ @NonNull WindowContainer[] open) {
+ if (open == null || open.length == 0 || close.asActivityRecord() == null) {
return false;
}
+ final Task closeTask = close.asActivityRecord().getTask();
+ for (int i = open.length - 1; i >= 0; --i) {
+ if (open[i].asActivityRecord() == null
+ || (closeTask != open[i].asActivityRecord().getTask())) {
+ return false;
+ }
+ }
return true;
}
- private static boolean isTaskSwitch(WindowContainer close, WindowContainer open) {
- if (close.asTask() == null || open.asTask() == null
- || (close.asTask() == open.asTask())) {
+ private static boolean isTaskSwitch(@NonNull WindowContainer close,
+ @NonNull WindowContainer[] open) {
+ if (open == null || open.length != 1 || close.asTask() == null) {
return false;
}
- return true;
+ return open[0].asTask() != null && (close.asTask() != open[0].asTask());
}
- private void initiate(WindowContainer close, WindowContainer open,
- ActivityRecord openActivity) {
- WindowContainer closeTarget;
+ private void initiate(@NonNull WindowContainer close, @NonNull WindowContainer[] open,
+ @NonNull ActivityRecord[] openingActivities) {
if (isActivitySwitch(close, open)) {
mSwitchType = ACTIVITY_SWITCH;
- closeTarget = close.asActivityRecord();
} else if (isTaskSwitch(close, open)) {
mSwitchType = TASK_SWITCH;
- // The transition target must be activity in legacy transition.
- closeTarget = WindowManagerService.sEnableShellTransitions ? close
- : close.asTask().getTopNonFinishingActivity();
} else {
mSwitchType = UNKNOWN;
return;
}
- mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType);
- mOpenAdaptor = createAdaptor(open, true, mSwitchType);
- mOpenActivity = openActivity;
- if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) {
+ mCloseAdaptor = createAdaptor(close, false, mSwitchType);
+ if (mCloseAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
clearBackAnimateTarget();
+ return;
}
+
+ mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open);
+ if (!mOpenAnimAdaptor.isValid()) {
+ Slog.w(TAG, "compose animations fail, skip");
+ clearBackAnimateTarget();
+ return;
+ }
+ mOpenActivities = openingActivities;
}
private boolean composeAnimations(@NonNull WindowContainer close,
- @NonNull WindowContainer open, ActivityRecord openActivity) {
+ @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) {
if (mComposed || mWaitTransition) {
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
clearBackAnimateTarget();
- if (close == null || open == null || openActivity == null) {
+ if (close == null || open == null || open.length == 0 || open.length > 2) {
Slog.e(TAG, "reset animation with null target close: "
- + close + " open: " + open);
+ + close + " open: " + Arrays.toString(open));
return false;
}
- initiate(close, open, openActivity);
+ initiate(close, open, openingActivities);
if (mSwitchType == UNKNOWN) {
return false;
}
@@ -816,9 +908,14 @@
return true;
}
- RemoteAnimationTarget[] getAnimationTargets() {
- return mComposed ? new RemoteAnimationTarget[] {
- mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null;
+ @Nullable RemoteAnimationTarget[] getAnimationTargets() {
+ if (!mComposed) {
+ return null;
+ }
+ final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2];
+ targets[0] = mCloseAdaptor.mAnimationTarget;
+ targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget();
+ return targets;
}
boolean isSupportWindowlessSurface() {
@@ -826,7 +923,7 @@
.isSupportWindowlessStartingSurface();
}
- boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
+ boolean containTarget(@NonNull ArrayList<WindowContainer> wcs, boolean open) {
for (int i = wcs.size() - 1; i >= 0; --i) {
if (isTarget(wcs.get(i), open)) {
return true;
@@ -835,22 +932,35 @@
return wcs.isEmpty();
}
- boolean isTarget(WindowContainer wc, boolean open) {
+ boolean isTarget(@NonNull WindowContainer wc, boolean open) {
if (!mComposed) {
return false;
}
+ if (open) {
+ for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
+ if (isAnimateTarget(wc, mOpenAnimAdaptor.mAdaptors[i].mTarget, mSwitchType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return isAnimateTarget(wc, mCloseAdaptor.mTarget, mSwitchType);
+ }
- // WC must be ActivityRecord in legacy transition, but it also can be Task or
- // TaskFragment when using Shell transition.
- // Open target: Can be Task or ActivityRecord or TaskFragment
- // Close target: Limit to the top activity for now, to reduce the chance of misjudgment.
- final WindowContainer target = open ? mOpenAdaptor.mTarget : mCloseAdaptor.mTarget;
- if (mSwitchType == TASK_SWITCH) {
- return wc == target
- || (wc.asTask() != null && wc.hasChild(target))
- || (wc.asActivityRecord() != null && target.hasChild(wc));
- } else if (mSwitchType == ACTIVITY_SWITCH) {
- return wc == target || (wc.asTaskFragment() != null && wc.hasChild(target));
+ private static boolean isAnimateTarget(@NonNull WindowContainer window,
+ @NonNull WindowContainer animationTarget, int switchType) {
+ if (switchType == TASK_SWITCH) {
+ // simplify home search for multiple hierarchy
+ if (window.isActivityTypeHome() && animationTarget.isActivityTypeHome()) {
+ return true;
+ }
+ return window == animationTarget
+ || (animationTarget.asTask() != null && animationTarget.hasChild(window))
+ || (animationTarget.asActivityRecord() != null
+ && window.hasChild(animationTarget));
+ } else if (switchType == ACTIVITY_SWITCH) {
+ return window == animationTarget
+ || (window.asTaskFragment() != null && window.hasChild(animationTarget));
}
return false;
}
@@ -864,19 +974,25 @@
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
}
- if (mOpenAdaptor != null) {
- mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch);
- mOpenAdaptor.mTarget.cancelAnimation();
- mOpenAdaptor = null;
+ if (mOpenAnimAdaptor != null) {
+ mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch);
+ mOpenAnimAdaptor = null;
}
- if (mOpenActivity != null && mOpenActivity.mLaunchTaskBehind) {
- restoreLaunchBehind(mOpenActivity);
+
+ if (mOpenActivities != null) {
+ for (int i = mOpenActivities.length - 1; i >= 0; --i) {
+ if (mOpenActivities[i].mLaunchTaskBehind) {
+ restoreLaunchBehind(mOpenActivities[i]);
+ }
+ }
}
}
void markStartingSurfaceMatch() {
mStartingSurfaceTargetMatch = true;
- mOpenAdaptor.reparentWindowlessSurfaceToTarget();
+ for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
+ mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget();
+ }
}
void clearBackAnimateTarget() {
@@ -885,13 +1001,13 @@
mWaitTransition = false;
mStartingSurfaceTargetMatch = false;
mSwitchType = UNKNOWN;
- mOpenActivity = null;
+ mOpenActivities = null;
}
// The close target must in close list
// The open target can either in close or open list
- boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps,
- ArrayList<WindowContainer> closeApps) {
+ boolean containsBackAnimationTargets(@NonNull ArrayList<WindowContainer> openApps,
+ @NonNull ArrayList<WindowContainer> closeApps) {
return containTarget(closeApps, false /* open */)
&& (containTarget(openApps, true /* open */)
|| containTarget(openApps, false /* open */));
@@ -901,9 +1017,9 @@
public String toString() {
return "AnimationTargets{"
+ " openTarget= "
- + (mOpenAdaptor != null ? mOpenAdaptor.mTarget : "null")
+ + (mOpenAnimAdaptor != null ? dumpOpenAnimTargetsToString() : null)
+ " closeTarget= "
- + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : "null")
+ + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : null)
+ " mSwitchType= "
+ mSwitchType
+ " mComposed= "
@@ -913,8 +1029,21 @@
+ '}';
}
- private static BackWindowAnimationAdaptor createAdaptor(
- WindowContainer target, boolean isOpen, int switchType) {
+ private String dumpOpenAnimTargetsToString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ for (int i = 0; i < mOpenAnimAdaptor.mAdaptors.length; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mOpenAnimAdaptor.mAdaptors[i].mTarget);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @NonNull private static BackWindowAnimationAdaptor createAdaptor(
+ @NonNull WindowContainer target, boolean isOpen, int switchType) {
final BackWindowAnimationAdaptor adaptor =
new BackWindowAnimationAdaptor(target, isOpen, switchType);
final SurfaceControl.Transaction pt = target.getPendingTransaction();
@@ -930,6 +1059,100 @@
return adaptor;
}
+ private static class BackWindowAnimationAdaptorWrapper {
+ final BackWindowAnimationAdaptor[] mAdaptors;
+ SurfaceControl.Transaction mCloseTransaction;
+
+ BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,
+ @NonNull WindowContainer... targets) {
+ mAdaptors = new BackWindowAnimationAdaptor[targets.length];
+ for (int i = targets.length - 1; i >= 0; --i) {
+ mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType);
+ }
+ }
+
+ boolean isValid() {
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ if (mAdaptors[i].mAnimationTarget == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void cleanUp(boolean startingSurfaceMatch) {
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch);
+ mAdaptors[i].mTarget.cancelAnimation();
+ }
+ if (mCloseTransaction != null) {
+ mCloseTransaction.apply();
+ mCloseTransaction = null;
+ }
+ }
+
+ void onAnimationFinish() {
+ final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction();
+ if (mCloseTransaction != null) {
+ pt.merge(mCloseTransaction);
+ mCloseTransaction = null;
+ }
+ if (mAdaptors.length > 1) {
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ final WindowContainer wc = mAdaptors[i].mTarget;
+ final WindowContainer parent = wc.getParent();
+ if (parent != null) {
+ pt.reparent(wc.getSurfaceControl(),
+ parent.getSurfaceControl());
+ }
+ }
+ }
+ }
+
+ @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() {
+ // Special handle for opening two activities together.
+ // If we animate both activities separately, the animation area and rounded corner
+ // would also being handled separately. To make them seem like "open" together, wrap
+ // their leash with another animation leash.
+ if (mAdaptors.length > 1 && mCloseTransaction == null) {
+ final Rect unionBounds = new Rect();
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
+ }
+ final WindowContainer wc = mAdaptors[0].mTarget;
+ final Task task = wc.asActivityRecord() != null
+ ? wc.asActivityRecord().getTask() : wc.asTask();
+ final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
+ final SurfaceControl leashSurface = new SurfaceControl.Builder()
+ .setName("cross-animation-leash")
+ .setContainerLayer()
+ .setHidden(false)
+ .setParent(task.getSurfaceControl())
+ .build();
+ final SurfaceControl.Transaction pt = wc.getPendingTransaction();
+ pt.setLayer(leashSurface, wc.getParent().getLastLayer());
+ mCloseTransaction = new SurfaceControl.Transaction();
+ mCloseTransaction.reparent(leashSurface, null);
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ BackWindowAnimationAdaptor adaptor = mAdaptors[i];
+ pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
+ pt.setPosition(adaptor.mAnimationTarget.leash,
+ adaptor.mAnimationTarget.localBounds.left,
+ adaptor.mAnimationTarget.localBounds.top);
+ }
+ return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface,
+ represent.isTranslucent, represent.clipRect, represent.contentInsets,
+ represent.prefixOrderIndex,
+ new Point(unionBounds.left, unionBounds.top),
+ unionBounds, unionBounds, represent.windowConfiguration,
+ true /* isNotInRecents */, null, null, represent.taskInfo,
+ represent.allowEnterPip);
+ } else {
+ return mAdaptors[0].mAnimationTarget;
+ }
+ }
+ }
+
private static class BackWindowAnimationAdaptor implements AnimationAdapter {
SurfaceControl mCapturedLeash;
private final Rect mBounds = new Rect();
@@ -943,7 +1166,7 @@
private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
private SurfaceControl mStartingSurface;
- BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen,
+ BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen,
int switchType) {
mBounds.set(target.getBounds());
mTarget = target;
@@ -1034,7 +1257,7 @@
return mAnimationTarget;
}
- void createStartingSurface() {
+ void createStartingSurface(@NonNull WindowContainer closeWindow) {
if (!mIsOpen) {
return;
}
@@ -1053,7 +1276,10 @@
final TaskSnapshot snapshot = getSnapshot(mTarget);
mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
.addWindowlessStartingSurface(openTask, mainActivity,
- mAnimationTarget.leash, snapshot,
+ // Choose configuration from closeWindow, because the configuration
+ // of opening target may not update before resume, so the starting
+ // surface should occlude it entirely.
+ mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(),
new IWindowlessStartingSurfaceCallback.Stub() {
// Once the starting surface has been created in shell, it will call
// onSurfaceAdded to pass the created surface to core, so if a
@@ -1108,15 +1334,17 @@
ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter,
Task currentTask, Task previousTask, ActivityRecord currentActivity,
- ActivityRecord previousActivity) {
+ ArrayList<ActivityRecord> previousActivity) {
switch (backType) {
case BackNavigationInfo.TYPE_RETURN_TO_HOME:
return new ScheduleAnimationBuilder(backType, adapter)
.setIsLaunchBehind(true)
.setComposeTarget(currentTask, previousTask);
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
+ ActivityRecord[] prevActs = new ActivityRecord[previousActivity.size()];
+ prevActs = previousActivity.toArray(prevActs);
return new ScheduleAnimationBuilder(backType, adapter)
- .setComposeTarget(currentActivity, previousActivity)
+ .setComposeTarget(currentActivity, prevActs)
.setIsLaunchBehind(false);
case BackNavigationInfo.TYPE_CROSS_TASK:
return new ScheduleAnimationBuilder(backType, adapter)
@@ -1130,7 +1358,7 @@
final int mType;
final BackAnimationAdapter mBackAnimationAdapter;
WindowContainer mCloseTarget;
- WindowContainer mOpenTarget;
+ WindowContainer[] mOpenTargets;
boolean mIsLaunchBehind;
ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) {
@@ -1138,9 +1366,10 @@
mBackAnimationAdapter = backAnimationAdapter;
}
- ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) {
+ ScheduleAnimationBuilder setComposeTarget(@NonNull WindowContainer close,
+ @NonNull WindowContainer... open) {
mCloseTarget = close;
- mOpenTarget = open;
+ mOpenTargets = open;
return this;
}
@@ -1150,43 +1379,60 @@
}
boolean containTarget(@NonNull WindowContainer wc) {
- return wc == mOpenTarget || wc == mCloseTarget
- || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc);
+ if (mOpenTargets != null) {
+ for (int i = mOpenTargets.length - 1; i >= 0; --i) {
+ if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc)) {
+ return true;
+ }
+ }
+ }
+ return wc == mCloseTarget || mCloseTarget.hasChild(wc);
}
/**
* Apply preview strategy on the opening target
+ * @param closeWindow The close window, where it's configuration should cover all
+ * open target(s).
* @param openAnimationAdaptor The animator who can create starting surface.
- * @param visibleOpenActivity The visible activity in opening target.
+ * @param visibleOpenActivities The visible activities in opening targets.
*/
- private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor,
- ActivityRecord visibleOpenActivity) {
- if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
- openAnimationAdaptor.createStartingSurface();
- return;
+ private void applyPreviewStrategy(@NonNull WindowContainer closeWindow,
+ @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor,
+ @NonNull ActivityRecord[] visibleOpenActivities) {
+ if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind
+ // TODO (b/274997067) Draw two snapshot in a single starting surface.
+ // We are using TaskId as the key of
+ // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create
+ // two activity snapshot with WindowlessStartingWindow.
+ // Try to draw two snapshot within a WindowlessStartingWindow, or find
+ // another key for StartingWindowRecordManager.
+ && openAnimationAdaptor.length == 1) {
+ openAnimationAdaptor[0].createStartingSurface(closeWindow);
+ } else {
+ for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
+ setLaunchBehind(visibleOpenActivities[i]);
+ }
}
- setLaunchBehind(visibleOpenActivity);
}
- Runnable build() {
- if (mOpenTarget == null || mCloseTarget == null) {
+ @Nullable Runnable build() {
+ if (mOpenTargets == null || mCloseTarget == null || mOpenTargets.length == 0) {
return null;
}
- final ActivityRecord openActivity = mOpenTarget.asTask() != null
- ? mOpenTarget.asTask().getTopNonFinishingActivity()
- : mOpenTarget.asActivityRecord() != null
- ? mOpenTarget.asActivityRecord() : null;
- if (openActivity == null) {
+ final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface();
+ final ActivityRecord[] openingActivities = getTopOpenActivities(mOpenTargets);
+
+ if (shouldLaunchBehind && openingActivities == null) {
Slog.e(TAG, "No opening activity");
return null;
}
- if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
+ if (!composeAnimations(mCloseTarget, mOpenTargets, openingActivities)) {
return null;
}
mCloseTarget.mTransitionController.mSnapshotController
.mActivitySnapshotController.clearOnBackPressedActivities();
- applyPreviewStrategy(mOpenAdaptor, openActivity);
+ applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1210,6 +1456,7 @@
// animation was canceled
return;
}
+ mOpenAnimAdaptor.onAnimationFinish();
if (!triggerBack) {
clearBackAnimateTarget();
} else {
@@ -1223,6 +1470,41 @@
}
}
+ /**
+ * Finds next opening activity(ies) based on open targets, which could be:
+ * 1. If the open window is Task, then the open activity can either be an activity, or
+ * two activities inside two TaskFragments
+ * 2. If the open window is Activity, then the open window can be an activity, or two
+ * adjacent TaskFragments below it.
+ */
+ @Nullable
+ private static ActivityRecord[] getTopOpenActivities(
+ @NonNull WindowContainer[] openWindows) {
+ ActivityRecord[] openActivities = null;
+ final WindowContainer mainTarget = openWindows[0];
+ if (mainTarget.asTask() != null) {
+ final ArrayList<ActivityRecord> inTaskActivities = new ArrayList<>();
+ final Task task = mainTarget.asTask();
+ final ActivityRecord tmpPreActivity = task.getTopNonFinishingActivity();
+ if (tmpPreActivity != null) {
+ inTaskActivities.add(tmpPreActivity);
+ findAdjacentActivityIfExist(tmpPreActivity, inTaskActivities);
+ }
+
+ openActivities = new ActivityRecord[inTaskActivities.size()];
+ for (int i = inTaskActivities.size() - 1; i >= 0; --i) {
+ openActivities[i] = inTaskActivities.get(i);
+ }
+ } else if (mainTarget.asActivityRecord() != null) {
+ final int size = openWindows.length;
+ openActivities = new ActivityRecord[size];
+ for (int i = size - 1; i >= 0; --i) {
+ openActivities[i] = openWindows[i].asActivityRecord();
+ }
+ }
+ return openActivities;
+ }
+
private static void setLaunchBehind(@NonNull ActivityRecord activity) {
if (!activity.isVisibleRequested()) {
activity.setVisibility(true);
@@ -1311,7 +1593,7 @@
static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
if (w.asTask() != null) {
final Task task = w.asTask();
- return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
+ return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
task.mTaskId, task.mUserId, false /* restoreFromDisk */,
false /* isLowResolution */);
}
@@ -1340,8 +1622,10 @@
proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress);
proto.write(LAST_BACK_TYPE, mLastBackType);
proto.write(SHOW_WALLPAPER, mShowWallpaper);
- if (mAnimationHandler.mOpenActivity != null) {
- mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY);
+ if (mAnimationHandler.mOpenAnimAdaptor != null
+ && mAnimationHandler.mOpenAnimAdaptor.mAdaptors.length > 0) {
+ mAnimationHandler.mOpenActivities[0].writeNameToProto(
+ proto, MAIN_OPEN_ACTIVITY);
} else {
proto.write(MAIN_OPEN_ACTIVITY, "");
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4579cc1..668cd87 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -28,6 +28,7 @@
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
+import static com.android.window.flags.Flags.balShowToasts;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -291,6 +292,10 @@
return name + "[debugOnly]";
}
+ private boolean callerIsRealCaller() {
+ return mCallingUid == mRealCallingUid;
+ }
+
private String dump(BalVerdict resultIfPiCreatorAllowsBal,
BalVerdict resultIfPiSenderAllowsBal) {
return " [callingPackage: " + getDebugPackageName(mCallingPackage, mCallingUid)
@@ -422,7 +427,7 @@
}
BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
- BalVerdict resultForRealCaller = callingUid == realCallingUid && resultForCaller.allows()
+ BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
? resultForCaller // no need to calculate again
// otherwise we might need to recalculate because the logic is not the same
: checkBackgroundActivityStartAllowedBySender(state, checkedOptions);
@@ -455,6 +460,7 @@
"With Android 15 BAL hardening this activity start would be blocked"
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
+ showBalToast("BAL would be blocked", state);
// return the realCaller result for backwards compatibility
return statsLog(resultForRealCaller, state);
}
@@ -466,6 +472,7 @@
"With Android 15 BAL hardening this activity start would be blocked"
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
+ showBalToast("BAL would be blocked", state);
return statsLog(resultForCaller, state);
}
if (resultForRealCaller.allows()
@@ -477,6 +484,7 @@
"With Android 14 BAL hardening this activity start would be blocked"
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
+ showBalToast("BAL would be blocked", state);
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
@@ -484,6 +492,7 @@
+ state.dump(resultForCaller, resultForRealCaller));
// fall through
}
+ showBalToast("BAL blocked", state);
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity launch blocked"
+ state.dump(resultForCaller, resultForRealCaller));
@@ -636,16 +645,14 @@
BalVerdict checkBackgroundActivityStartAllowedBySender(
BalState state,
ActivityOptions checkedOptions) {
- int realCallingUid = state.mRealCallingUid;
- BackgroundStartPrivileges backgroundStartPrivileges = state.mBackgroundStartPrivileges;
if (PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions)
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
- realCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+ state.mRealCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
- "realCallingUid has BAL permission. realCallingUid: " + realCallingUid);
+ "realCallingUid has BAL permission.");
}
// don't abort if the realCallingUid has a visible window
@@ -653,26 +660,23 @@
if (state.mRealCallingUidHasAnyVisibleWindow) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
- "realCallingUid has visible (non-toast) window. realCallingUid: "
- + realCallingUid);
+ "realCallingUid has visible (non-toast) window.");
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mIsRealCallingUidPersistentSystemProcess
- && backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+ && state.mBackgroundStartPrivileges.allowsBackgroundActivityStarts()) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
"realCallingUid is persistent system process AND intent "
- + "sender allowed (allowBackgroundActivityStart = true). "
- + "realCallingUid: " + realCallingUid);
+ + "sender allowed (allowBackgroundActivityStart = true).");
}
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(
- UserHandle.getUserId(realCallingUid), realCallingUid)) {
+ UserHandle.getUserId(state.mRealCallingUid), state.mRealCallingUid)) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
- "realCallingUid is a companion app. "
- + "realCallingUid: " + realCallingUid);
+ "realCallingUid is a companion app.");
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
@@ -863,8 +867,7 @@
+ (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
+ getApplicationLabel(mService.mContext.getPackageManager(),
launchedFromPackageName);
- UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- toastText, Toast.LENGTH_LONG).show());
+ showToast(toastText);
Slog.i(TAG, asmDebugInfo);
}
@@ -883,6 +886,19 @@
return true;
}
+ private void showBalToast(String toastText, BalState state) {
+ if (balShowToasts()) {
+ showToast(toastText
+ + " caller:" + state.mCallingPackage
+ + " realCaller:" + state.mRealCallingPackage);
+ }
+ }
+
+ private void showToast(String toastText) {
+ UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
+ toastText, Toast.LENGTH_LONG).show());
+ }
+
/**
* If the top activity uid does not match the launching or launched activity, and the launch was
* not requested from the top uid, we want to clear out all non matching activities to prevent
@@ -931,12 +947,10 @@
if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)
&& (!shouldBlockActivityStart || finishCount[0] > 0)) {
- UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- (shouldBlockActivityStart
- ? "Top activities cleared by "
- : "Top activities would be cleared by ")
- + ActivitySecurityModelFeatureFlags.DOC_LINK,
- Toast.LENGTH_LONG).show());
+ showToast((shouldBlockActivityStart
+ ? "Top activities cleared by "
+ : "Top activities would be cleared by ")
+ + ActivitySecurityModelFeatureFlags.DOC_LINK);
Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
@@ -1014,11 +1028,10 @@
}
if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
- UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
- (ActivitySecurityModelFeatureFlags.DOC_LINK
- + (restrictActivitySwitch ? " returned home due to "
- : " would return home due to ")
- + callingLabel), Toast.LENGTH_LONG).show());
+ showToast((ActivitySecurityModelFeatureFlags.DOC_LINK
+ + (restrictActivitySwitch ? " returned home due to "
+ : " would return home due to ")
+ + callingLabel));
}
// If the activity switch should be restricted, return home rather than the
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 275396f..1462878 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -150,8 +150,8 @@
/**
* A unique token associated with the embedded window that can be used by the host window
- * to request focus transfer to the embedded. This is not the input token since we don't
- * want to give clients access to each others input token.
+ * to request focus transfer and gesture transfer to the embedded. This is not the input
+ * token since we don't want to give clients access to each others input token.
*/
private final IBinder mInputTransferToken;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index fa2c94a..ccaa3b0 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -665,12 +665,14 @@
mTopTurnScreenOnActivity = top;
}
- if (top.mDismissKeyguard && mKeyguardShowing) {
+ final boolean isKeyguardSecure = controller.mWindowManager.isKeyguardSecure(
+ controller.mService.getCurrentUserId());
+ if (top.mDismissKeyguardIfInsecure && mKeyguardShowing && !isKeyguardSecure) {
mKeyguardGoingAway = true;
} else if (top.canShowWhenLocked()) {
mTopOccludesActivity = top;
}
- top.mDismissKeyguard = false;
+ top.mDismissKeyguardIfInsecure = false;
// Only the top activity may control occluded, as we can't occlude the Keyguard
// if the top app doesn't want to occlude it.
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4a467df..e82f322 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -174,10 +174,14 @@
private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
+ private static final int MSG_SEND_SLEEP_TRANSITION = 3;
+
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+ private static final long SLEEP_TRANSITION_WAIT_MILLIS = 1000L;
+
private Object mLastWindowFreezeSource = null;
private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private long mUserActivityTimeout = -1;
@@ -1132,6 +1136,11 @@
mWmService.mPowerManagerInternal.
setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj);
break;
+ case MSG_SEND_SLEEP_TRANSITION:
+ synchronized (mService.mGlobalLock) {
+ sendSleepTransition((DisplayContent) msg.obj);
+ }
+ break;
default:
break;
}
@@ -1681,8 +1690,8 @@
return false;
}
- if (!StorageManager.isUserKeyUnlocked(mCurrentUser)) {
- // Can't launch home on secondary display areas if device is still locked.
+ if (!StorageManager.isCeStorageUnlocked(mCurrentUser)) {
+ // Can't launch home on secondary display areas if CE storage is still locked.
return false;
}
@@ -2442,8 +2451,38 @@
return result;
}
+ void sendSleepTransition(final DisplayContent display) {
+ // We don't actually care about collecting anything here. We really just want
+ // this as a signal to the transition-player.
+ final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
+ display.mTransitionController, mWmService.mSyncEngine);
+ final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> {
+ if (deferred && !display.shouldSleep()) {
+ transition.abort();
+ } else {
+ display.mTransitionController.requestStartTransition(transition,
+ null /* trigger */, null /* remote */, null /* display */);
+ // Force playing immediately so that unrelated ops can't be collected.
+ transition.playNow();
+ }
+ };
+ if (!display.mTransitionController.isCollecting()) {
+ // Since this bypasses sync, submit directly ignoring whether sync-engine
+ // is active.
+ if (mWindowManager.mSyncEngine.hasActiveSync()) {
+ Slog.w(TAG, "Ongoing sync outside of a transition.");
+ }
+ display.mTransitionController.moveToCollecting(transition);
+ sendSleepTransition.onCollectStarted(false /* deferred */);
+ } else {
+ display.mTransitionController.startCollectOrQueue(transition,
+ sendSleepTransition);
+ }
+ }
+
void applySleepTokens(boolean applyToRootTasks) {
- boolean builtSleepTransition = false;
+ boolean scheduledSleepTransition = false;
+
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
final DisplayContent display = getChildAt(displayNdx);
@@ -2453,35 +2492,16 @@
}
display.setIsSleeping(displayShouldSleep);
- if (display.mTransitionController.isShellTransitionsEnabled() && !builtSleepTransition
+ if (display.mTransitionController.isShellTransitionsEnabled()
+ && !scheduledSleepTransition
// Only care if there are actual sleep tokens.
&& displayShouldSleep && !display.mAllSleepTokens.isEmpty()) {
- builtSleepTransition = true;
- // We don't actually care about collecting anything here. We really just want
- // this as a signal to the transition-player.
- final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
- display.mTransitionController, mWmService.mSyncEngine);
- final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> {
- if (deferred && !display.shouldSleep()) {
- transition.abort();
- } else {
- display.mTransitionController.requestStartTransition(transition,
- null /* trigger */, null /* remote */, null /* display */);
- // Force playing immediately so that unrelated ops can't be collected.
- transition.playNow();
- }
- };
- if (!display.mTransitionController.isCollecting()) {
- // Since this bypasses sync, submit directly ignoring whether sync-engine
- // is active.
- if (mWindowManager.mSyncEngine.hasActiveSync()) {
- Slog.w(TAG, "Ongoing sync outside of a transition.");
- }
- display.mTransitionController.moveToCollecting(transition);
- sendSleepTransition.onCollectStarted(false /* deferred */);
- } else {
- display.mTransitionController.startCollectOrQueue(transition,
- sendSleepTransition);
+ scheduledSleepTransition = true;
+
+ if (!mHandler.hasMessages(MSG_SEND_SLEEP_TRANSITION)) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_SEND_SLEEP_TRANSITION, display),
+ SLEEP_TRANSITION_WAIT_MILLIS);
}
}
@@ -2535,6 +2555,10 @@
}
});
}
+
+ if (!scheduledSleepTransition) {
+ mHandler.removeMessages(MSG_SEND_SLEEP_TRANSITION);
+ }
}
protected Task getRootTask(int rooTaskId) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6418148..4ced5d5 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -343,14 +343,14 @@
}
// Check if the caller is allowed to dismiss keyguard.
- final boolean dismissKeyguard = options.getDismissKeyguard();
- if (aInfo != null && dismissKeyguard) {
+ final boolean dismissKeyguardIfInsecure = options.getDismissKeyguardIfInsecure();
+ if (aInfo != null && dismissKeyguardIfInsecure) {
final int controlKeyguardPerm = ActivityTaskManagerService.checkPermission(
CONTROL_KEYGUARD, callingPid, callingUid);
if (controlKeyguardPerm != PERMISSION_GRANTED) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
+ " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ") with dismissKeyguard=true";
+ + ", uid=" + callingUid + ") with dismissKeyguardIfInsecure=true";
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3775ccd..0c55d8a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -86,6 +86,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerService.H;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.Collections;
@@ -166,8 +167,8 @@
mCanSetUnrestrictedGestureExclusion =
service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_GESTURE_EXCLUSION)
== PERMISSION_GRANTED;
- mCanAlwaysUpdateWallpaper =
- service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER)
+ mCanAlwaysUpdateWallpaper = Flags.alwaysUpdateWallpaperPermission()
+ && service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER)
== PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
@@ -963,6 +964,23 @@
}
@Override
+ public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
+ IBinder inputTransferToken) {
+ if (hostWindow == null) {
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ boolean didTransfer;
+ try {
+ didTransfer = mService.transferHostTouchGestureToEmbedded(this, hostWindow,
+ inputTransferToken);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return didTransfer;
+ }
+ @Override
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5c5a1e1..f348928 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3508,10 +3508,13 @@
top.mLetterboxUiController.getLetterboxPositionForVerticalReachability();
}
}
- // User Aspect Ratio Settings is enabled if the app is not in SCM
+ // User Aspect Ratio Settings button is enabled if the app is not in SCM and has
+ // launchable activities
info.topActivityEligibleForUserAspectRatioButton = top != null
&& !info.topActivityInSizeCompat
- && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings()
+ && mAtmService.mContext.getPackageManager()
+ .getLaunchIntentForPackage(getBasePackageName()) != null;
info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 34ae370..e7a1cf1 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -49,6 +49,7 @@
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
+import android.window.RemoteTransition;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
import android.window.TaskFragmentParentInfo;
@@ -566,7 +567,8 @@
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) {
if (isValidTransaction(wct)) {
- applyTransaction(wct, transitionType, shouldApplyIndependently);
+ applyTransaction(
+ wct, transitionType, shouldApplyIndependently, null /* remoteTransition */);
}
// Even if the transaction is empty, we still need to invoke #onTransactionFinished
// unless the organizer has been unregistered.
@@ -587,14 +589,15 @@
@Override
public void applyTransaction(@NonNull WindowContainerTransaction wct,
- @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
+ @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently,
+ @Nullable RemoteTransition remoteTransition) {
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) {
if (!isValidTransaction(wct)) {
return;
}
mWindowOrganizerController.applyTaskFragmentTransactionLocked(wct, transitionType,
- shouldApplyIndependently);
+ shouldApplyIndependently, remoteTransition);
}
}
@@ -839,6 +842,7 @@
Slog.e(TAG, "Caller organizer=" + organizer + " is no longer registered");
return false;
}
+
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 41e49b9..12392a6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -35,6 +35,7 @@
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -733,7 +734,8 @@
* the task was removed from hierarchy.
*/
int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root,
- TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) {
+ TaskSnapshot taskSnapshot, Configuration configuration,
+ IWindowlessStartingSurfaceCallback callback) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return INVALID_TASK_ID;
@@ -743,6 +745,7 @@
return INVALID_TASK_ID;
}
final StartingWindowInfo info = task.getStartingWindowInfo(activity);
+ info.taskInfo.configuration.setTo(configuration);
info.taskInfo.taskDescription = activity.taskDescription;
info.taskSnapshot = taskSnapshot;
info.windowlessStartingSurfaceCallback = callback;
@@ -1195,8 +1198,7 @@
}
public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
- if (task == null || !task.isOrganized()
- || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
+ if (!shouldInterceptBackPressedOnRootTask(task)) {
return false;
}
final TaskOrganizerPendingEventsQueue pendingEventsQueue =
@@ -1229,6 +1231,11 @@
return true;
}
+ boolean shouldInterceptBackPressedOnRootTask(Task task) {
+ return task != null && task.isOrganized()
+ && mInterceptBackPressedOnRootTasks.contains(task.mTaskId);
+ }
+
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println("TaskOrganizerController:");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9fb7e8d..6e3d24b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2020,7 +2020,7 @@
}
if (win.mActivityRecord != null) {
- win.mActivityRecord.postWindowRemoveStartingWindowCleanup();
+ win.mActivityRecord.postWindowRemoveStartingWindowCleanup(win);
}
if (win.mAttrs.type == TYPE_WALLPAPER) {
@@ -8960,6 +8960,43 @@
}
}
+ boolean transferHostTouchGestureToEmbedded(Session session, IWindow hostWindow,
+ IBinder inputTransferToken) {
+ final IBinder hostInputChannel, embeddedInputChannel;
+ synchronized (mGlobalLock) {
+ final WindowState hostWindowState = windowForClientLocked(session, hostWindow, false);
+ if (hostWindowState == null) {
+ Slog.w(TAG, "Attempt to transfer touch gesture with invalid host window");
+ return false;
+ }
+
+ final EmbeddedWindowController.EmbeddedWindow ew =
+ mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
+ if (ew == null || ew.mHostWindowState == null) {
+ Slog.w(TAG, "Attempt to transfer touch gesture to non-existent embedded window");
+ return false;
+ }
+ if (ew.mHostWindowState.mClient.asBinder() != hostWindow.asBinder()) {
+ Slog.w(TAG, "Attempt to transfer touch gesture to embedded window not associated"
+ + " with host window");
+ return false;
+ }
+ embeddedInputChannel = ew.getInputChannelToken();
+ if (embeddedInputChannel == null) {
+ Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input"
+ + " channel");
+ return false;
+ }
+ hostInputChannel = hostWindowState.mInputChannelToken;
+ if (hostInputChannel == null) {
+ Slog.w(TAG,
+ "Attempt to transfer touch focus to a host window with no input channel");
+ return false;
+ }
+ return mInputManager.transferTouchFocus(hostInputChannel, embeddedInputChannel);
+ }
+ }
+
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a8b9417..95e2515 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -103,6 +103,7 @@
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
+import android.window.RemoteTransition;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentOperation;
@@ -464,12 +465,20 @@
* transition, which will be queued until the sync engine is
* free if there is any other active sync. If {@code false},
* the {@code wct} will be directly applied to the active sync.
+ * @param remoteTransition {@link RemoteTransition} to apply for the transaction. Only available
+ * for system organizers.
*/
void applyTaskFragmentTransactionLocked(@NonNull WindowContainerTransaction wct,
- @WindowManager.TransitionType int type, boolean shouldApplyIndependently) {
+ @WindowManager.TransitionType int type, boolean shouldApplyIndependently,
+ @Nullable RemoteTransition remoteTransition) {
enforceTaskFragmentOrganizerPermission("applyTaskFragmentTransaction()",
Objects.requireNonNull(wct.getTaskFragmentOrganizer()),
Objects.requireNonNull(wct));
+ if (remoteTransition != null && !mTaskFragmentOrganizerController.isSystemOrganizer(
+ wct.getTaskFragmentOrganizer().asBinder())) {
+ throw new SecurityException(
+ "Only a system organizer is allowed to use remote transition!");
+ }
final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
@@ -512,7 +521,7 @@
return;
}
mTransitionController.requestStartTransition(transition, null /* startTask */,
- null /* remoteTransition */, null /* displayChange */);
+ remoteTransition, null /* displayChange */);
transition.setAllReady();
};
mTransitionController.startCollectOrQueue(transition, doApply);
@@ -1135,16 +1144,12 @@
if (pipTask == null) {
break;
}
- ActivityRecord[] pipActivity = new ActivityRecord[1];
- pipTask.forAllActivities((activity) -> {
- if (activity.pictureInPictureArgs != null) {
- pipActivity[0] = activity;
- }
- });
+ ActivityRecord pipActivity = pipTask.getActivity(
+ (activity) -> activity.pictureInPictureArgs != null);
Rect entryBounds = hop.getBounds();
mService.mRootWindowContainer.moveActivityToPinnedRootTask(
- pipActivity[0], null /* launchIntoPipHostActivity */,
+ pipActivity, null /* launchIntoPipHostActivity */,
"moveActivityToPinnedRootTask", null /* transition */, entryBounds);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 33751b9..209d934 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -28,7 +28,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
import android.os.Debug;
@@ -53,18 +52,6 @@
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
- private float mSurfaceX = 0;
- private float mSurfaceY = 0;
-
- // Initialize to the identity matrix.
- private float mLastDsdx = 1;
- private float mLastDtdx = 0;
- private float mLastDsdy = 0;
- private float mLastDtdy = 1;
-
- private float mSurfaceAlpha = 0;
-
- private int mSurfaceLayer = 0;
private final String title;
@@ -73,8 +60,6 @@
private final int mWindowType;
private final Session mWindowSession;
- // Used to track whether we have called detach children on the way to invisibility.
- boolean mChildrenDetached;
WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
int windowType) {
@@ -157,44 +142,11 @@
}
}
- void setPosition(SurfaceControl.Transaction t, float left, float top) {
- final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
- if (!surfaceMoved) {
- return;
- }
-
- mSurfaceX = left;
- mSurfaceY = top;
-
- ProtoLog.i(WM_SHOW_TRANSACTIONS,
- "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s", left, top, title);
-
- t.setPosition(mSurfaceControl, left, top);
- }
-
- void setMatrix(SurfaceControl.Transaction t, float dsdx, float dtdx, float dtdy, float dsdy) {
- final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx ||
- mLastDtdy != dtdy || mLastDsdy != dsdy;
- if (!matrixChanged) {
- return;
- }
-
- mLastDsdx = dsdx;
- mLastDtdx = dtdx;
- mLastDtdy = dtdy;
- mLastDsdy = dsdy;
-
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE MATRIX [%f,%f,%f,%f]: %s",
- dsdx, dtdx, dtdy, dsdy, title);
- t.setMatrix(mSurfaceControl, dsdx, dtdx, dtdy, dsdy);
- }
-
boolean prepareToShowInTransaction(SurfaceControl.Transaction t, float alpha) {
if (mSurfaceControl == null) {
return false;
}
- mSurfaceAlpha = alpha;
t.setAlpha(mSurfaceControl, alpha);
return true;
}
@@ -305,7 +257,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(SHOWN, mSurfaceShown);
- proto.write(LAYER, mSurfaceLayer);
proto.end(token);
}
@@ -314,13 +265,6 @@
pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
}
pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
- pw.print(" layer="); pw.print(mSurfaceLayer);
- pw.print(" alpha="); pw.print(mSurfaceAlpha);
- pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY); pw.print(") ");
- pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
- pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
- pw.print(", "); pw.print(mLastDtdy); pw.println(")");
}
@Override
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index a4adf58..627461a 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -862,37 +862,41 @@
Slog.i(TAG, "isEnabledCredentialProviderService with componentName: "
+ componentName.flattenToString());
- // TODO(253157366): Check additional set of services.
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
enforceCallingPackage(callingPackage, callingUid);
- synchronized (mLock) {
- final List<CredentialManagerServiceImpl> services =
- getServiceListForUserLocked(userId);
- for (CredentialManagerServiceImpl s : services) {
- final ComponentName serviceComponentName = s.getServiceComponentName();
- if (serviceComponentName.equals(componentName)) {
- if (!s.getServicePackageName().equals(callingPackage)) {
- // The component name and the package name do not match.
- MetricUtilities.logApiCalledSimpleV2(
- ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.FAILURE, callingUid);
- Slog.w(
- TAG,
- "isEnabledCredentialProviderService: Component name does "
- + "not match package name.");
- return false;
- }
- MetricUtilities.logApiCalledSimpleV2(
- ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.SUCCESS, callingUid);
- return true;
- }
- }
+ if (componentName == null) {
+ Slog.w(TAG, "isEnabledCredentialProviderService componentName is null");
+ // If the component name was not specified then throw an error and
+ // record a failure because the request failed due to invalid input.
+ MetricUtilities.logApiCalledSimpleV2(
+ ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
+ ApiStatus.FAILURE, callingUid);
+ return false;
}
- return false;
+ if (!componentName.getPackageName().equals(callingPackage)) {
+ Slog.w(TAG, "isEnabledCredentialProviderService component name"
+ + " does not match requested component");
+ // If the requested component name package name does not match
+ // the calling package then throw an error and record a failure
+ // metric (because the request failed due to invalid input).
+ MetricUtilities.logApiCalledSimpleV2(
+ ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
+ ApiStatus.FAILURE, callingUid);
+ throw new IllegalArgumentException("provided component name does not match"
+ + " does not match requesting component");
+ }
+
+ final Set<ComponentName> enabledProviders = getEnabledProvidersForUser(userId);
+ MetricUtilities.logApiCalledSimpleV2(
+ ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
+ ApiStatus.SUCCESS, callingUid);
+ if (enabledProviders == null) {
+ return false;
+ }
+ return enabledProviders.contains(componentName);
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index d960439..395ea91 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -16,8 +16,6 @@
package com.android.server.devicepolicy;
-import static com.android.server.devicepolicy.DevicePolicyManagerService.DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -200,7 +198,7 @@
* Effective state of the feature flag. It is updated to the current configuration value
* during boot and doesn't change value after than unless overridden by test code.
*/
- boolean mEffectiveKeepProfilesRunning = DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
+ boolean mEffectiveKeepProfilesRunning = false;
DevicePolicyData(@UserIdInt int userId) {
mUserId = userId;
@@ -401,7 +399,7 @@
out.endTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS);
}
- if (policyData.mEffectiveKeepProfilesRunning != DEFAULT_KEEP_PROFILES_RUNNING_FLAG) {
+ if (policyData.mEffectiveKeepProfilesRunning) {
out.startTag(null, TAG_KEEP_PROFILES_RUNNING);
out.attributeBoolean(null, ATTR_VALUE, policyData.mEffectiveKeepProfilesRunning);
out.endTag(null, TAG_KEEP_PROFILES_RUNNING);
@@ -592,7 +590,7 @@
policy.mCurrentRoleHolder = parser.getAttributeValue(null, ATTR_VALUE);
} else if (TAG_KEEP_PROFILES_RUNNING.equals(tag)) {
policy.mEffectiveKeepProfilesRunning = parser.getAttributeBoolean(
- null, ATTR_VALUE, DEFAULT_KEEP_PROFILES_RUNNING_FLAG);
+ null, ATTR_VALUE, false);
// Deprecated tags below
} else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
if (policy.mUserControlDisabledPackages == null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1ff117e..93dc219 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -873,9 +873,6 @@
"enable_permission_based_access";
private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
- // TODO(b/265683382) remove the flag after rollout.
- public static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
-
// TODO(b/266831522) remove the flag after rollout.
private static final String APPLICATION_EXEMPTIONS_FLAG = "application_exemptions";
private static final boolean DEFAULT_APPLICATION_EXEMPTIONS_FLAG = true;
@@ -2178,13 +2175,29 @@
return packageNameAndSignature;
}
- private void suspendAppsForQuietProfiles(boolean toSuspend) {
+ private void unsuspendAppsForQuietProfiles() {
PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
List<UserInfo> users = mUserManagerInternal.getUsers(true /* excludeDying */);
+
for (UserInfo user : users) {
- if (user.isManagedProfile() && user.isQuietModeEnabled()) {
- pmi.setPackagesSuspendedForQuietMode(user.id, toSuspend);
+ if (!user.isManagedProfile() || !user.isQuietModeEnabled()) {
+ continue;
}
+ int userId = user.id;
+ var suspendedByAdmin = getPackagesSuspendedByAdmin(userId);
+ var packagesToUnsuspend = mInjector.getPackageManager(userId)
+ .getInstalledPackages(PackageManager.PackageInfoFlags.of(
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE))
+ .stream()
+ .map(packageInfo -> packageInfo.packageName)
+ .filter(pkg -> !suspendedByAdmin.contains(pkg))
+ .toArray(String[]::new);
+
+ Slogf.i(LOG_TAG, "Unsuspending work apps for user %d", userId);
+ // When app suspension was used for quiet mode, the apps were suspended by platform
+ // package, just like when admin suspends them. So although it wasn't admin who
+ // suspended, this method will remove the right suspension record.
+ pmi.setPackagesSuspendedByAdmin(userId, packagesToUnsuspend, false /* suspended */);
}
}
@@ -3436,9 +3449,9 @@
}
}
- // In case flag value has changed, we apply it during boot to avoid doing it concurrently
- // with user toggling quiet mode.
- setKeepProfileRunningEnabledUnchecked(isKeepProfilesRunningFlagEnabled());
+ // Check whether work apps were paused via suspension and unsuspend if necessary.
+ // TODO: move it into PolicyVersionUpgrader so that it is executed only once.
+ unsuspendWorkAppsIfNecessary();
}
// TODO(b/230841522) Make it static.
@@ -11039,9 +11052,6 @@
(size == 1 ? "" : "s"));
}
pw.println();
- pw.println("Keep profiles running: "
- + getUserData(UserHandle.USER_SYSTEM).mEffectiveKeepProfilesRunning);
- pw.println();
mPolicyCache.dump(pw);
pw.println();
@@ -15539,11 +15549,6 @@
}
@Override
- public Set<String> getPackagesSuspendedByAdmin(@UserIdInt int userId) {
- return DevicePolicyManagerService.this.getPackagesSuspendedByAdmin(userId);
- }
-
- @Override
public void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, int reason,
boolean isSafe) {
// TODO(b/178494483): use EventLog instead
@@ -15571,11 +15576,6 @@
}
}
- @Override
- public boolean isKeepProfilesRunningEnabled() {
- return getUserDataUnchecked(UserHandle.USER_SYSTEM).mEffectiveKeepProfilesRunning;
- }
-
private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
return getDefaultCrossProfilePackages().contains(packageName)
? AppOpsManager.MODE_ALLOWED
@@ -23028,32 +23028,22 @@
DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG);
}
- private static boolean isKeepProfilesRunningFlagEnabled() {
- return DEFAULT_KEEP_PROFILES_RUNNING_FLAG;
- }
-
private boolean isUnicornFlagEnabled() {
return false;
}
- private void setKeepProfileRunningEnabledUnchecked(boolean keepProfileRunning) {
+ private void unsuspendWorkAppsIfNecessary() {
synchronized (getLockObject()) {
DevicePolicyData policyData = getUserDataUnchecked(UserHandle.USER_SYSTEM);
- if (policyData.mEffectiveKeepProfilesRunning == keepProfileRunning) {
+ if (!policyData.mEffectiveKeepProfilesRunning) {
return;
}
- policyData.mEffectiveKeepProfilesRunning = keepProfileRunning;
+ policyData.mEffectiveKeepProfilesRunning = false;
saveSettingsLocked(UserHandle.USER_SYSTEM);
}
- suspendAppsForQuietProfiles(keepProfileRunning);
- }
- @Override
- public void setOverrideKeepProfilesRunning(boolean enabled) {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
- setKeepProfileRunningEnabledUnchecked(enabled);
- Slog.i(LOG_TAG, "Keep profiles running overridden to: " + enabled);
+ Slog.w(LOG_TAG, "Work apps may have been paused via suspension previously.");
+ unsuspendAppsForQuietProfiles();
}
public void setMtePolicy(int flags, String callerPackageName) {
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index a385fe3..2ea28a31 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -20,6 +20,5 @@
srcs: [":services.midi-sources"],
libs: [
"services.core",
- "aconfig_midi_flags_java_lib",
],
}
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
index f4d51f9..683cae1 100644
--- a/services/midi/OWNERS
+++ b/services/midi/OWNERS
@@ -1 +1,3 @@
philburk@google.com
+robertwu@google.com
+elaurent@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 2f47cc7..39aaab2 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,7 +16,7 @@
package com.android.server.midi;
-import static com.android.media.midi.flags.Flags.virtualUmp;
+import static android.media.midi.Flags.virtualUmp;
import android.Manifest;
import android.annotation.NonNull;
diff --git a/services/proguard.flags b/services/proguard.flags
index e11e613..261bb7c 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -47,6 +47,11 @@
-keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
-keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; }
-keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; }
+-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+-keep,allowaccessmodification class android.app.admin.flags.Flags { *; }
# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
-keep public class com.android.server.utils.Slogf { *; }
@@ -99,9 +104,6 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
<methods>;
}
--keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl {
- <methods>;
-}
-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
*** usbDeviceRemoved(...);
*** usbDeviceAdded(...);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 163d248..c7b1abf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -28,6 +28,8 @@
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.server.display.DisplayManagerService.ENABLE_ON_CONNECT;
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
import static com.google.common.truth.Truth.assertThat;
@@ -90,12 +92,14 @@
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -113,6 +117,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
@@ -130,6 +135,7 @@
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -141,6 +147,8 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.time.Duration;
import java.util.ArrayList;
@@ -322,6 +330,12 @@
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Mock DisplayManagerFlags mMockFlags;
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .build();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -2406,6 +2420,39 @@
}
@Test
+ public void testConnectExternalDisplay_withDisplayManagementAndSysprop_shouldEnableDisplay() {
+ Assume.assumeTrue(Build.IS_ENG || Build.IS_USERDEBUG);
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ doAnswer((Answer<Boolean>) invocationOnMock -> true)
+ .when(() -> SystemProperties.getBoolean(ENABLE_ON_CONNECT, false));
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+ localService.registerDisplayGroupListener(callback);
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
+
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ callback.waitForExpectedEvent();
+ callback.clear();
+
+ callback.expectsEvent(EVENT_DISPLAY_CONNECTED);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ callback.waitForExpectedEvent();
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ false);
+ assertThat(display.isEnabledLocked()).isTrue();
+ assertThat(callback.receivedEvents()).containsExactly(DISPLAY_GROUP_EVENT_ADDED,
+ EVENT_DISPLAY_CONNECTED, EVENT_DISPLAY_ADDED).inOrder();
+ }
+
+ @Test
public void testConnectInternalDisplay_withDisplayManagement_shouldConnectAndAddDisplay() {
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
diff --git a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
index cc97b8f..76a1c3c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/MasterClearReceiverTest.java
@@ -154,10 +154,11 @@
intent.putExtra(Intent.EXTRA_REASON, "Self destruct");
intent.putExtra(Intent.EXTRA_FORCE_FACTORY_RESET, true);
intent.putExtra(Intent.EXTRA_WIPE_ESIMS, true);
+ intent.putExtra("keep_memtag_mode", true);
mReceiver.onReceive(mContext, intent);
verifyRebootWipeUserData(/* shutdown= */ true, /* reason= */ "Self destruct",
- /* force= */ true, /* wipeEuicc= */ true);
+ /* force= */ true, /* wipeEuicc= */ true, /* keepMemtagMode= */ true);
verifyWipeExternalData();
}
@@ -211,7 +212,7 @@
mRebootWipeUserDataLatch.countDown();
return null;
}).when(() -> RecoverySystem
- .rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean()));
+ .rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean(), anyBoolean()));
}
private void expectWipeExternalData() {
@@ -244,11 +245,16 @@
private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
boolean wipeEuicc) throws Exception {
+ verifyRebootWipeUserData(shutdown, reason, force, wipeEuicc, /* keepMemtagMode= */ false);
+ }
+
+ private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
+ boolean wipeEuicc, boolean keepMemtagMode) throws Exception {
boolean called = mRebootWipeUserDataLatch.await(5, TimeUnit.SECONDS);
assertWithMessage("rebootWipeUserData not called in 5s").that(called).isTrue();
verify(()-> RecoverySystem.rebootWipeUserData(same(mContext), eq(shutdown), eq(reason),
- eq(force), eq(wipeEuicc)));
+ eq(force), eq(wipeEuicc), eq(keepMemtagMode)));
}
private void verifyNoRebootWipeUserData() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 3ee8050..032d026 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -86,6 +86,9 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.DeviceConfig;
import android.util.IntArray;
import android.util.Log;
@@ -96,6 +99,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.sdksandbox.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService.StickyBroadcast;
import com.android.server.am.ProcessList.IsolatedUidRange;
@@ -145,8 +149,11 @@
private static final String TEST_EXTRA_KEY1 = "com.android.server.am.TEST_EXTRA_KEY1";
private static final String TEST_EXTRA_VALUE1 = "com.android.server.am.TEST_EXTRA_VALUE1";
+ private static final String PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS =
+ "apply_sdk_sandbox_audit_restrictions";
private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
"apply_sdk_sandbox_next_restrictions";
+ private static final String APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS = ":isSdkSandboxAudit";
private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
private static final int TEST_UID = 11111;
private static final int USER_ID = 666;
@@ -183,6 +190,9 @@
public final ApplicationExitInfoTest.ServiceThreadRule
mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private Context mContext = getInstrumentation().getTargetContext();
@Mock private AppOpsService mAppOpsService;
@@ -338,6 +348,7 @@
mockitoSession.finishMocking();
}
}
+
@SuppressWarnings("GuardedBy")
@SmallTest
@Test
@@ -367,6 +378,77 @@
}
}
+ @SuppressWarnings("GuardedBy")
+ @SmallTest
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SELINUX_SDK_SANDBOX_AUDIT)
+ public void applySdkSandboxAuditRestrictions() throws Exception {
+ MockitoSession mockitoSession =
+ ExtendedMockito.mockitoSession().spyStatic(Process.class).startMocking();
+ try {
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS, "true")));
+ assertThat(sProcessListSettingsListener.applySdkSandboxRestrictionsAudit()).isTrue();
+ ExtendedMockito.doReturn(true).when(() -> Process.isSdkSandboxUid(anyInt()));
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = "com.android.sdksandbox";
+ info.seInfo = "default:targetSdkVersion=34:complete";
+ final ProcessRecord appRec =
+ new ProcessRecord(
+ mAms,
+ info,
+ TAG,
+ Process.FIRST_SDK_SANDBOX_UID,
+ /* sdkSandboxClientPackageName= */ "com.example.client",
+ /* definingUid= */ 0,
+ /* definingProcessName= */ "");
+ assertThat(mAms.mProcessList.updateSeInfo(appRec))
+ .contains(APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS);
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @SmallTest
+ @Test
+ public void applySdkSandboxNextAndAuditRestrictions() throws Exception {
+ MockitoSession mockitoSession =
+ ExtendedMockito.mockitoSession().spyStatic(Process.class).startMocking();
+ try {
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true")));
+ sProcessListSettingsListener.onPropertiesChanged(
+ new DeviceConfig.Properties(
+ DeviceConfig.NAMESPACE_ADSERVICES,
+ Map.of(PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS, "true")));
+ assertThat(sProcessListSettingsListener.applySdkSandboxRestrictionsNext()).isTrue();
+ assertThat(sProcessListSettingsListener.applySdkSandboxRestrictionsAudit()).isTrue();
+ ExtendedMockito.doReturn(true).when(() -> Process.isSdkSandboxUid(anyInt()));
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = "com.android.sdksandbox";
+ info.seInfo = "default:targetSdkVersion=34:complete";
+ final ProcessRecord appRec =
+ new ProcessRecord(
+ mAms,
+ info,
+ TAG,
+ Process.FIRST_SDK_SANDBOX_UID,
+ /* sdkSandboxClientPackageName= */ "com.example.client",
+ /* definingUid= */ 0,
+ /* definingProcessName= */ "");
+ assertThat(mAms.mProcessList.updateSeInfo(appRec))
+ .contains(APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
+ assertThat(mAms.mProcessList.updateSeInfo(appRec))
+ .doesNotContain(APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS);
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
private UidRecord addUidRecord(int uid) {
final UidRecord uidRec = new UidRecord(uid, mAms);
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index 4ffa0fb..5f9a17c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -88,7 +88,7 @@
Log.d(TAG, "Mocking " + inv);
return null;
}).when(() -> RecoverySystem.rebootWipeUserData(any(), anyBoolean(), any(),
- anyBoolean(), anyBoolean()));
+ anyBoolean(), anyBoolean(), anyBoolean()));
}
@After
@@ -270,17 +270,20 @@
private void verifyRebootWipeUserDataMinimumArgsCalled() {
verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
- /* reason= */ null, /* force= */ false, /* wipeEuicc= */ false));
+ /* reason= */ null, /* force= */ false, /* wipeEuicc= */ false,
+ /* keepMemtagMode= */ false));
}
private void verifyRebootWipeUserDataMinimumArgsButForceCalled() {
verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ false,
- /* reason= */ null, /* force= */ true, /* wipeEuicc= */ false));
+ /* reason= */ null, /* force= */ true, /* wipeEuicc= */ false,
+ /* keepMemtagMode= */ false));
}
private void verifyRebootWipeUserDataAllArgsCalled() {
verify(() -> RecoverySystem.rebootWipeUserData(mContext, /* shutdown= */ true,
- /* reason= */ REASON, /* force= */ true, /* wipeEuicc= */ true));
+ /* reason= */ REASON, /* force= */ true, /* wipeEuicc= */ true,
+ /* keepMemtagMode= */ false));
}
private void verifyWipeAdoptableStorageNotCalled() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 809a0e8..64e86f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -983,9 +983,8 @@
final JobStatus blue = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
- // Unmetered preference is disabled for now.
assertFalse(red.getPreferUnmetered());
- assertFalse(blue.getPreferUnmetered());
+ assertTrue(blue.getPreferUnmetered());
controller.maybeStartTrackingJobLocked(red, null);
controller.maybeStartTrackingJobLocked(blue, null);
@@ -1039,7 +1038,7 @@
generalCallback.onLost(meteredNet);
assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
- assertTrue(red.getHasAccessToUnmetered());
+ assertFalse(red.getHasAccessToUnmetered());
assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
assertTrue(blue.getHasAccessToUnmetered());
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 7ae6a2d..bb9dcf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -189,10 +189,7 @@
}
private static JobInfo.Builder createJob(int id) {
- return new JobInfo.Builder(id, new ComponentName("foo", "bar"))
- .setPrefersBatteryNotLow(true)
- .setPrefersCharging(true)
- .setPrefersDeviceIdle(true);
+ return new JobInfo.Builder(id, new ComponentName("foo", "bar"));
}
private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
@@ -536,15 +533,12 @@
jb = createJob(i);
if (i > 0) {
jb.setRequiresDeviceIdle(true);
- jb.setPrefersDeviceIdle(false);
}
if (i > 1) {
jb.setRequiresBatteryNotLow(true);
- jb.setPrefersBatteryNotLow(false);
}
if (i > 2) {
jb.setRequiresCharging(true);
- jb.setPrefersCharging(false);
}
jobs[i] = createJobStatus("", jb);
flexTracker.add(jobs[i]);
@@ -553,55 +547,53 @@
synchronized (mFlexibilityController.mLock) {
ArrayList<ArraySet<JobStatus>> trackedJobs = flexTracker.getArrayList();
assertEquals(1, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(3, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
+
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
+ assertEquals(1, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
- assertEquals(1, trackedJobs.get(3).size());
+ assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
- assertEquals(2, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
- assertEquals(0, trackedJobs.get(4).size());
-
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
- assertEquals(1, trackedJobs.get(0).size());
- assertEquals(2, trackedJobs.get(1).size());
- assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(2, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
- assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
flexTracker.remove(jobs[1]);
assertEquals(2, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(1, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(2).size());
+ assertEquals(2, trackedJobs.get(3).size());
+ assertEquals(0, trackedJobs.get(4).size());
+
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
+ assertEquals(1, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(0, trackedJobs.get(2).size());
assertEquals(1, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
- assertEquals(1, trackedJobs.get(0).size());
- assertEquals(2, trackedJobs.get(1).size());
- assertEquals(0, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
- assertEquals(0, trackedJobs.get(4).size());
-
- // Over halfway through the flex window. The job that prefers all flex constraints
- // should have its first flex constraint dropped.
final long nowElapsed = ((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
+ HOUR_IN_MILLIS);
JobSchedulerService.sElapsedRealtimeClock =
@@ -609,9 +601,9 @@
flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
assertEquals(1, trackedJobs.get(0).size());
- assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
- assertEquals(0, trackedJobs.get(3).size());
+ assertEquals(1, trackedJobs.get(3).size());
assertEquals(0, trackedJobs.get(4).size());
}
}
@@ -626,13 +618,8 @@
@Test
public void testExceptions_UserInitiated() {
- JobInfo.Builder jb = createJob(0)
- .setUserInitiated(true)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
- // Attempt to add flex constraints to the job. For now, we will ignore them.
- .setPrefersBatteryNotLow(true)
- .setPrefersCharging(true)
- .setPrefersDeviceIdle(false);
+ JobInfo.Builder jb = createJob(0);
+ jb.setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
JobStatus js = createJobStatus("testExceptions_UserInitiated", jb);
assertFalse(js.hasFlexibilityConstraint());
}
@@ -648,10 +635,10 @@
@Test
public void testExceptions_NoFlexibleConstraints() {
- JobInfo.Builder jb = createJob(0)
- .setPrefersBatteryNotLow(false)
- .setPrefersCharging(false)
- .setPrefersDeviceIdle(false);
+ JobInfo.Builder jb = createJob(0);
+ jb.setRequiresDeviceIdle(true);
+ jb.setRequiresCharging(true);
+ jb.setRequiresBatteryNotLow(true);
JobStatus js = createJobStatus("testExceptions_NoFlexibleConstraints", jb);
assertFalse(js.hasFlexibilityConstraint());
}
@@ -710,50 +697,15 @@
JobStatus js = createJobStatus("testTopAppBypass", jb);
synchronized (mFlexibilityController.mLock) {
js.setHasAccessToUnmetered(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
+ assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
js.setHasAccessToUnmetered(true);
- assertEquals(1, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
+ assertEquals(1, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
js.setHasAccessToUnmetered(false);
- assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
+ assertEquals(0, mFlexibilityController.getNumSatisfiedRequiredConstraintsLocked(js));
}
}
@Test
- public void testGetNumSatisfiedFlexibleConstraints() {
- long nowElapsed = FROZEN_TIME;
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, true, nowElapsed);
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, true, nowElapsed);
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, true, nowElapsed);
- JobInfo.Builder jb = createJob(0)
- .setPrefersBatteryNotLow(false)
- .setPrefersCharging(false)
- .setPrefersDeviceIdle(false);
- JobStatus js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
- assertEquals(0, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
-
- jb = createJob(0)
- .setPrefersBatteryNotLow(true)
- .setPrefersCharging(false)
- .setPrefersDeviceIdle(false);
- js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
- assertEquals(1, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
-
- jb = createJob(0)
- .setPrefersBatteryNotLow(true)
- .setPrefersCharging(false)
- .setPrefersDeviceIdle(true);
- js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
- assertEquals(2, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
-
- jb = createJob(0)
- .setPrefersBatteryNotLow(true)
- .setPrefersCharging(true)
- .setPrefersDeviceIdle(true);
- js = createJobStatus("testGetNumSatisfiedFlexibleConstraints", jb);
- assertEquals(3, mFlexibilityController.getNumSatisfiedFlexibleConstraintsLocked(js));
- }
-
- @Test
public void testSetConstraintSatisfied_Constraints() {
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME);
assertFalse(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE));
@@ -784,11 +736,8 @@
jb = createJob(i);
constraints = constraintCombinations[i];
jb.setRequiresDeviceIdle((constraints & CONSTRAINT_IDLE) != 0);
- jb.setPrefersDeviceIdle((constraints & CONSTRAINT_IDLE) == 0);
jb.setRequiresBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0);
- jb.setPrefersBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) == 0);
jb.setRequiresCharging((constraints & CONSTRAINT_CHARGING) != 0);
- jb.setPrefersCharging((constraints & CONSTRAINT_CHARGING) == 0);
synchronized (mFlexibilityController.mLock) {
mFlexibilityController.maybeStartTrackingJobLocked(
createJobStatus(String.valueOf(i), jb), null);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1de7e37..92aa982 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -1240,9 +1240,7 @@
@Test
public void testReadinessStatusWithConstraint_FlexibilityConstraint() {
final JobStatus job = createJobStatus(
- new JobInfo.Builder(101, new ComponentName("foo", "bar"))
- .setPrefersCharging(true)
- .build());
+ new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), false);
markImplicitConstraintsSatisfied(job, true);
assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java
new file mode 100644
index 0000000..09935f2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/idle/DeviceIdlenessTrackerTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 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.job.controllers.idle;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.controllers.idle.DeviceIdlenessTracker.KEY_INACTIVITY_IDLE_THRESHOLD_MS;
+import static com.android.server.job.controllers.idle.DeviceIdlenessTracker.KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.AppSchedulingModuleThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdlenessTrackerTest {
+ private DeviceIdlenessTracker mDeviceIdlenessTracker;
+ private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
+ private BroadcastReceiver mBroadcastReceiver;
+ private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder =
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);;
+
+ private MockitoSession mMockingSession;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private Resources mResources;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DeviceConfig.class)
+ .mockStatic(LocalServices.class)
+ .startMocking();
+
+ // Called in StateController constructor.
+ when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
+ when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
+ when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
+ // Called in DeviceIdlenessTracker.startTracking.
+ when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
+ when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class));
+ when(mContext.getResources()).thenReturn(mResources);
+ doReturn((int) (31 * MINUTE_IN_MILLIS)).when(mResources).getInteger(
+ com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+ doReturn((int) (17 * MINUTE_IN_MILLIS)).when(mResources).getInteger(
+ com.android.internal.R.integer
+ .config_jobSchedulerInactivityIdleThresholdOnStablePower);
+ doReturn(mPowerManager).when(() -> LocalServices.getService(PowerManager.class));
+
+ // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
+ // in the past, and QuotaController sometimes floors values at 0, so if the test time
+ // causes sessions with negative timestamps, they will fail.
+ JobSchedulerService.sSystemClock =
+ getAdvancedClock(Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC),
+ 24 * HOUR_IN_MILLIS);
+ JobSchedulerService.sUptimeMillisClock = getAdvancedClock(
+ Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC),
+ 24 * HOUR_IN_MILLIS);
+ JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC),
+ 24 * HOUR_IN_MILLIS);
+
+ // Initialize real objects.
+ // Capture the listeners.
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ mDeviceIdlenessTracker = new DeviceIdlenessTracker();
+ mDeviceIdlenessTracker.startTracking(mContext,
+ mJobSchedulerService, mock(IdlenessListener.class));
+
+ verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ mBroadcastReceiver = broadcastReceiverCaptor.getValue();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private Clock getAdvancedClock(Clock clock, long incrementMs) {
+ return Clock.offset(clock, Duration.ofMillis(incrementMs));
+ }
+
+ private void advanceElapsedClock(long incrementMs) {
+ JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock(
+ JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+ }
+
+ private void setBatteryState(boolean isCharging, boolean isBatteryNotLow) {
+ doReturn(isCharging).when(mJobSchedulerService).isBatteryCharging();
+ doReturn(isBatteryNotLow).when(mJobSchedulerService).isBatteryNotLow();
+ mDeviceIdlenessTracker.onBatteryStateChanged(isCharging, isBatteryNotLow);
+ }
+
+ private void setDeviceConfigLong(String key, long val) {
+ mDeviceConfigPropertiesBuilder.setLong(key, val);
+ mDeviceIdlenessTracker.processConstant(mDeviceConfigPropertiesBuilder.build(), key);
+ }
+
+ @Test
+ public void testThresholdChangeWithStablePowerChange() {
+ setDeviceConfigLong(KEY_INACTIVITY_IDLE_THRESHOLD_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(KEY_INACTIVITY_STABLE_POWER_IDLE_THRESHOLD_MS, 5 * MINUTE_IN_MILLIS);
+ setBatteryState(false, false);
+
+ Intent screenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
+ mBroadcastReceiver.onReceive(mContext, screenOffIntent);
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ long expectedUnstableAlarmElapsed = nowElapsed + 10 * MINUTE_IN_MILLIS;
+ long expectedStableAlarmElapsed = nowElapsed + 5 * MINUTE_IN_MILLIS;
+
+ InOrder inOrder = inOrder(mAlarmManager);
+ inOrder.verify(mAlarmManager)
+ .setWindow(anyInt(), eq(expectedUnstableAlarmElapsed), anyLong(), anyString(),
+ eq(AppSchedulingModuleThread.getExecutor()), any());
+
+ // Advanced the clock a little to make sure the tracker continues to use the original time.
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ // Charging isn't enough for stable power.
+ setBatteryState(true, false);
+ inOrder.verify(mAlarmManager, never())
+ .setWindow(anyInt(), anyLong(), anyLong(), anyString(),
+ eq(AppSchedulingModuleThread.getExecutor()), any());
+
+ // Now on stable power.
+ setBatteryState(true, true);
+ inOrder.verify(mAlarmManager)
+ .setWindow(anyInt(), eq(expectedStableAlarmElapsed), anyLong(), anyString(),
+ eq(AppSchedulingModuleThread.getExecutor()), any());
+
+ // Battery-not-low isn't enough for stable power. Go back to unstable timing.
+ setBatteryState(false, true);
+ inOrder.verify(mAlarmManager)
+ .setWindow(anyInt(), eq(expectedUnstableAlarmElapsed), anyLong(), anyString(),
+ eq(AppSchedulingModuleThread.getExecutor()), any());
+
+ // Still not on stable power.
+ setBatteryState(false, false);
+ inOrder.verify(mAlarmManager, never())
+ .setWindow(anyInt(), anyLong(), anyLong(), anyString(),
+ eq(AppSchedulingModuleThread.getExecutor()), any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 5c8a19c..1e65c89 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
@@ -103,6 +105,8 @@
@Mock
private ActivityManager mActivityManager;
@Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
private PackageManager mPackageManager;
@Mock
private PackageInstallerService mInstallerService;
@@ -160,12 +164,17 @@
when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState);
when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
+ when(mContext.getSystemService(AppOpsManager.class)).thenReturn(
+ mAppOpsManager);
when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
mLauncherActivityInfos);
when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
when(mActivityManager.getLauncherLargeIconDensity()).thenReturn(100);
+ when(mAppOpsManager.checkOp(
+ eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED),
+ anyInt(), eq(PACKAGE))).thenReturn(MODE_ALLOWED);
doReturn(mComputer).when(mPackageManagerService).snapshotComputer();
when(mComputer.getPackageUid(eq(CALLER_PACKAGE), eq(0L), eq(mUserId))).thenReturn(
Binder.getCallingUid());
@@ -305,6 +314,21 @@
}
@Test
+ public void archiveApp_appOptedOutOfArchiving() {
+ when(mAppOpsManager.checkOp(
+ eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED),
+ anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED);
+
+ Exception e = assertThrows(
+ ParcelableException.class,
+ () -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ TextUtils.formatSimple("The app %s is opted out of archiving.", PACKAGE));
+ }
+
+ @Test
public void archiveApp_success() {
mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
@@ -319,6 +343,39 @@
}
@Test
+ public void isAppArchivable_success() throws PackageManager.NameNotFoundException {
+ assertThat(mArchiveManager.isAppArchivable(PACKAGE, UserHandle.CURRENT)).isTrue();
+ }
+
+ @Test
+ public void isAppArchivable_installerDoesntSupportUnarchival()
+ throws PackageManager.NameNotFoundException {
+ doReturn(new ParceledListSlice<>(List.of()))
+ .when(mPackageManagerService).queryIntentReceivers(any(), any(), any(), anyLong(),
+ eq(mUserId));
+
+ assertThat(mArchiveManager.isAppArchivable(PACKAGE, UserHandle.CURRENT)).isFalse();
+ }
+
+ @Test
+ public void isAppArchivable_noMainActivities() throws PackageManager.NameNotFoundException {
+ when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
+ List.of());
+
+ assertThat(mArchiveManager.isAppArchivable(PACKAGE, UserHandle.CURRENT)).isFalse();
+ }
+
+ @Test
+ public void isAppArchivable_appOptedOutOfArchiving()
+ throws PackageManager.NameNotFoundException {
+ when(mAppOpsManager.checkOp(
+ eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED),
+ anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED);
+
+ assertThat(mArchiveManager.isAppArchivable(PACKAGE, UserHandle.CURRENT)).isFalse();
+ }
+
+ @Test
public void unarchiveApp_callerPackageNameIncorrect() {
mUserState.setArchiveState(createArchiveState()).setInstalled(false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index eb00164..a6ba5d4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -87,8 +87,8 @@
TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE,
DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE,
VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE))
- suspendPackageHelper = SuspendPackageHelper(pms, rule.mocks().injector,
- rule.mocks().userManagerService, broadcastHelper, protectedPackages)
+ suspendPackageHelper = SuspendPackageHelper(
+ pms, rule.mocks().injector, broadcastHelper, protectedPackages)
defaultAppProvider = rule.mocks().defaultAppProvider
testHandler = rule.mocks().handler
packageSetting1 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_1)!!
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 4240373..7b381ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -39,7 +39,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -56,14 +56,14 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
null /* packageNames */, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).isNull()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOfNulls(0), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).isEmpty()
}
@@ -73,7 +73,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID,
- Binder.getCallingUid(), false /* forQuietMode */, false /* quarantined */)
+ Binder.getCallingUid(), false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
@@ -84,7 +84,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(DEVICE_OWNER_PACKAGE), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
@@ -95,7 +95,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(NONEXISTENT_PACKAGE), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
assertThat(failedNames).asList().hasSize(1)
assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
@@ -108,7 +108,7 @@
val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
knownPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)!!
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)!!
assertThat(failedNames.size).isEqualTo(knownPackages.size)
for (pkg in knownPackages) {
@@ -117,33 +117,19 @@
}
@Test
- fun setPackagesSuspended_forQuietMode() {
- val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
- INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE,
- PERMISSION_CONTROLLER_PACKAGE, MGMT_ROLE_HOLDER_PACKAGE)
- val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
- knownPackages, true /* suspended */, null /* appExtras */,
- null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */, false /* quarantined */)!!
-
- assertThat(failedNames.size).isEqualTo(1)
- assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE)
- }
-
- @Test
fun setPackagesUnsuspended() {
val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, false /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
@@ -191,7 +177,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, appExtras, null /* launcherExtras */,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */, false /* quarantined */)
+ false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -209,7 +195,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, true /* suspended */, appExtras, null /* launcherExtras */,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */, false /* quarantined */)
+ false /* quarantined */)
testHandler.flush()
Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
@@ -250,7 +236,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */, false /* quarantined */)
+ false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -265,7 +251,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
- TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ TEST_USER_ID, deviceOwnerUid, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -280,7 +266,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_2), true /* suspended */, null /* appExtras */, launcherExtras,
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
- false /* forQuietMode */, false /* quarantined */)
+ false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
@@ -295,7 +281,7 @@
var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
arrayOf(TEST_PACKAGE_1), true /* suspended */, null /* appExtras */,
null /* launcherExtras */, dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID,
- deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
+ deviceOwnerUid, false /* quarantined */)
testHandler.flush()
assertThat(failedNames).isEmpty()
diff --git a/services/tests/servicestests/res/xml/user_100_v9.xml b/services/tests/servicestests/res/xml/user_100_v9.xml
new file mode 100644
index 0000000..03c08ed
--- /dev/null
+++ b/services/tests/servicestests/res/xml/user_100_v9.xml
@@ -0,0 +1,20 @@
+<user id="100"
+ serialNumber="0"
+ flags="3091"
+ type="android.os.usertype.full.SYSTEM"
+ created="0"
+ lastLoggedIn="0"
+ lastLoggedInFingerprint="0"
+ profileBadge="0">
+ <restrictions no_oem_unlock="true" />
+ <device_policy_local_restrictions>
+ <restrictions_user user_id="0">
+ <restrictions no_camera="true" />
+ </restrictions_user>
+ <restrictions_user user_id="100">
+ <restrictions no_camera="true" />
+ <restrictions no_install_unknown_sources="true" />
+ </restrictions_user>
+ </device_policy_local_restrictions>
+ <ignorePrepareStorageErrors>false</ignorePrepareStorageErrors>
+</user>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b9e45ba..82efdd3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -535,6 +535,78 @@
@SmallTest
@Test
+ public void testOnClientChange_magnificationTripleTapEnabled_requestConnection() {
+ when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr).requestConnection(true);
+ }
+
+ @SmallTest
+ @Test
+ public void testOnClientChange_magnificationTripleTapDisabled_requestDisconnection() {
+ when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr).requestConnection(false);
+ }
+
+ @SmallTest
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testOnClientChange_magnificationTwoFingerTripleTapEnabled_requestConnection() {
+ when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationTwoFingerTripleTapEnabledLocked(true);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr).requestConnection(true);
+ }
+
+ @SmallTest
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testOnClientChange_magnificationTwoFingerTripleTapDisabled_requestDisconnection() {
+ when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
+
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
+ userState.setMagnificationTwoFingerTripleTapEnabledLocked(false);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr).requestConnection(false);
+ }
+
+ @SmallTest
+ @Test
public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() {
when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false);
@@ -547,6 +619,64 @@
verify(mMockWindowMagnificationMgr).requestConnection(true);
}
+ @SmallTest
+ @Test
+ public void testOnClientChange_magnificationTripleTapDisabled_removeMagnificationButton() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ }
+
+ @SmallTest
+ @Test
+ public void testOnClientChange_magnificationTripleTapEnabled_keepMagnificationButton() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ }
+
+ @SmallTest
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void onClientChange_magnificationTwoFingerTripleTapDisabled_removeMagnificationButton() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ userState.setMagnificationTwoFingerTripleTapEnabledLocked(false);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt());
+ }
+
+ @SmallTest
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void onClientChange_magnificationTwoFingerTripleTapEnabled_keepMagnificationButton() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationTwoFingerTripleTapEnabledLocked(true);
+
+ // Invokes client change to trigger onUserStateChanged.
+ mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
+
+ verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt());
+ }
+
@Test
public void testUnbindIme_whenServiceUnbinds() {
setupAccessibilityServiceConnection(AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index f88afe7..a78f2dc 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -41,6 +41,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -53,6 +55,7 @@
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -84,6 +87,8 @@
@Rule
public MockitoRule mockitorule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private Context mContext;
@Mock
@@ -418,6 +423,37 @@
eq(callback));
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flaggedOff_throwsUnsupportedOperationException()
+ throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ mAuthService.mImpl.getLastAuthenticationTime(0,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flaggedOn_callsBiometricService()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final int userId = 0;
+ final int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG;
+
+ mAuthService.mImpl.getLastAuthenticationTime(userId, authenticators);
+
+ waitForIdle();
+ verify(mBiometricService).getLastAuthenticationTime(eq(userId), eq(authenticators));
+ }
+
private static void setInternalAndTestBiometricPermissions(
Context context, boolean hasPermission) {
for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0230d77..408442b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -62,6 +62,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -71,12 +72,17 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.keymaster.HardwareAuthenticatorType;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.service.gatekeeper.IGateKeeperService;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
@@ -92,6 +98,7 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
@@ -105,6 +112,9 @@
@SmallTest
public class BiometricServiceTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String TEST_PACKAGE_NAME = "test_package";
private static final long TEST_REQUEST_ID = 44;
@@ -162,10 +172,16 @@
@Mock
private BiometricCameraManager mBiometricCameraManager;
+ @Mock
+ private IKeystoreAuthorization mKeystoreAuthService;
+
+ @Mock
+ private IGateKeeperService mGateKeeperService;
+
BiometricContextProvider mBiometricContextProvider;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
resetReceivers();
@@ -215,6 +231,9 @@
mStatusBarService, null /* handler */,
mAuthSessionCoordinator);
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
+ when(mInjector.getKeystoreAuthorizationService()).thenReturn(mKeystoreAuthService);
+ when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
+ when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
@@ -1751,6 +1770,44 @@
verifyNoMoreInteractions(callback);
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flagOff_throwsUnsupportedOperationException()
+ throws RemoteException {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flagOn_callsKeystoreAuthorization()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ final int[] hardwareAuthenticators = new int[] {
+ HardwareAuthenticatorType.PASSWORD,
+ HardwareAuthenticatorType.FINGERPRINT
+ };
+
+ final int userId = 0;
+ final long secureUserId = mGateKeeperService.getSecureUserId(userId);
+
+ assertNotEquals(GateKeeper.INVALID_SECURE_USER_ID, secureUserId);
+
+ final long expectedResult = 31337L;
+
+ when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators)))
+ .thenReturn(expectedResult);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+
+ final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId,
+ Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
+
+ assertEquals(expectedResult, result);
+ verify(mKeystoreAuthService).getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators));
+ }
+
// Helper methods
private int invokeCanAuthenticate(BiometricService service, int authenticators)
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 461d637..2598a6b 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
@@ -347,6 +347,8 @@
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.uniqueId = UNIQUE_ID;
doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ doReturn(Display.INVALID_DISPLAY).when(mDisplayManagerInternalMock)
+ .getDisplayIdToMirror(anyInt());
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
@@ -1627,6 +1629,7 @@
@Test
public void openNonBlockedAppOnMirrorDisplay_flagDisabled_launchesActivity() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt()))
.thenReturn(Display.DEFAULT_DISPLAY);
addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
index af633cc..dbd6c88 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceRule.java
@@ -37,6 +37,7 @@
import android.os.Binder;
import android.testing.TestableContext;
import android.util.ArraySet;
+import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
@@ -137,6 +138,8 @@
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.uniqueId = "uniqueId";
doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
+ doReturn(Display.INVALID_DISPLAY).when(mDisplayManagerInternalMock)
+ .getDisplayIdToMirror(anyInt());
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
index dc38f2b..a28647e 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionAllowlistManagerTest.java
@@ -33,6 +33,7 @@
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.contentcapture.IContentProtectionAllowlistCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -48,6 +49,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.List;
+
/**
* Test for {@link ContentProtectionAllowlistManager}.
*
@@ -58,7 +61,9 @@
@SmallTest
public class ContentProtectionAllowlistManagerTest {
- private static final String PACKAGE_NAME = "com.test.package.name";
+ private static final String FIRST_PACKAGE_NAME = "com.test.first.package.name";
+
+ private static final String SECOND_PACKAGE_NAME = "com.test.second.package.name";
private static final long TIMEOUT_MS = 111_111_111L;
@@ -74,12 +79,18 @@
@Mock private RemoteContentProtectionService mMockRemoteContentProtectionService;
+ @Mock private IContentProtectionAllowlistCallback mMockAllowlistCallback;
+
private final TestLooper mTestLooper = new TestLooper();
private Handler mHandler;
private ContentProtectionAllowlistManager mContentProtectionAllowlistManager;
+ private boolean mUseMockPackageMonitor = true;
+
+ private boolean mUseMockAllowlistCallback = true;
+
@Before
public void setup() {
mHandler = new Handler(mTestLooper.getLooper());
@@ -91,6 +102,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verifyZeroInteractions(mMockContentCaptureManagerService);
verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -103,6 +116,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
verifyZeroInteractions(mMockContentCaptureManagerService);
verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -117,6 +132,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -132,6 +149,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -144,6 +163,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isTrue();
verifyZeroInteractions(mMockContentCaptureManagerService);
verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -158,6 +179,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -173,6 +196,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor, never()).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -186,6 +211,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -201,6 +228,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -216,6 +245,8 @@
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -229,6 +260,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -244,6 +277,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor, never()).register(any(), any(), any());
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -259,6 +294,8 @@
verifyZeroInteractions(mMockContentCaptureManagerService);
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -274,6 +311,8 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
@@ -290,60 +329,130 @@
assertThat(mHandler.hasMessagesOrCallbacks()).isFalse();
verify(mMockPackageMonitor, times(2)).register(any(), eq(UserHandle.ALL), eq(mHandler));
verify(mMockPackageMonitor).unregister();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void isAllowed() {
- boolean actual = mContentProtectionAllowlistManager.isAllowed(PACKAGE_NAME);
+ public void isAllowed_default() {
+ boolean actual = mContentProtectionAllowlistManager.isAllowed(FIRST_PACKAGE_NAME);
assertThat(actual).isFalse();
verifyZeroInteractions(mMockContentCaptureManagerService);
verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void handleUpdate_updateDisabled() {
+ public void isAllowed_false() throws Exception {
+ mUseMockAllowlistCallback = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
+ manager.mAllowlistCallback.setAllowlist(List.of(FIRST_PACKAGE_NAME));
+ mTestLooper.dispatchNext();
+
+ boolean actual = manager.isAllowed(SECOND_PACKAGE_NAME);
+
+ assertThat(actual).isFalse();
+ verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ }
+
+ @Test
+ public void isAllowed_true() throws Exception {
+ mUseMockAllowlistCallback = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
+ manager.mAllowlistCallback.setAllowlist(List.of(FIRST_PACKAGE_NAME));
+ mTestLooper.dispatchNext();
+
+ boolean actual = manager.isAllowed(FIRST_PACKAGE_NAME);
+
+ assertThat(actual).isTrue();
+ verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyZeroInteractions(mMockPackageMonitor);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ }
+
+ @Test
+ public void handlePackagesChanged_updateDisabled() {
mSetFlagsRule.disableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
- ContentProtectionAllowlistManager manager =
- new ContentProtectionAllowlistManager(
- mMockContentCaptureManagerService, mHandler, TIMEOUT_MS);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
manager.mPackageMonitor.onSomePackagesChanged();
verifyZeroInteractions(mMockContentCaptureManagerService);
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void handleUpdate_updateEnabled() {
+ public void handlePackagesChanged_updateEnabled_noService() {
mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
- ContentProtectionAllowlistManager manager =
- new ContentProtectionAllowlistManager(
- mMockContentCaptureManagerService, mHandler, TIMEOUT_MS);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void handleUpdate_rateLimit_noService() {
+ public void handlePackagesChanged_updateEnabled_withService() {
mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
- ContentProtectionAllowlistManager manager =
- new ContentProtectionAllowlistManager(
- mMockContentCaptureManagerService, mHandler, TIMEOUT_MS);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
+ when(mMockContentCaptureManagerService.createRemoteContentProtectionService())
+ .thenReturn(mMockRemoteContentProtectionService);
+
+ manager.mPackageMonitor.onSomePackagesChanged();
+
+ verify(mMockRemoteContentProtectionService)
+ .onUpdateAllowlistRequest(mMockAllowlistCallback);
+ verifyZeroInteractions(mMockAllowlistCallback);
+ }
+
+ @Test
+ public void handlePackagesChanged_updateEnabled_withServiceException() {
+ mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
+ when(mMockContentCaptureManagerService.createRemoteContentProtectionService())
+ .thenReturn(mMockRemoteContentProtectionService);
+ doThrow(new RuntimeException("TEST EXCEPTION"))
+ .when(mMockRemoteContentProtectionService)
+ .onUpdateAllowlistRequest(mMockAllowlistCallback);
+
+ manager.mPackageMonitor.onSomePackagesChanged();
+
+ // Does not rethrow
+ verify(mMockRemoteContentProtectionService)
+ .onUpdateAllowlistRequest(mMockAllowlistCallback);
+ verifyZeroInteractions(mMockAllowlistCallback);
+ }
+
+ @Test
+ public void handlePackagesChanged_rateLimit_noService() {
+ mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
manager.mPackageMonitor.onSomePackagesChanged();
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
+ verifyZeroInteractions(mMockRemoteContentProtectionService);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void handleUpdate_rateLimit_beforeTimeout() {
+ public void handlePackagesChanged_rateLimit_beforeTimeout() {
mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
- ContentProtectionAllowlistManager manager =
- new ContentProtectionAllowlistManager(
- mMockContentCaptureManagerService, mHandler, TIMEOUT_MS);
+ mUseMockPackageMonitor = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
when(mMockContentCaptureManagerService.createRemoteContentProtectionService())
.thenReturn(mMockRemoteContentProtectionService);
@@ -351,14 +460,17 @@
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService).createRemoteContentProtectionService();
+ verify(mMockRemoteContentProtectionService)
+ .onUpdateAllowlistRequest(mMockAllowlistCallback);
+ verifyZeroInteractions(mMockAllowlistCallback);
}
@Test
- public void handleUpdate_rateLimit_afterTimeout() {
+ public void handlePackagesChanged_rateLimit_afterTimeout() {
mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
+ mUseMockPackageMonitor = false;
ContentProtectionAllowlistManager manager =
- new ContentProtectionAllowlistManager(
- mMockContentCaptureManagerService, mHandler, /* timeoutMs= */ 0L);
+ new TestContentProtectionAllowlistManager(/* timeoutMs= */ 0L);
when(mMockContentCaptureManagerService.createRemoteContentProtectionService())
.thenReturn(mMockRemoteContentProtectionService);
@@ -366,17 +478,50 @@
manager.mPackageMonitor.onSomePackagesChanged();
verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
+ verify(mMockRemoteContentProtectionService, times(2))
+ .onUpdateAllowlistRequest(mMockAllowlistCallback);
+ verifyZeroInteractions(mMockAllowlistCallback);
+ }
+
+ @Test
+ public void handlePackagesChanged_rateLimit_afterUpdate() throws Exception {
+ mSetFlagsRule.enableFlags(FLAG_BLOCKLIST_UPDATE_ENABLED);
+ mUseMockPackageMonitor = false;
+ mUseMockAllowlistCallback = false;
+ ContentProtectionAllowlistManager manager = new TestContentProtectionAllowlistManager();
+ when(mMockContentCaptureManagerService.createRemoteContentProtectionService())
+ .thenReturn(mMockRemoteContentProtectionService);
+
+ manager.mPackageMonitor.onSomePackagesChanged();
+ manager.mAllowlistCallback.setAllowlist(List.of());
+ mTestLooper.dispatchNext();
+ manager.mPackageMonitor.onSomePackagesChanged();
+
+ verify(mMockContentCaptureManagerService, times(2)).createRemoteContentProtectionService();
+ verify(mMockRemoteContentProtectionService, times(2))
+ .onUpdateAllowlistRequest(manager.mAllowlistCallback);
}
private class TestContentProtectionAllowlistManager extends ContentProtectionAllowlistManager {
TestContentProtectionAllowlistManager() {
- super(mMockContentCaptureManagerService, mHandler, TIMEOUT_MS);
+ this(TIMEOUT_MS);
+ }
+
+ TestContentProtectionAllowlistManager(long timeoutMs) {
+ super(mMockContentCaptureManagerService, mHandler, timeoutMs);
+ }
+
+ @Override
+ protected IContentProtectionAllowlistCallback createAllowlistCallback() {
+ return mUseMockAllowlistCallback
+ ? mMockAllowlistCallback
+ : super.createAllowlistCallback();
}
@Override
protected PackageMonitor createPackageMonitor() {
- return mMockPackageMonitor;
+ return mUseMockPackageMonitor ? mMockPackageMonitor : super.createPackageMonitor();
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 4bb7d63..2db46e6 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -784,66 +784,6 @@
}
@Test
- public void testPersistedPreferredBatteryNotLowConstraint() throws Exception {
- JobInfo.Builder b = new Builder(8, mComponent)
- .setPrefersBatteryNotLow(true)
- .setPersisted(true);
- JobStatus taskStatus =
- JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
-
- mTaskStoreUnderTest.add(taskStatus);
- waitForPendingIo();
-
- final JobSet jobStatusSet = new JobSet();
- mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
- assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
- JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
- assertEquals("Battery-not-low constraint not persisted correctly.",
- taskStatus.getJob().isPreferBatteryNotLow(),
- loaded.getJob().isPreferBatteryNotLow());
- }
-
- @Test
- public void testPersistedPreferredChargingConstraint() throws Exception {
- JobInfo.Builder b = new Builder(8, mComponent)
- .setPrefersCharging(true)
- .setPersisted(true);
- JobStatus taskStatus =
- JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
-
- mTaskStoreUnderTest.add(taskStatus);
- waitForPendingIo();
-
- final JobSet jobStatusSet = new JobSet();
- mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
- assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
- JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
- assertEquals("Charging constraint not persisted correctly.",
- taskStatus.getJob().isPreferCharging(),
- loaded.getJob().isPreferCharging());
- }
-
- @Test
- public void testPersistedPreferredDeviceIdleConstraint() throws Exception {
- JobInfo.Builder b = new Builder(8, mComponent)
- .setPrefersDeviceIdle(true)
- .setPersisted(true);
- JobStatus taskStatus =
- JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null, null);
-
- mTaskStoreUnderTest.add(taskStatus);
- waitForPendingIo();
-
- final JobSet jobStatusSet = new JobSet();
- mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
- assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
- JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
- assertEquals("Idle constraint not persisted correctly.",
- taskStatus.getJob().isPreferDeviceIdle(),
- loaded.getJob().isPreferDeviceIdle());
- }
-
- @Test
public void testJobWorkItems() throws Exception {
JobWorkItem item1 = new JobWorkItem.Builder().build();
item1.bumpDeliveryCount();
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 253592c..d1b2e8e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -43,6 +43,7 @@
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Resources;
import android.multiuser.Flags;
import android.os.Looper;
import android.os.Parcel;
@@ -50,21 +51,26 @@
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.text.TextUtils;
+import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService.UserData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -77,6 +83,7 @@
@MediumTest
public class UserManagerServiceUserInfoTest {
private UserManagerService mUserManagerService;
+ private Resources mResources;
@Before
public void setup() {
@@ -96,6 +103,8 @@
assertEquals("Multiple users so this test can't run.", 1, users.size());
assertEquals("Only user present isn't the system user.",
UserHandle.USER_SYSTEM, users.get(0).id);
+
+ mResources = InstrumentationRegistry.getTargetContext().getResources();
}
@Test
@@ -109,7 +118,7 @@
byte[] bytes = baos.toByteArray();
UserData read = mUserManagerService.readUserLP(
- data.info.id, new ByteArrayInputStream(bytes));
+ data.info.id, new ByteArrayInputStream(bytes), 0);
assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false);
}
@@ -146,11 +155,13 @@
// Clear the restrictions to see if they are properly read in from the user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);
+ final int userVersion = 10;
//read the secondary and SYSTEM user file to fetch local/global device policy restrictions.
- mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes));
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes),
+ userVersion);
if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
mUserManagerService.readUserLP(UserHandle.USER_SYSTEM,
- new ByteArrayInputStream(systemUserBytes));
+ new ByteArrayInputStream(systemUserBytes), userVersion);
}
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
@@ -303,6 +314,45 @@
assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO));
}
+ /** Tests readUserLP upgrading from version 9 to 10+. */
+ @Test
+ public void testUserRestrictionsUpgradeFromV9() throws Exception {
+ final String[] localRestrictions = new String[] {
+ UserManager.DISALLOW_CAMERA,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ };
+
+ final int userId = 100;
+ UserData data = new UserData();
+ data.info = createUser(userId, FLAG_FULL, "A type");
+
+ mUserManagerService.putUserInfo(data.info);
+
+ for (String restriction : localRestrictions) {
+ assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
+ assertFalse(mUserManagerService.hasUserRestriction(restriction, userId));
+ }
+
+ // Convert the xml resource to the system storage xml format.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream os = new DataOutputStream(baos);
+ XmlPullParser in = mResources.getXml(R.xml.user_100_v9);
+ XmlSerializer out = Xml.newBinarySerializer();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+ Xml.copy(in, out);
+ byte[] userBytes = baos.toByteArray();
+ baos.reset();
+
+ final int userVersion = 9;
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(userBytes),
+ userVersion);
+
+ for (String restriction : localRestrictions) {
+ assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
+ assertTrue(mUserManagerService.hasUserRestriction(restriction, userId));
+ }
+ }
+
/** Creates a UserInfo with the given flags and userType. */
private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) {
return new UserInfo(userId, "A Name", "A path", flags, userType);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 93adddb..6e5baee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -275,7 +275,7 @@
case "android.activity.launchTypeBubble": // KEY_LAUNCHED_FROM_BUBBLE
case "android.activity.splashScreenStyle": // KEY_SPLASH_SCREEN_STYLE
case "android.activity.launchIntoPipParams": // KEY_LAUNCH_INTO_PIP_PARAMS
- case "android.activity.dismissKeyguard": // KEY_DISMISS_KEYGUARD
+ case "android.activity.dismissKeyguardIfInsecure": // KEY_DISMISS_KEYGUARD_IF_INSECURE
case "android.activity.pendingIntentCreatorBackgroundActivityStartMode":
// KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE
case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 568471d..526201f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -247,39 +247,9 @@
}
@Test
- public void testInterceptQuietProfile_keepProfilesRunningEnabled() {
- // GIVEN that the user the activity is starting as is currently in quiet mode and
- // profiles are kept running when in quiet mode.
+ public void testInterceptQuietProfile() {
+ // GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
- when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
-
- // THEN calling intercept returns false because package also has to be suspended.
- assertFalse(
- mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
- }
-
- @Test
- public void testInterceptQuietProfile_keepProfilesRunningDisabled() {
- // GIVEN that the user the activity is starting as is currently in quiet mode and
- // profiles are stopped when in quiet mode (pre-U behavior, no profile app suspension).
- when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
- when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
-
- // THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
-
- // THEN the returned intent is the quiet mode intent
- assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
- .filterEquals(mInterceptor.mIntent));
- }
-
- @Test
- public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningEnabled() {
- // GIVEN that the user the activity is starting as is currently in quiet mode,
- // the package is suspended and profiles are kept running while in quiet mode.
- suspendPackage("com.test.suspending.package");
- when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
- when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
@@ -290,12 +260,10 @@
}
@Test
- public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningDisabled() {
- // GIVEN that the user the activity is starting as is currently in quiet mode,
- // the package is suspended and profiles are stopped while in quiet mode.
+ public void testInterceptQuietProfileWhenPackageSuspended() {
suspendPackage("com.test.suspending.package");
+ // GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
- when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null, null));
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 dd7dec0..7b1fa03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -76,6 +76,7 @@
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -601,30 +602,33 @@
final Task task = createTask(mDefaultDisplay);
final ActivityRecord bottomActivity = createActivityRecord(task);
final ActivityRecord homeActivity = mRootHomeTask.getTopNonFinishingActivity();
-
+ final ArrayList<ActivityRecord> openActivities = new ArrayList<>();
+ openActivities.add(homeActivity);
final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toHomeBuilder =
animationHandler.prepareAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME,
- mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
+ mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, openActivities);
assertTrue(toHomeBuilder.mIsLaunchBehind);
toHomeBuilder.build();
- verify(mAtm.mTaskOrganizerController, never())
- .addWindowlessStartingSurface(any(), any(), any(), any(), any());
+ verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface(
+ any(), any(), any(), any(), any(), any());
animationHandler.clearBackAnimateTarget();
+ openActivities.clear();
// Back to ACTIVITY and TASK have the same logic, just with different target.
final ActivityRecord topActivity = createActivityRecord(task);
+ openActivities.add(bottomActivity);
final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toActivityBuilder =
animationHandler.prepareAnimation(
BackNavigationInfo.TYPE_CROSS_ACTIVITY, mBackAnimationAdapter, task, task,
- topActivity, bottomActivity);
+ topActivity, openActivities);
assertFalse(toActivityBuilder.mIsLaunchBehind);
toActivityBuilder.build();
if (preferWindowlessSurface) {
- verify(mAtm.mTaskOrganizerController)
- .addWindowlessStartingSurface(any(), any(), any(), any(), any());
+ verify(mAtm.mTaskOrganizerController).addWindowlessStartingSurface(
+ any(), any(), any(), any(), any(), any());
} else {
- verify(mAtm.mTaskOrganizerController, never())
- .addWindowlessStartingSurface(any(), any(), any(), any(), any());
+ verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface(
+ any(), any(), any(), any(), any(), any());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4c25a4b..3b4b220 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1444,7 +1444,7 @@
});
assertSecurityException(expectCallable,
() -> mAtm.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable, () -> mAtm.getTaskSnapshot(0, true, false));
+ assertSecurityException(expectCallable, () -> mAtm.getTaskSnapshot(0, true));
assertSecurityException(expectCallable, () -> mAtm.registerTaskStackListener(null));
assertSecurityException(expectCallable,
() -> mAtm.unregisterTaskStackListener(null));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index c241033..eb78906 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -872,7 +872,7 @@
new TestDisplayContent.Builder(mAtm, 1000, 1500)
.setSystemDecorations(true).build();
- // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
+ // Use invalid user id to let StorageManager.isCeStorageUnlocked() return false.
final int currentUser = mRootWindowContainer.mCurrentUser;
mRootWindowContainer.mCurrentUser = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 9f43a17..55a7089 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -145,7 +145,7 @@
verifySecureExceptionThrown(activityOptions, taskSupervisor);
activityOptions = ActivityOptions.makeBasic();
- activityOptions.setDismissKeyguard();
+ activityOptions.setDismissKeyguardIfInsecure();
verifySecureExceptionThrown(activityOptions, taskSupervisor);
activityOptions = ActivityOptions.makeBasic();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index c57b051..8a90f12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -86,7 +86,9 @@
import android.platform.test.annotations.Presubmit;
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
import android.window.ITaskFragmentOrganizer;
+import android.window.RemoteTransition;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
@@ -546,6 +548,35 @@
}
@Test
+ public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() {
+ mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+
+ // Throw exception if the transaction has remote transition and is not requested by system
+ // organizer
+ assertThrows(SecurityException.class, () ->
+ mController.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE,
+ true /* shouldApplyIndependently */,
+ new RemoteTransition(mock(IRemoteTransition.class))));
+ }
+
+ @Test
+ public void testApplyTransaction_allowRemoteTransitionForSystemOrganizer() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+
+ mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+
+ // Remote transition is allowed for system organizer
+ mController.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE,
+ true /* shouldApplyIndependently */,
+ new RemoteTransition(mock(IRemoteTransition.class)));
+ }
+
+ @Test
public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment() {
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
@@ -1801,13 +1832,13 @@
private void assertApplyTransactionDisallowed(WindowContainerTransaction t) {
assertThrows(SecurityException.class, () ->
mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE,
- false /* shouldApplyIndependently */));
+ false /* shouldApplyIndependently */, null /* remoteTransition */));
}
/** Asserts that applying the given transaction will not throw any exception. */
private void assertApplyTransactionAllowed(WindowContainerTransaction t) {
mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE,
- false /* shouldApplyIndependently */);
+ false /* shouldApplyIndependently */, null /* remoteTransition */);
}
/** Asserts that there will be a transaction for TaskFragment appeared. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 435a835..0639deb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -73,6 +73,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
@@ -570,12 +571,15 @@
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
final Task task = rootTask.getBottomMostTask();
final ActivityRecord root = task.getTopNonFinishingActivity();
+ final PackageManager pm = mContext.getPackageManager();
+ spyOn(pm);
spyOn(mWm.mLetterboxConfiguration);
spyOn(root);
spyOn(root.mLetterboxUiController);
doReturn(true).when(root.mLetterboxUiController)
.shouldEnableUserAspectRatioSettings();
+ doReturn(new Intent()).when(pm).getLaunchIntentForPackage(anyString());
doReturn(false).when(root).inSizeCompatMode();
doReturn(task).when(root).getOrganizedTask();
@@ -593,6 +597,10 @@
doReturn(true).when(root).inSizeCompatMode();
assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
doReturn(false).when(root).inSizeCompatMode();
+
+ // When app doesn't have any launchable activities the button is not enabled
+ doReturn(null).when(pm).getLaunchIntentForPackage(anyString());
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 699580a..2c39173 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -614,7 +614,7 @@
t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), true);
mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
- false /* shouldApplyIndependently */);
+ false /* shouldApplyIndependently */, null /* remoteTransition */);
// Should be not visible and not focusable after the transaction.
assertFalse(taskFragment.shouldBeVisible(null));
@@ -628,7 +628,7 @@
t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), false);
mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
- false /* shouldApplyIndependently */);
+ false /* shouldApplyIndependently */, null /* remoteTransition */);
// Should be visible and focusable after the transaction.
assertTrue(taskFragment.shouldBeVisible(null));
@@ -680,7 +680,7 @@
assertThrows(SecurityException.class, () ->
mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
- false /* shouldApplyIndependently */)
+ false /* shouldApplyIndependently */, null /* remoteTransition */)
);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e413663..f64ab22 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -57,6 +57,7 @@
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageEventsQuery;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -113,6 +114,8 @@
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import com.android.server.utils.AlarmQueue;
+import libcore.util.EmptyArray;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -1478,6 +1481,14 @@
* Called by the Binder stub.
*/
UsageEvents queryEvents(int userId, long beginTime, long endTime, int flags) {
+ return queryEventsWithTypes(userId, beginTime, endTime, flags, EmptyArray.INT);
+ }
+
+ /**
+ * Called by the Binder stub.
+ */
+ UsageEvents queryEventsWithTypes(int userId, long beginTime, long endTime, int flags,
+ int[] eventTypeFilter) {
synchronized (mLock) {
if (!mUserUnlockedStates.contains(userId)) {
Slog.w(TAG, "Failed to query events for locked user " + userId);
@@ -1488,7 +1499,7 @@
if (service == null) {
return null; // user was stopped or removed
}
- return service.queryEvents(beginTime, endTime, flags);
+ return service.queryEvents(beginTime, endTime, flags, eventTypeFilter);
}
}
@@ -2123,7 +2134,7 @@
private final class BinderService extends IUsageStatsManager.Stub {
- private boolean hasPermission(String callingPackage) {
+ private boolean hasQueryPermission(String callingPackage) {
final int callingUid = Binder.getCallingUid();
if (callingUid == Process.SYSTEM_UID) {
return true;
@@ -2203,10 +2214,37 @@
return uid == Process.SYSTEM_UID;
}
+ private UsageEvents queryEventsHelper(int userId, long beginTime, long endTime,
+ String callingPackage, int[] eventTypeFilter) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
+ callingUid, userId);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
+ userId, callingPackage, callingPid, callingUid);
+ final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
+ final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
+ callingPid, callingUid);
+ int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
+ if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
+ if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
+ if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
+ if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
+
+ return UsageStatsService.this.queryEventsWithTypes(userId, beginTime, endTime,
+ flags, eventTypeFilter);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public ParceledListSlice<UsageStats> queryUsageStats(int bucketType, long beginTime,
long endTime, String callingPackage, int userId) {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
@@ -2234,7 +2272,7 @@
@Override
public ParceledListSlice<ConfigurationStats> queryConfigurationStats(int bucketType,
long beginTime, long endTime, String callingPackage) throws RemoteException {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
@@ -2256,7 +2294,7 @@
@Override
public ParceledListSlice<EventStats> queryEventStats(int bucketType,
long beginTime, long endTime, String callingPackage) throws RemoteException {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
@@ -2277,32 +2315,25 @@
@Override
public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
- final int userId = UserHandle.getCallingUserId();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- callingUid, userId);
+ return queryEventsHelper(UserHandle.getCallingUserId(), beginTime, endTime,
+ callingPackage, /* eventTypeFilter= */ EmptyArray.INT);
+ }
- final long token = Binder.clearCallingIdentity();
- try {
- final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
- userId, callingPackage, callingPid, callingUid);
- final boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
- final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
- callingPid, callingUid);
- int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
- if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
- if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
- if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
- if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
- return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
- } finally {
- Binder.restoreCallingIdentity(token);
+ @Override
+ public UsageEvents queryEventsWithFilter(@NonNull UsageEventsQuery query,
+ @NonNull String callingPackage) {
+ Objects.requireNonNull(query);
+ Objects.requireNonNull(callingPackage);
+
+ if (!hasQueryPermission(callingPackage)) {
+ return null;
}
+ return queryEventsHelper(UserHandle.getCallingUserId(), query.getBeginTimeMillis(),
+ query.getEndTimeMillis(), callingPackage, query.getEventTypeFilter());
}
@Override
@@ -2312,7 +2343,7 @@
final int callingUserId = UserHandle.getUserId(callingUid);
checkCallerIsSameApp(callingPackage);
- final boolean includeTaskRoot = hasPermission(callingPackage);
+ final boolean includeTaskRoot = hasQueryPermission(callingPackage);
final long token = Binder.clearCallingIdentity();
try {
@@ -2326,7 +2357,7 @@
@Override
public UsageEvents queryEventsForUser(long beginTime, long endTime, int userId,
String callingPackage) {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
@@ -2337,33 +2368,14 @@
"No permission to query usage stats for this user");
}
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- callingUid, callingUserId);
-
- final long token = Binder.clearCallingIdentity();
- try {
- final boolean hideShortcutInvocationEvents = shouldHideShortcutInvocationEvents(
- userId, callingPackage, callingPid, callingUid);
- final boolean obfuscateNotificationEvents = shouldObfuscateNotificationEvents(
- callingPid, callingUid);
- boolean hideLocusIdEvents = shouldHideLocusIdEvents(callingPid, callingUid);
- int flags = UsageEvents.SHOW_ALL_EVENT_DATA;
- if (obfuscateInstantApps) flags |= UsageEvents.OBFUSCATE_INSTANT_APPS;
- if (hideShortcutInvocationEvents) flags |= UsageEvents.HIDE_SHORTCUT_EVENTS;
- if (hideLocusIdEvents) flags |= UsageEvents.HIDE_LOCUS_EVENTS;
- if (obfuscateNotificationEvents) flags |= UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
- return UsageStatsService.this.queryEvents(userId, beginTime, endTime, flags);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return queryEventsHelper(userId, beginTime, endTime, callingPackage,
+ /* eventTypeFilter= */ EmptyArray.INT);
}
@Override
public UsageEvents queryEventsForPackageForUser(long beginTime, long endTime,
int userId, String pkg, String callingPackage) {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
return null;
}
if (userId != UserHandle.getCallingUserId()) {
@@ -2404,7 +2416,7 @@
if (actualCallingUid != callingUid) {
return false;
}
- } else if (!hasPermission(callingPackage)) {
+ } else if (!hasQueryPermission(callingPackage)) {
return false;
}
final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
@@ -2454,7 +2466,7 @@
final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
// If the calling app is asking about itself, continue, else check for permission.
final boolean sameApp = packageUid == callingUid;
- if (!sameApp && !hasPermission(callingPackage)) {
+ if (!sameApp && !hasQueryPermission(callingPackage)) {
throw new SecurityException("Don't have permission to query app standby bucket");
}
@@ -2502,7 +2514,7 @@
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- if (!hasPermission(callingPackageName)) {
+ if (!hasQueryPermission(callingPackageName)) {
throw new SecurityException(
"Don't have permission to query app standby bucket");
}
@@ -2556,7 +2568,7 @@
final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
// If the calling app is asking about itself, continue, else check for permission.
if (packageUid != callingUid) {
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
throw new SecurityException(
"Don't have permission to query min app standby bucket");
}
@@ -2900,7 +2912,7 @@
if (!hasPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission");
}
- if (!hasPermission(callingPackage)) {
+ if (!hasQueryPermission(callingPackage)) {
throw new SecurityException("Don't have permission to query usage stats");
}
synchronized (mLock) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index ddb2796..9b67ab6 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -70,7 +70,7 @@
* in UsageStatsService.
*/
class UserUsageStatsService {
- private static final String TAG = "UsageStatsService";
+ private static final String TAG = UsageStatsService.TAG;
private static final boolean DEBUG = UsageStatsService.DEBUG;
private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final int sDateFormatFlags =
@@ -535,10 +535,23 @@
return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner, true);
}
- UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
+ UsageEvents queryEvents(final long beginTime, final long endTime, int flags,
+ int[] eventTypeFilter) {
if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
return null;
}
+
+ // Ensure valid event type filter.
+ final boolean isQueryForAllEvents = ArrayUtils.isEmpty(eventTypeFilter);
+ final boolean[] queryEventFilter = new boolean[Event.MAX_EVENT_TYPE + 1];
+ if (!isQueryForAllEvents) {
+ for (int eventType : eventTypeFilter) {
+ if (eventType < Event.NONE || eventType > Event.MAX_EVENT_TYPE) {
+ throw new IllegalArgumentException("invalid event type: " + eventType);
+ }
+ queryEventFilter[eventType] = true;
+ }
+ }
final ArraySet<String> names = new ArraySet<>();
List<Event> results = queryStats(INTERVAL_DAILY,
beginTime, endTime, new StatCombiner<Event>() {
@@ -547,6 +560,7 @@
List<Event> accumulatedResult) {
final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
+
for (int i = startIndex; i < size; i++) {
Event event = stats.events.get(i);
if (event.mTimeStamp >= endTime) {
@@ -554,6 +568,10 @@
}
final int eventType = event.mEventType;
+ if (!isQueryForAllEvents && !queryEventFilter[eventType]) {
+ continue;
+ }
+
if (eventType == Event.SHORTCUT_INVOCATION
&& (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
continue;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1f32c97..55fecfc 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,6 +55,7 @@
import com.android.internal.telecom.ClientTransactionalServiceRepository;
import com.android.internal.telecom.ClientTransactionalServiceWrapper;
import com.android.internal.telecom.ITelecomService;
+import com.android.server.telecom.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -412,6 +414,14 @@
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
/**
+ * The extra for call log uri that was used to mark missed calls as read when dialer gets the
+ * notification on reboot.
+ */
+ @FlaggedApi(Flags.FLAG_ADD_CALL_URI_FOR_MISSED_CALLS)
+ public static final String EXTRA_CALL_LOG_URI =
+ "android.telecom.extra.CALL_LOG_URI";
+
+ /**
* Optional extra for incoming containing a long which specifies the time the
* call was answered by user. This value is in milliseconds.
* @hide
@@ -2361,6 +2371,11 @@
* <p>
* <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after
* the call is placed in order for the notification to be non-dismissible.
+ * <p><b>Note</b>: Call Forwarding MMI codes can only be dialed by applications that are
+ * configured as the user defined default dialer or system dialer role. If a call containing a
+ * call forwarding MMI code is placed by an application that is not in one of these roles, the
+ * dialer will be launched with a UI showing the MMI code already populated so that the user can
+ * confirm the action before the call is placed.
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 042b2a3..4250bd1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9692,6 +9692,7 @@
*
* @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
* @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
*/
public static final String
KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c0d6b30..e9ea5a7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17483,9 +17483,8 @@
* {@link CarrierConfigManager
* #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
* and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SLICING_ADDITIONAL_ERROR_CODES)
public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16;
/**
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 11cbcb1..cb7926c 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -568,6 +568,7 @@
private final int mSkip464Xlat;
private final boolean mAlwaysOn;
private final @InfrastructureBitmask int mInfrastructureBitmask;
+ private final boolean mEsimBootstrapProvisioning;
/**
* Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
@@ -979,6 +980,18 @@
return mInfrastructureBitmask;
}
+ /**
+ * Returns esim bootstrap provisioning flag for which the APN can be used on. For example,
+ * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning
+ * is being activated.
+ *
+ * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isEsimBootstrapProvisioning() {
+ return mEsimBootstrapProvisioning;
+ }
+
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
@@ -1016,6 +1029,7 @@
this.mSkip464Xlat = builder.mSkip464Xlat;
this.mAlwaysOn = builder.mAlwaysOn;
this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
+ this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning;
}
/**
@@ -1097,6 +1111,8 @@
.setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)
.setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
+ .setEsimBootstrapProvisioning(cursor.getInt(
+ cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1)
.buildWithoutCheck();
}
@@ -1137,6 +1153,7 @@
.setSkip464Xlat(apn.mSkip464Xlat)
.setAlwaysOn(apn.mAlwaysOn)
.setInfrastructureBitmask(apn.mInfrastructureBitmask)
+ .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning)
.buildWithoutCheck();
}
@@ -1184,6 +1201,7 @@
sb.append(", ").append(mAlwaysOn);
sb.append(", ").append(mInfrastructureBitmask);
sb.append(", ").append(Objects.hash(mUser, mPassword));
+ sb.append(", ").append(mEsimBootstrapProvisioning);
return sb.toString();
}
@@ -1247,7 +1265,7 @@
mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask,
mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime,
mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat,
- mAlwaysOn, mInfrastructureBitmask);
+ mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning);
}
@Override
@@ -1289,7 +1307,8 @@
&& mCarrierId == other.mCarrierId
&& mSkip464Xlat == other.mSkip464Xlat
&& mAlwaysOn == other.mAlwaysOn
- && mInfrastructureBitmask == other.mInfrastructureBitmask;
+ && mInfrastructureBitmask == other.mInfrastructureBitmask
+ && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning);
}
/**
@@ -1340,7 +1359,8 @@
&& Objects.equals(mCarrierId, other.mCarrierId)
&& Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
&& Objects.equals(mAlwaysOn, other.mAlwaysOn)
- && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask);
+ && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask)
+ && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning);
}
/**
@@ -1378,7 +1398,9 @@
&& Objects.equals(this.mCarrierId, other.mCarrierId)
&& Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
&& Objects.equals(this.mAlwaysOn, other.mAlwaysOn)
- && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask);
+ && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask)
+ && Objects.equals(this.mEsimBootstrapProvisioning,
+ other.mEsimBootstrapProvisioning);
}
// Equal or one is null.
@@ -1451,6 +1473,7 @@
apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask);
+ apnValue.put(Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning);
return apnValue;
}
@@ -1724,6 +1747,7 @@
dest.writeInt(mSkip464Xlat);
dest.writeBoolean(mAlwaysOn);
dest.writeInt(mInfrastructureBitmask);
+ dest.writeBoolean(mEsimBootstrapProvisioning);
}
private static ApnSetting readFromParcel(Parcel in) {
@@ -1760,6 +1784,7 @@
.setSkip464Xlat(in.readInt())
.setAlwaysOn(in.readBoolean())
.setInfrastructureBitmask(in.readInt())
+ .setEsimBootstrapProvisioning(in.readBoolean())
.buildWithoutCheck();
}
@@ -1842,6 +1867,7 @@
private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
private boolean mAlwaysOn;
private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR;
+ private boolean mEsimBootstrapProvisioning;
/**
* Default constructor for Builder.
@@ -2280,6 +2306,19 @@
}
/**
+ * Sets esim bootstrap provisioning flag
+ *
+ * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap
+ * provisioning, {@code false} otherwise.
+ * @hide
+ */
+ @NonNull
+ public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) {
+ this.mEsimBootstrapProvisioning = esimBootstrapProvisioning;
+ return this;
+ }
+
+ /**
* Builds {@link ApnSetting} from this builder.
*
* @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index f4f2be6..3d49d81 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -166,22 +166,6 @@
}
android_test {
- name: "FlickerTestsAppLaunch",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
- package_name: "com.android.server.wm.flicker.launch",
- instrumentation_target_package: "com.android.server.wm.flicker.launch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppLaunchCommon-src",
- ":FlickerTestsAppLaunch2-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
name: "FlickerTestsAppLaunch1",
defaults: ["FlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 85709c9..ed71531 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -28,7 +28,13 @@
<option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
<!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
<option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
index 2e9620b..17f7490 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
+import com.android.server.wm.flicker.replacesLayer
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -119,7 +120,13 @@
@FlakyTest(bugId = 240916028)
@Test
override fun appLayerReplacesLauncher() {
- super.appLayerReplacesLauncher()
+ flicker.replacesLayer(
+ ComponentNameMatcher.LAUNCHER,
+ testApp,
+ ignoreEntriesWithRotationLayer = true,
+ ignoreSnapshot = true,
+ ignoreSplashscreen = false
+ )
}
@FlakyTest(bugId = 240916028)
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index cb37821..59dc689 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -313,7 +313,8 @@
@Test
public void testBuilderAndGettersSafeModeDisabled() {
- final VcnGatewayConnectionConfig config = newBuilderMinimal().enableSafeMode(false).build();
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
assertFalse(config.isSafeModeEnabled());
}
@@ -335,7 +336,8 @@
@Test
public void testPersistableBundleSafeModeDisabled() {
- final VcnGatewayConnectionConfig config = newBuilderMinimal().enableSafeMode(false).build();
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
@@ -456,7 +458,7 @@
assertEquals(config.isSafeModeEnabled(), configEqual.isSafeModeEnabled());
final VcnGatewayConnectionConfig configNotEqual =
- newBuilderMinimal().enableSafeMode(false).build();
+ newBuilderMinimal().setSafeModeEnabled(false).build();
assertEquals(config, configEqual);
assertNotEquals(config, configNotEqual);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index bf73198..f846164 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -661,7 +661,7 @@
throws Exception {
final VcnGatewayConnectionConfig config =
VcnGatewayConnectionConfigTest.newTestBuilderMinimal()
- .enableSafeMode(safeModeEnabledByCaller)
+ .setSafeModeEnabled(safeModeEnabledByCaller)
.build();
final VcnGatewayConnection.Dependencies deps =
mock(VcnGatewayConnection.Dependencies.class);
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index a20266a..28eab8f 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -20,7 +20,6 @@
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.aidl.EnforcePermissionDetector
-import com.google.android.lint.aidl.EnforcePermissionHelperDetector
import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector
import com.google.auto.service.AutoService
@@ -30,7 +29,8 @@
override val issues = listOf(
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION,
SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
)
@@ -45,4 +45,4 @@
feedbackUrl = "http://b/issues/new?component=315013",
contact = "repsonsible-apis@google.com"
)
-}
\ No newline at end of file
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 3a95df9..dcd94f1 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -30,31 +30,34 @@
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.google.android.lint.findCallExpression
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
-import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.toUElement
+import org.jetbrains.uast.skipParenthesizedExprDown
import java.util.EnumSet
/**
- * Lint Detector that ensures that any method overriding a method annotated
- * with @EnforcePermission is also annotated with the exact same annotation.
- * The intent is to surface the effective permission checks to the service
- * implementations.
+ * Lint Detector that ensures consistency when using the @EnforcePermission
+ * annotation. Multiple verifications are implemented:
*
- * This is done with 2 mechanisms:
* 1. Visit any annotation usage, to ensure that any derived class will have
- * the correct annotation on each methods. This is for the top to bottom
- * propagation.
- * 2. Visit any annotation, to ensure that if a method is annotated, it has
+ * the correct annotation on each methods. Even if the subclass does not
+ * have the annotation, visitAnnotationUsage will be called which allows us
+ * to capture the issue.
+ * 2. Visit any method, to ensure that if a method is annotated, it has
* its ancestor also annotated. This is to avoid having an annotation on a
* Java method without the corresponding annotation on the AIDL interface.
+ * 3. When annotated, ensures that the first instruction is to call the helper
+ * method (or the parent helper).
*/
class EnforcePermissionDetector : Detector(), SourceCodeScanner {
@@ -62,9 +65,8 @@
return listOf(ANNOTATION_ENFORCE_PERMISSION)
}
- override fun getApplicableUastTypes(): List<Class<out UElement>> {
- return listOf(UAnnotation::class.java)
- }
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(UMethod::class.java)
private fun annotationValueGetChildren(elem: PsiElement): Array<PsiElement> {
if (elem is PsiArrayInitializerMemberValue)
@@ -129,11 +131,6 @@
overriddenMethod: PsiMethod,
checkEquivalence: Boolean = true
) {
- // If method is not from a Stub subclass, this method shouldn't use @EP at all.
- // This is handled by EnforcePermissionHelperDetector.
- if (!isContainedInSubclassOfStub(context, overridingMethod.toUElement() as? UMethod)) {
- return
- }
val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val location = context.getLocation(element)
@@ -169,40 +166,102 @@
) {
if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
annotationInfo.origin == AnnotationOrigin.METHOD) {
+ /* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
+ val uMethod = element as? UMethod ?: return
+ if (!isContainedInSubclassOfStub(context, uMethod)) {
+ return
+ }
val overridingMethod = element.sourcePsi as PsiMethod
val overriddenMethod = usageInfo.referenced as PsiMethod
compareMethods(context, element, overridingMethod, overriddenMethod)
}
}
- override fun createUastHandler(context: JavaContext): UElementHandler {
- return object : UElementHandler() {
- override fun visitAnnotation(node: UAnnotation) {
- if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) {
- return
- }
- val method = node.uastParent as? UMethod ?: return
- val overridingMethod = method as PsiMethod
- val parents = overridingMethod.findSuperMethods()
- for (overriddenMethod in parents) {
- // The equivalence check can be skipped, if both methods are
- // annotated, it will be verified by visitAnnotationUsage.
- compareMethods(context, method, overridingMethod,
- overriddenMethod, checkEquivalence = false)
- }
+ override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
+
+ private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
+ override fun visitMethod(node: UMethod) {
+ if (context.evaluator.isAbstract(node)) return
+ if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
+
+ if (!isContainedInSubclassOfStub(context, node)) {
+ context.report(
+ ISSUE_MISUSING_ENFORCE_PERMISSION,
+ node,
+ context.getLocation(node),
+ "The class of ${node.name} does not inherit from an AIDL generated Stub class"
+ )
+ return
+ }
+
+ /* Check that we are connected to the super class */
+ val overridingMethod = node as PsiMethod
+ val parents = overridingMethod.findSuperMethods()
+ for (overriddenMethod in parents) {
+ // The equivalence check can be skipped, if both methods are
+ // annotated, it will be verified by visitAnnotationUsage.
+ compareMethods(context, node, overridingMethod,
+ overriddenMethod, checkEquivalence = false)
+ }
+
+ /* Check that the helper is called as a first instruction */
+ val targetExpression = getHelperMethodCallSourceString(node)
+ val message =
+ "Method must start with $targetExpression or super.${node.name}(), if applicable"
+
+ val firstExpression = (node.uastBody as? UBlockExpression)
+ ?.expressions?.firstOrNull()
+
+ if (firstExpression == null) {
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ )
+ return
+ }
+
+ val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
+ .asSourceString()
+ .filterNot(Char::isWhitespace)
+
+ if (firstExpressionSource != targetExpression &&
+ firstExpressionSource != "super.$targetExpression") {
+ // calling super.<methodName>() is also legal
+ val directSuper = context.evaluator.getSuperMethod(node)
+ val firstCall = findCallExpression(firstExpression)?.resolve()
+ if (directSuper != null && firstCall == directSuper) return
+
+ val locationTarget = getLocationTarget(firstExpression)
+ val expressionLocation = context.getLocation(locationTarget)
+
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ getHelperMethodFix(node, expressionLocation),
+ )
}
}
}
companion object {
+
+ private const val HELPER_SUFFIX = "_enforcePermission"
+
val EXPLANATION = """
- The @EnforcePermission annotation is used to indicate that the underlying binder code
- has already verified the caller's permissions before calling the appropriate method. The
- verification code is usually generated by the AIDL compiler, which also takes care of
- annotating the generated Java code.
+ The @EnforcePermission annotation is used to delegate the verification of the caller's
+ permissions to a generated AIDL method.
In order to surface that information to platform developers, the same annotation must be
used on the implementation class or methods.
+
+ The @EnforcePermission annotation can only be used on methods whose class extends from
+ the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
+ AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
+
+ yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
+ either call it directly, or call it indirectly via super.yourMethodName().
"""
val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
@@ -230,5 +289,44 @@
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
+
+ val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
+ id = "MissingEnforcePermissionHelper",
+ briefDescription = """Missing permission-enforcing method call in AIDL method
+ |annotated with @EnforcePermission""".trimMargin(),
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MisusingEnforcePermissionAnnotation",
+ briefDescription = "@EnforcePermission cannot be used here",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ /**
+ * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
+ * resulting in an incorrect Location if used directly
+ */
+ private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
+ if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
+ if (firstExpression is UDeclarationsExpression) {
+ return firstExpression.declarations.firstOrNull()?.sourcePsi
+ }
+ return null
+ }
}
}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
deleted file mode 100644
index 758de4d..0000000
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ /dev/null
@@ -1,151 +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.google.android.lint.aidl
-
-import com.android.tools.lint.client.api.UElementHandler
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.google.android.lint.findCallExpression
-import com.intellij.psi.PsiElement
-import org.jetbrains.uast.UBlockExpression
-import org.jetbrains.uast.UDeclarationsExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UExpression
-import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.skipParenthesizedExprDown
-
-import java.util.EnumSet
-
-class EnforcePermissionHelperDetector : Detector(), SourceCodeScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement?>> =
- listOf(UMethod::class.java)
-
- override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
-
- private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
- override fun visitMethod(node: UMethod) {
- if (context.evaluator.isAbstract(node)) return
- if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
-
- if (!isContainedInSubclassOfStub(context, node)) {
- context.report(
- ISSUE_MISUSING_ENFORCE_PERMISSION,
- node,
- context.getLocation(node),
- "The class of ${node.name} does not inherit from an AIDL generated Stub class"
- )
- return
- }
-
- val targetExpression = getHelperMethodCallSourceString(node)
- val message =
- "Method must start with $targetExpression or super.${node.name}(), if applicable"
-
- val firstExpression = (node.uastBody as? UBlockExpression)
- ?.expressions?.firstOrNull()
-
- if (firstExpression == null) {
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- )
- return
- }
-
- val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
- .asSourceString()
- .filterNot(Char::isWhitespace)
-
- if (firstExpressionSource != targetExpression &&
- firstExpressionSource != "super.$targetExpression") {
- // calling super.<methodName>() is also legal
- val directSuper = context.evaluator.getSuperMethod(node)
- val firstCall = findCallExpression(firstExpression)?.resolve()
- if (directSuper != null && firstCall == directSuper) return
-
- val locationTarget = getLocationTarget(firstExpression)
- val expressionLocation = context.getLocation(locationTarget)
-
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- getHelperMethodFix(node, expressionLocation),
- )
- }
- }
- }
-
- companion object {
- private const val HELPER_SUFFIX = "_enforcePermission"
-
- private const val EXPLANATION = """
- The @EnforcePermission annotation can only be used on methods whose class extends from
- the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
- AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
-
- yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
- either call it directly, or call it indirectly via super.yourMethodName().
- """
-
- val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
- id = "MissingEnforcePermissionHelper",
- briefDescription = """Missing permission-enforcing method call in AIDL method
- |annotated with @EnforcePermission""".trimMargin(),
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionHelperDetector::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
- )
- )
-
- val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
- id = "MisusingEnforcePermissionAnnotation",
- briefDescription = "@EnforcePermission cannot be used here",
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionHelperDetector::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
- )
- )
-
- /**
- * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
- * resulting in an incorrect Location if used directly
- */
- private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
- if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
- if (firstExpression is UDeclarationsExpression) {
- return firstExpression.declarations.firstOrNull()?.sourcePsi
- }
- return null
- }
- }
-}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
index 5a63bb4..3ef02f8 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
@@ -25,10 +25,10 @@
@Suppress("UnstableApiUsage")
class EnforcePermissionHelperDetectorCodegenTest : LintDetectorTest() {
- override fun getDetector(): Detector = EnforcePermissionHelperDetector()
+ override fun getDetector(): Detector = EnforcePermissionDetector()
override fun getIssues(): List<Issue> = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
index 10a6e1d..64e2bfb 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
@@ -20,10 +20,10 @@
import com.android.tools.lint.checks.infrastructure.TestLintTask
class EnforcePermissionHelperDetectorTest : LintDetectorTest() {
- override fun getDetector() = EnforcePermissionHelperDetector()
+ override fun getDetector() = EnforcePermissionDetector()
override fun getIssues() = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- EnforcePermissionHelperDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk()