diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 0ec68d7f94e8e19545e784316de77c3612ae50e5..c35424d80dae1af081328889dd63f5daad74e327 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -15,6 +15,7 @@ aconfig_srcjars = [ ":android.app.usage.flags-aconfig-java{.generated_srcjars}", ":android.content.pm.flags-aconfig-java{.generated_srcjars}", + ":android.nfc.flags-aconfig-java{.generated_srcjars}", ":android.os.flags-aconfig-java{.generated_srcjars}", ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}", ":android.security.flags-aconfig-java{.generated_srcjars}", @@ -122,6 +123,19 @@ cc_aconfig_library { aconfig_declarations: "com.android.text.flags-aconfig", } +// NFC +aconfig_declarations { + name: "android.nfc.flags-aconfig", + package: "android.nfc", + srcs: ["core/java/android/nfc/*.aconfig"], +} + +java_aconfig_library { + name: "android.nfc.flags-aconfig-java", + aconfig_declarations: "android.nfc.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Security aconfig_declarations { name: "android.security.flags-aconfig", 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 e7ea7c2dbcbed34677183e903063e77af7b05700..a143d6fd42507ae91118a3fbbccb765c4e63af3e 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1762,8 +1762,16 @@ public class JobSchedulerService extends com.android.server.SystemService sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount()); } - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, - uId, null, jobStatus.getBatteryName(), + final int sourceUid = uId; + FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, + jobStatus.isProxyJob() + ? new int[]{sourceUid, jobStatus.getUid()} : new int[]{sourceUid}, + // Given that the source tag is set by the calling app, it should be connected + // to the calling app in the attribution for a proxied job. + jobStatus.isProxyJob() + ? new String[]{null, jobStatus.getSourceTag()} + : new String[]{jobStatus.getSourceTag()}, + jobStatus.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED, JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(), jobStatus.getLoggingJobId(), @@ -2191,8 +2199,16 @@ public class JobSchedulerService extends com.android.server.SystemService cancelled, reason, internalReasonCode, debugReason); // If the job was running, the JobServiceContext should log with state FINISHED. if (!wasRunning) { - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, - cancelled.getSourceUid(), null, cancelled.getBatteryName(), + final int sourceUid = cancelled.getSourceUid(); + FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, + cancelled.isProxyJob() + ? new int[]{sourceUid, cancelled.getUid()} : new int[]{sourceUid}, + // Given that the source tag is set by the calling app, it should be connected + // to the calling app in the attribution for a proxied job. + cancelled.isProxyJob() + ? new String[]{null, cancelled.getSourceTag()} + : new String[]{cancelled.getSourceTag()}, + cancelled.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED, internalReasonCode, cancelled.getStandbyBucket(), cancelled.getLoggingJobId(), 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 b737041f838e3d602cbc6ffc06227aa84db7bbf1..43d2ae9e077b74b5c3bb989ccd19ce61b6a85bea 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -470,8 +470,15 @@ public final class JobServiceContext implements ServiceConnection { return false; } mJobPackageTracker.noteActive(job); - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, - job.getSourceUid(), null, job.getBatteryName(), + final int sourceUid = job.getSourceUid(); + 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 + // to the calling app in the attribution for a proxied job. + job.isProxyJob() + ? new String[]{null, job.getSourceTag()} + : new String[]{job.getSourceTag()}, + job.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED, JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, job.getStandbyBucket(), @@ -1531,8 +1538,16 @@ public final class JobServiceContext implements ServiceConnection { } mJobPackageTracker.noteInactive(completedJob, loggingInternalStopReason, loggingDebugReason); - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, - completedJob.getSourceUid(), null, completedJob.getBatteryName(), + final int sourceUid = completedJob.getSourceUid(); + FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, + completedJob.isProxyJob() + ? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid}, + // Given that the source tag is set by the calling app, it should be connected + // to the calling app in the attribution for a proxied job. + completedJob.isProxyJob() + ? new String[]{null, completedJob.getSourceTag()} + : new String[]{completedJob.getSourceTag()}, + completedJob.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED, loggingInternalStopReason, completedJob.getStandbyBucket(), completedJob.getLoggingJobId(), 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 e0c766f9c7679e2bdc65421601cddd8b86260941..458ff35c30ee975222e47ff1a2c0728e470c3116 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 @@ -255,6 +255,9 @@ public final class JobStatus { final String tag; + /** Whether this job was scheduled by one app on behalf of another. */ + final boolean mIsProxyJob; + private GrantedUriPermissions uriPerms; private boolean prepared; @@ -626,6 +629,9 @@ public final class JobStatus { : bnNamespace + job.getService().flattenToShortString(); this.tag = "*job*/" + this.batteryName + "#" + job.getId(); + final String componentPackage = job.getService().getPackageName(); + mIsProxyJob = !this.sourcePackageName.equals(componentPackage); + this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; @@ -1048,6 +1054,11 @@ public final class JobStatus { return mLoggingJobId; } + /** Returns whether this job was scheduled by one app on behalf of another. */ + public boolean isProxyJob() { + return mIsProxyJob; + } + public void printUniqueId(PrintWriter pw) { if (mNamespace != null) { pw.print(mNamespace); @@ -1292,6 +1303,12 @@ public final class JobStatus { return mNamespaceHash; } + /** + * Returns the tag passed by the calling app to describe the source app work. This is primarily + * only valid if {@link #isProxyJob()} returns true, but may be non-null if an app uses + * {@link JobScheduler#scheduleAsPackage(JobInfo, String, int, String)} for itself. + */ + @Nullable public String getSourceTag() { return sourceTag; } @@ -1871,9 +1888,13 @@ public final class JobStatus { mReadyDynamicSatisfied = mDynamicConstraints != 0 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) { - FrameworkStatsLog.write_non_chained( + FrameworkStatsLog.write( FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED, - sourceUid, null, getBatteryName(), getProtoConstraint(constraint), + isProxyJob() ? new int[]{sourceUid, getUid()} : new int[]{sourceUid}, + // Given that the source tag is set by the calling app, it should be connected + // to the calling app in the attribution for a proxied job. + isProxyJob() ? new String[]{null, sourceTag} : new String[]{sourceTag}, + getBatteryName(), getProtoConstraint(constraint), state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED : FrameworkStatsLog .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 3de774868bb78d3bf8f0f26d879e89e9ad62bf08..9b8d2b496a302f6acf904c4de3757ee0c5a91986 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -554,6 +554,30 @@ package android.provider { } +package android.se.omapi { + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeFrameworkInitializer { + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager); + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeServiceManager { + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer(); + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public SeServiceManager.ServiceNotFoundException(@NonNull String); + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final class SeServiceManager.ServiceRegisterer { + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder get(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void register(@NonNull android.os.IBinder); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder tryGet(); + } + +} + package android.telecom { public abstract class ConnectionService extends android.app.Service { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 69ed5aeda7de648912e90965548b62cc15fc316f..660859d6e66e469444b1faa228b703599e5c2ebf 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9627,6 +9627,73 @@ package android.nfc { } +package android.nfc.cardemulation { + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; + } + + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable { + ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid(); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String); + method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR; + } + +} + package android.os { public class BatteryManager { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index ee031db50b620071b47d618dc65af3e188901585..b11a686d8f3260711c52a1da94bcc6096a852066 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -251,6 +251,18 @@ UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#isBatt New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.isBatteryCharging() UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder#setBatteryCharging(boolean): New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder.setBatteryCharging(boolean) +UnflaggedApi: android.nfc.cardemulation.AidGroup#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.AidGroup#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.PARCELABLE_WRITE_RETURN_VALUE +UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.ApduServiceInfo#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.ApduServiceInfo.PARCELABLE_WRITE_RETURN_VALUE +UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#CONTENTS_FILE_DESCRIPTOR: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.CONTENTS_FILE_DESCRIPTOR +UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#PARCELABLE_WRITE_RETURN_VALUE: + New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.PARCELABLE_WRITE_RETURN_VALUE UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_ONBOARDING: New API must be flagged with @FlaggedApi: field android.os.BugreportParams.BUGREPORT_MODE_ONBOARDING UnflaggedApi: android.provider.Settings#ACTION_APP_PERMISSIONS_SETTINGS: diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b4f6d6fe770d0aa0226f50b9239fe8f38a063ebb..4f4569134c1959acc83c2a0d6ba277c65e9c2a4d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -5192,11 +5192,21 @@ public class ActivityManager { * @hide */ public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) { + broadcastStickyIntent(intent, null, appOp, options, userId); + } + + /** + * Convenience for sending a sticky broadcast. For internal use only. + * + * @hide + */ + public static void broadcastStickyIntent(Intent intent, String[] excludedPackages, + int appOp, Bundle options, int userId) { try { getService().broadcastIntentWithFeature( null, null, intent, null, null, Activity.RESULT_OK, null, null, null /*requiredPermissions*/, null /*excludedPermissions*/, - null /*excludedPackages*/, appOp, options, false, true, userId); + excludedPackages, appOp, options, false, true, userId); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9f03030b35186d04f91d8b9473145917b95ddc6d..25c48e630380cb00dbe58633c04c6439ee7aa77d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -167,6 +167,8 @@ import android.provider.Downloads; import android.provider.FontsContract; import android.provider.Settings; import android.renderscript.RenderScriptCacheDir; +import android.se.omapi.SeFrameworkInitializer; +import android.se.omapi.SeServiceManager; import android.security.NetworkSecurityPolicy; import android.security.net.config.NetworkSecurityConfigProvider; import android.system.ErrnoException; @@ -8402,8 +8404,8 @@ public final class ActivityThread extends ClientTransactionHandler BinderCallsStats.startForBluetooth(context); }); NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager()); - DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager()); + SeFrameworkInitializer.setSeServiceManager(new SeServiceManager()); } private void purgePendingResources() { diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2436e57b74bc168dd18ab02de50f57a172e91b12..958669ee58528ff49db732c9eb823671e88cc1b1 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -16,7 +16,11 @@ package android.nfc.cardemulation; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.nfc.Flags; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -29,6 +33,11 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; + +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ /** * The AidGroup class represents a group of Application Identifiers (AIDs). @@ -39,28 +48,30 @@ import java.util.List; * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements Parcelable { /** * The maximum number of AIDs that can be present in any one group. */ - public static final int MAX_NUM_AIDS = 256; + private static final int MAX_NUM_AIDS = 256; + + private static final String TAG = "AidGroup"; - static final String TAG = "AidGroup"; - @UnsupportedAppUsage - final List<String> aids; - @UnsupportedAppUsage - final String category; - @UnsupportedAppUsage - final String description; + private final List<String> mAids; + private final String mCategory; + @SuppressWarnings("unused") // Unused as of now, but part of the XML input. + private final String mDescription; /** * Creates a new AidGroup object. * - * @param aids The list of AIDs present in the group - * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + * @param aids list of AIDs present in the group + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} */ - public AidGroup(List<String> aids, String category) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public AidGroup(@NonNull List<String> aids, @Nullable String category) { if (aids == null || aids.size() == 0) { throw new IllegalArgumentException("No AIDS in AID group."); } @@ -73,45 +84,55 @@ public final class AidGroup implements Parcelable { } } if (isValidCategory(category)) { - this.category = category; + this.mCategory = category; } else { - this.category = CardEmulation.CATEGORY_OTHER; + this.mCategory = CardEmulation.CATEGORY_OTHER; } - this.aids = new ArrayList<String>(aids.size()); + this.mAids = new ArrayList<String>(aids.size()); for (String aid : aids) { - this.aids.add(aid.toUpperCase()); + this.mAids.add(aid.toUpperCase(Locale.US)); } - this.description = null; + this.mDescription = null; } - @UnsupportedAppUsage - AidGroup(String category, String description) { - this.aids = new ArrayList<String>(); - this.category = category; - this.description = description; + /** + * Creates a new AidGroup object. + * + * @param category category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT} + * @param description description of this group + */ + AidGroup(@NonNull String category, @NonNull String description) { + this.mAids = new ArrayList<String>(); + this.mCategory = category; + this.mDescription = description; } /** + * Returns the category of this group. * @return the category of this AID group */ - @UnsupportedAppUsage + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getCategory() { - return category; + return mCategory; } /** + * Returns the list of AIDs in this group. + * * @return the list of AIDs in this group */ - @UnsupportedAppUsage + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getAids() { - return aids; + return mAids; } @Override public String toString() { - StringBuilder out = new StringBuilder("Category: " + category + - ", AIDs:"); - for (String aid : aids) { + StringBuilder out = new StringBuilder("Category: " + mCategory + + ", AIDs:"); + for (String aid : mAids) { out.append(aid); out.append(", "); } @@ -119,41 +140,44 @@ public final class AidGroup implements Parcelable { } /** - * Dump debugging info as AidGroupProto + * Dump debugging info as AidGroupProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. * * @param proto the ProtoOutputStream to write to */ - public void dump(ProtoOutputStream proto) { - proto.write(AidGroupProto.CATEGORY, category); - for (String aid : aids) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ProtoOutputStream proto) { + proto.write(AidGroupProto.CATEGORY, mCategory); + for (String aid : mAids) { proto.write(AidGroupProto.AIDS, aid); } } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(category); - dest.writeInt(aids.size()); - if (aids.size() > 0) { - dest.writeStringList(aids); + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mCategory); + dest.writeInt(mAids.size()); + if (mAids.size() > 0) { + dest.writeStringList(mAids); } } - @UnsupportedAppUsage - public static final @android.annotation.NonNull Parcelable.Creator<AidGroup> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<AidGroup> CREATOR = new Parcelable.Creator<AidGroup>() { @Override public AidGroup createFromParcel(Parcel source) { - String category = source.readString(); + String category = source.readString8(); int listSize = source.readInt(); ArrayList<String> aidList = new ArrayList<String>(); if (listSize > 0) { @@ -168,8 +192,17 @@ public final class AidGroup implements Parcelable { } }; - @UnsupportedAppUsage - static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { + /** + * Create an instance of AID group from XML file. + * + * @param parser input xml parser stream + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public static AidGroup createFromXml(@NonNull XmlPullParser parser) + throws XmlPullParserException, IOException { String category = null; ArrayList<String> aids = new ArrayList<String>(); AidGroup group = null; @@ -210,11 +243,16 @@ public final class AidGroup implements Parcelable { return group; } - @UnsupportedAppUsage - public void writeAsXml(XmlSerializer out) throws IOException { + /** + * Serialize instance of AID group to XML file. + * @param out XML serializer stream + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void writeAsXml(@NonNull XmlSerializer out) throws IOException { out.startTag(null, "aid-group"); - out.attribute(null, "category", category); - for (String aid : aids) { + out.attribute(null, "category", mCategory); + for (String aid : mAids) { out.startTag(null, "aid"); out.attribute(null, "value", aid); out.endTag(null, "aid"); @@ -222,7 +260,7 @@ public final class AidGroup implements Parcelable { out.endTag(null, "aid-group"); } - static boolean isValidCategory(String category) { + private static boolean isValidCategory(String category) { return CardEmulation.CATEGORY_PAYMENT.equals(category) || CardEmulation.CATEGORY_OTHER.equals(category); } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 793a70e93f56c3cb3343ce4127623f10371a2a47..18ec914860fb77a982fdc7f9e6dd0deba72dce37 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -14,10 +14,16 @@ * limitations under the License. */ +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + package android.nfc.cardemulation; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -28,7 +34,9 @@ import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.nfc.Flags; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -38,7 +46,6 @@ import android.util.proto.ProtoOutputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -47,82 +54,82 @@ import java.util.List; import java.util.Map; /** + * Class holding APDU service info. + * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements Parcelable { - static final String TAG = "ApduServiceInfo"; + private static final String TAG = "ApduServiceInfo"; /** * The service that implements this */ - @UnsupportedAppUsage - final ResolveInfo mService; + private final ResolveInfo mService; /** * Description of the service */ - final String mDescription; + private final String mDescription; /** * Whether this service represents AIDs running on the host CPU */ - final boolean mOnHost; + private final boolean mOnHost; /** * Offhost reader name. * eg: SIM, eSE etc */ - String mOffHostName; + private String mOffHostName; /** * Offhost reader name from manifest file. - * Used for unsetOffHostSecureElement() + * Used for resetOffHostSecureElement() */ - final String mStaticOffHostName; + private final String mStaticOffHostName; /** * Mapping from category to static AID group */ - @UnsupportedAppUsage - final HashMap<String, AidGroup> mStaticAidGroups; + private final HashMap<String, AidGroup> mStaticAidGroups; /** * Mapping from category to dynamic AID group */ - @UnsupportedAppUsage - final HashMap<String, AidGroup> mDynamicAidGroups; + private final HashMap<String, AidGroup> mDynamicAidGroups; /** * Whether this service should only be started when the device is unlocked. */ - final boolean mRequiresDeviceUnlock; + private final boolean mRequiresDeviceUnlock; /** * Whether this service should only be started when the device is screen on. */ - final boolean mRequiresDeviceScreenOn; + private final boolean mRequiresDeviceScreenOn; /** * The id of the service banner specified in XML. */ - final int mBannerResourceId; + private final int mBannerResourceId; /** * The uid of the package the service belongs to */ - final int mUid; + private final int mUid; /** * Settings Activity for this service */ - final String mSettingsActivityName; + private final String mSettingsActivityName; /** * @hide */ - @UnsupportedAppUsage public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, + List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { this(info, onHost, description, staticAidGroups, dynamicAidGroups, @@ -134,7 +141,7 @@ public final class ApduServiceInfo implements Parcelable { * @hide */ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, + List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { this.mService = info; @@ -147,19 +154,28 @@ public final class ApduServiceInfo implements Parcelable { this.mRequiresDeviceUnlock = requiresUnlock; this.mRequiresDeviceScreenOn = requiresScreenOn; for (AidGroup aidGroup : staticAidGroups) { - this.mStaticAidGroups.put(aidGroup.category, aidGroup); + this.mStaticAidGroups.put(aidGroup.getCategory(), aidGroup); } for (AidGroup aidGroup : dynamicAidGroups) { - this.mDynamicAidGroups.put(aidGroup.category, aidGroup); + this.mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); } this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; } - @UnsupportedAppUsage - public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws - XmlPullParserException, IOException { + /** + * Creates a new ApduServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @param onHost whether service is on host or not (secure element) + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public ApduServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info, boolean onHost) + throws XmlPullParserException, IOException { ServiceInfo si = info.serviceInfo; XmlResourceParser parser = null; try { @@ -277,9 +293,9 @@ public final class ApduServiceInfo implements Parcelable { groupAttrs.recycle(); } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) && currentGroup != null) { - if (currentGroup.aids.size() > 0) { - if (!mStaticAidGroups.containsKey(currentGroup.category)) { - mStaticAidGroups.put(currentGroup.category, currentGroup); + if (currentGroup.getAids().size() > 0) { + if (!mStaticAidGroups.containsKey(currentGroup.getCategory())) { + mStaticAidGroups.put(currentGroup.getCategory(), currentGroup); } } else { Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs"); @@ -291,8 +307,8 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.AidFilter); String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). toUpperCase(); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -305,8 +321,8 @@ public final class ApduServiceInfo implements Parcelable { toUpperCase(); // Add wildcard char to indicate prefix aid = aid.concat("*"); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -319,8 +335,8 @@ public final class ApduServiceInfo implements Parcelable { toUpperCase(); // Add wildcard char to indicate suffix aid = aid.concat("#"); - if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { - currentGroup.aids.add(aid); + if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) { + currentGroup.getAids().add(aid); } else { Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); } @@ -336,11 +352,25 @@ public final class ApduServiceInfo implements Parcelable { mUid = si.applicationInfo.uid; } + /** + * Returns the app component corresponding to this APDU service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public ComponentName getComponent() { return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name); } + /** + * Returns the offhost secure element name (if the service is offhost). + * + * @return offhost secure element name for offhost services + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable public String getOffHostSecureElement() { return mOffHostName; } @@ -353,18 +383,30 @@ public final class ApduServiceInfo implements Parcelable { * for that category. * @return List of AIDs registered by the service */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getAids() { final ArrayList<String> aids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - aids.addAll(group.aids); + aids.addAll(group.getAids()); } return aids; } + /** + * Returns a consolidated list of AIDs with prefixes from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getPrefixAids() { final ArrayList<String> prefixAids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - for (String aid : group.aids) { + for (String aid : group.getAids()) { if (aid.endsWith("*")) { prefixAids.add(aid); } @@ -373,10 +415,20 @@ public final class ApduServiceInfo implements Parcelable { return prefixAids; } + /** + * Returns a consolidated list of AIDs with subsets from the AID groups + * registered by this service. Note that if a service has both + * a static (manifest-based) AID group for a category and a dynamic + * AID group, only the dynamically registered AIDs will be returned + * for that category. + * @return List of prefix AIDs registered by the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public List<String> getSubsetAids() { final ArrayList<String> subsetAids = new ArrayList<String>(); for (AidGroup group : getAidGroups()) { - for (String aid : group.aids) { + for (String aid : group.getAids()) { if (aid.endsWith("#")) { subsetAids.add(aid); } @@ -384,14 +436,28 @@ public final class ApduServiceInfo implements Parcelable { } return subsetAids; } + /** * Returns the registered AID group for this category. + * + * @param category category name + * @return {@link AidGroup} instance for the provided category */ - public AidGroup getDynamicAidGroupForCategory(String category) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public AidGroup getDynamicAidGroupForCategory(@NonNull String category) { return mDynamicAidGroups.get(category); } - public boolean removeDynamicAidGroupForCategory(String category) { + /** + * Removes the registered AID group for this category. + * + * @param category category name + * @return {@code true} if an AID group existed + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public boolean removeDynamicAidGroupForCategory(@NonNull String category) { return (mDynamicAidGroups.remove(category) != null); } @@ -403,7 +469,9 @@ public final class ApduServiceInfo implements Parcelable { * for that category. * @return List of AIDs registered by the service */ - public ArrayList<AidGroup> getAidGroups() { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public List<AidGroup> getAidGroups() { final ArrayList<AidGroup> groups = new ArrayList<AidGroup>(); for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) { groups.add(entry.getValue()); @@ -421,49 +489,83 @@ public final class ApduServiceInfo implements Parcelable { /** * Returns the category to which this service has attributed the AID that is passed in, * or null if we don't know this AID. + * @param aid AID to lookup for + * @return category name corresponding to this AID */ - public String getCategoryForAid(String aid) { - ArrayList<AidGroup> groups = getAidGroups(); + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public String getCategoryForAid(@NonNull String aid) { + List<AidGroup> groups = getAidGroups(); for (AidGroup group : groups) { - if (group.aids.contains(aid.toUpperCase())) { - return group.category; + if (group.getAids().contains(aid.toUpperCase())) { + return group.getCategory(); } } return null; } - public boolean hasCategory(String category) { + /** + * Returns whether there is any AID group for this category. + * @param category category name + * @return {@code true} if an AID group exists + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean hasCategory(@NonNull String category) { return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); } - @UnsupportedAppUsage + /** + * Returns whether the service is on host or not. + * @return true if the service is on host (not secure element) + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost() { return mOnHost; } - @UnsupportedAppUsage + /** + * Returns whether the service requires device unlock. + * @return whether the service requires device unlock + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock() { return mRequiresDeviceUnlock; } /** * Returns whether this service should only be started when the device is screen on. + * @return whether the service requires screen on */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn() { return mRequiresDeviceScreenOn; } - @UnsupportedAppUsage + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getDescription() { return mDescription; } - @UnsupportedAppUsage + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid() { return mUid; } - public void setOrReplaceDynamicAidGroup(AidGroup aidGroup) { + /** + * Add or replace an AID group to this service. + * @param aidGroup instance of aid group to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicAidGroup(@NonNull AidGroup aidGroup) { mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); } @@ -476,7 +578,8 @@ public final class ApduServiceInfo implements Parcelable { * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number] * (e.g. eSE/eSE1, eSE2, etc.). */ - public void setOffHostSecureElement(String offHost) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setOffHostSecureElement(@NonNull String offHost) { mOffHostName = offHost; } @@ -484,15 +587,30 @@ public final class ApduServiceInfo implements Parcelable { * Resets the off host Secure Element to statically defined * by the service in the manifest file. */ - public void unsetOffHostSecureElement() { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void resetOffHostSecureElement() { mOffHostName = mStaticOffHostName; } - public CharSequence loadLabel(PackageManager pm) { + /** + * Load label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { return mService.loadLabel(pm); } - public CharSequence loadAppLabel(PackageManager pm) { + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return app label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadAppLabel(@NonNull PackageManager pm) { try { return pm.getApplicationLabel(pm.getApplicationInfo( mService.resolvePackageName, PackageManager.GET_META_DATA)); @@ -501,12 +619,25 @@ public final class ApduServiceInfo implements Parcelable { } } - public Drawable loadIcon(PackageManager pm) { + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { return mService.loadIcon(pm); } - @UnsupportedAppUsage - public Drawable loadBanner(PackageManager pm) { + /** + * Load application banner for this service. + * @param pm packagemanager instance + * @return app banner corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadBanner(@NonNull PackageManager pm) { Resources res; try { res = pm.getResourcesForApplication(mService.serviceInfo.packageName); @@ -521,7 +652,12 @@ public final class ApduServiceInfo implements Parcelable { } } - @UnsupportedAppUsage + /** + * Load activity name for this service. + * @return activity name for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getSettingsActivityName() { return mSettingsActivityName; } @Override @@ -556,14 +692,15 @@ public final class ApduServiceInfo implements Parcelable { return getComponent().hashCode(); } - + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeInt(mOnHost ? 1 : 0); @@ -584,8 +721,8 @@ public final class ApduServiceInfo implements Parcelable { dest.writeString(mSettingsActivityName); }; - @UnsupportedAppUsage - public static final @android.annotation.NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = new Parcelable.Creator<ApduServiceInfo>() { @Override public ApduServiceInfo createFromParcel(Parcel source) { @@ -620,7 +757,15 @@ public final class ApduServiceInfo implements Parcelable { } }; - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + /** + * Dump contents for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")" + " (UID: " + getUid() + ")"); @@ -633,15 +778,15 @@ public final class ApduServiceInfo implements Parcelable { } pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { - pw.println(" Category: " + group.category); - for (String aid : group.aids) { + pw.println(" Category: " + group.getCategory()); + for (String aid : group.getAids()) { pw.println(" AID: " + aid); } } pw.println(" Dynamic AID groups:"); for (AidGroup group : mDynamicAidGroups.values()) { - pw.println(" Category: " + group.category); - for (String aid : group.aids) { + pw.println(" Category: " + group.getCategory()); + for (String aid : group.getAids()) { pw.println(" AID: " + aid); } } @@ -651,7 +796,7 @@ public final class ApduServiceInfo implements Parcelable { } /** - * Dump debugging info as ApduServiceInfoProto + * Dump debugging info as ApduServiceInfoProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. @@ -659,7 +804,8 @@ public final class ApduServiceInfo implements Parcelable { * * @param proto the ProtoOutputStream to write to */ - public void dumpDebug(ProtoOutputStream proto) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME); proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index 7a36b269240cd38578fee8751c671acf20f669b6..ec919e4d66bc334f80d42a5d5703f9744e62f8af 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -14,9 +14,16 @@ * limitations under the License. */ +/********************************************************************** + * This file is not a part of the NFC mainline module * + * *******************************************************************/ + package android.nfc.cardemulation; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -26,7 +33,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.nfc.Flags; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -36,13 +45,16 @@ import android.util.proto.ProtoOutputStream; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; /** + * Class to hold NfcF service info. + * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements Parcelable { static final String TAG = "NfcFServiceInfo"; @@ -51,42 +63,42 @@ public final class NfcFServiceInfo implements Parcelable { /** * The service that implements this */ - final ResolveInfo mService; + private final ResolveInfo mService; /** * Description of the service */ - final String mDescription; + private final String mDescription; /** * System Code of the service */ - final String mSystemCode; + private final String mSystemCode; /** * System Code of the service registered by API */ - String mDynamicSystemCode; + private String mDynamicSystemCode; /** * NFCID2 of the service */ - final String mNfcid2; + private final String mNfcid2; /** * NFCID2 of the service registered by API */ - String mDynamicNfcid2; + private String mDynamicNfcid2; /** * The uid of the package the service belongs to */ - final int mUid; + private final int mUid; /** * LF_T3T_PMM of the service */ - final String mT3tPmm; + private final String mT3tPmm; /** * @hide @@ -104,7 +116,16 @@ public final class NfcFServiceInfo implements Parcelable { this.mT3tPmm = t3tPmm; } - public NfcFServiceInfo(PackageManager pm, ResolveInfo info) + /** + * Creates a new NfcFServiceInfo object. + * + * @param pm packageManager instance + * @param info app component info + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public NfcFServiceInfo(@NonNull PackageManager pm, @NonNull ResolveInfo info) throws XmlPullParserException, IOException { ServiceInfo si = info.serviceInfo; XmlResourceParser parser = null; @@ -192,44 +213,107 @@ public final class NfcFServiceInfo implements Parcelable { mUid = si.applicationInfo.uid; } + /** + * Returns the app component corresponding to this NFCF service. + * + * @return app component for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public ComponentName getComponent() { return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name); } + /** + * Returns the system code corresponding to this service. + * + * @return system code for this service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getSystemCode() { return (mDynamicSystemCode == null ? mSystemCode : mDynamicSystemCode); } - public void setOrReplaceDynamicSystemCode(String systemCode) { + /** + * Add or replace a system code to this service. + * @param systemCode system code to set or replace + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicSystemCode(@NonNull String systemCode) { mDynamicSystemCode = systemCode; } + /** + * Returns NFC ID2. + * + * @return nfc id2 to return + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getNfcid2() { return (mDynamicNfcid2 == null ? mNfcid2 : mDynamicNfcid2); } - public void setOrReplaceDynamicNfcid2(String nfcid2) { + /** + * Set or replace NFC ID2 + * + * @param nfcid2 NFC ID2 string + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setDynamicNfcid2(@NonNull String nfcid2) { mDynamicNfcid2 = nfcid2; } + /** + * Returns description of service. + * @return user readable description of service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getDescription() { return mDescription; } + /** + * Returns uid of service. + * @return uid of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid() { return mUid; } + /** + * Returns LF_T3T_PMM of the service + * @return returns LF_T3T_PMM of the service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull public String getT3tPmm() { return mT3tPmm; } - public CharSequence loadLabel(PackageManager pm) { + /** + * Load application label for this service. + * @param pm packagemanager instance + * @return label name corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public CharSequence loadLabel(@NonNull PackageManager pm) { return mService.loadLabel(pm); } - public Drawable loadIcon(PackageManager pm) { + /** + * Load application icon for this service. + * @param pm packagemanager instance + * @return app icon corresponding to service + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public Drawable loadIcon(@NonNull PackageManager pm) { return mService.loadIcon(pm); } @@ -270,13 +354,15 @@ public final class NfcFServiceInfo implements Parcelable { return getComponent().hashCode(); } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override public int describeContents() { return 0; } + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeString(mSystemCode); @@ -293,7 +379,8 @@ public final class NfcFServiceInfo implements Parcelable { dest.writeString(mT3tPmm); }; - public static final @android.annotation.NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR = + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final @NonNull Parcelable.Creator<NfcFServiceInfo> CREATOR = new Parcelable.Creator<NfcFServiceInfo>() { @Override public NfcFServiceInfo createFromParcel(Parcel source) { @@ -322,7 +409,15 @@ public final class NfcFServiceInfo implements Parcelable { } }; - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + /** + * Dump contents of the service for debugging. + * @param fd parcelfiledescriptor instance + * @param pw printwriter instance + * @param args args for dumping + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dump(@NonNull ParcelFileDescriptor fd, @NonNull PrintWriter pw, + @NonNull String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")" + " (UID: " + getUid() + ")"); @@ -332,14 +427,15 @@ public final class NfcFServiceInfo implements Parcelable { } /** - * Dump debugging info as NfcFServiceInfoProto + * Dump debugging info as NfcFServiceInfoProto. * * If the output belongs to a sub message, the caller is responsible for wrapping this function * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. * * @param proto the ProtoOutputStream to write to */ - public void dumpDebug(ProtoOutputStream proto) { + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void dumpDebug(@NonNull ProtoOutputStream proto) { Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME); proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); @@ -347,4 +443,3 @@ public final class NfcFServiceInfo implements Parcelable { proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm()); } } - diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig new file mode 100644 index 0000000000000000000000000000000000000000..e3faf3978856cfc172c3d873880ea7580fb35f46 --- /dev/null +++ b/core/java/android/nfc/flags.aconfig @@ -0,0 +1,8 @@ +package: "android.nfc" + +flag { + name: "enable_nfc_mainline" + namespace: "nfc" + description: "Flag for NFC mainline changes" + bug: "292140387" +} diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index ecf17707a5c84efb446fb04a5006678f0b1027ea..dc024701fbef1ad499457e27c76cae506086433f 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -178,7 +178,7 @@ public abstract class PrintService extends Service { * <pre> <print-service * android:vendor="SomeVendor" * android:settingsActivity="foo.bar.MySettingsActivity" - * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity." + * android:addPrintersActivity="foo.bar.MyAddPrintersActivity." * . . . * /></pre> * <p> diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 052c28325545a2503a1059b96e9c1170e1710338..be6fb313b2304cf8f34522c1952caa7bef54e728 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -263,6 +263,8 @@ public final class SurfaceControl implements Parcelable { long nativeObject, int compatibility); private static native void nativeSetFrameRateCategory( long transactionObj, long nativeObject, int category); + private static native void nativeSetFrameRateSelectionStrategy( + long transactionObj, long nativeObject, int strategy); private static native long nativeGetHandle(long nativeObject); private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject, @@ -850,6 +852,35 @@ public final class SurfaceControl implements Parcelable { */ public static final int METADATA_GAME_MODE = 8; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"FRAME_RATE_SELECTION_STRATEGY_"}, + value = {FRAME_RATE_SELECTION_STRATEGY_SELF, + FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN}) + public @interface FrameRateSelectionStrategy {} + + // From window.h. Keep these in sync. + /** + * Default value. The layer uses its own frame rate specifications, assuming it has any + * specifications, instead of its parent's. + * However, {@link #FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} on an ancestor layer + * supersedes this behavior, meaning that this layer will inherit the frame rate specifications + * of that ancestor layer. + * @hide + */ + public static final int FRAME_RATE_SELECTION_STRATEGY_SELF = 0; + + /** + * The layer's frame rate specifications will propagate to and override those of its descendant + * layers. + * The layer with this strategy has the {@link #FRAME_RATE_SELECTION_STRATEGY_SELF} behavior + * for itself. This does mean that any parent or ancestor layer that also has the strategy + * {@link FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN} will override this layer's + * frame rate specifications. + * @hide + */ + public static final int FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1; + /** * Builder class for {@link SurfaceControl} objects. * @@ -3668,6 +3699,31 @@ public final class SurfaceControl implements Parcelable { return this; } + /** + * Sets the frame rate selection strategy for the {@link SurfaceControl}. + * + * This instructs the system on how to choose a display refresh rate, following the + * strategy for the layer's frame rate specifications relative to other layers'. + * + * @param sc The SurfaceControl to specify the frame rate category of. + * @param strategy The frame rate selection strategy. + * + * @return This transaction object. + * + * @see #setFrameRate(SurfaceControl, float, int, int) + * @see #setFrameRateCategory(SurfaceControl, int) + * @see #setDefaultFrameRateCompatibility(SurfaceControl, int) + * + * @hide + */ + @NonNull + public Transaction setFrameRateSelectionStrategy( + @NonNull SurfaceControl sc, @FrameRateSelectionStrategy int strategy) { + checkPreconditions(sc); + nativeSetFrameRateSelectionStrategy(mNativeObject, sc.mNativeObject, strategy); + return this; + } + /** * Sets focus on the window identified by the input {@code token} if the window is focusable * otherwise the request is dropped. diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java index 61470f2bc71a9da9bfa4b90e3c6078b05d4df8c2..3557f16a6dc888e641f24864068720866cdd0644 100644 --- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java +++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java @@ -417,7 +417,9 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { if (mBeamer == null) { return; } - mBeamer.forget(token); + dispatch(() -> { + mBeamer.forget(token); + }); } @Override diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index b8d251fc5cc5e8f46d56351cf2a799a404113ba4..ec5d4ff16ee5d63599865a2d45173046fa27ada0 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -15,3 +15,10 @@ flag { description: "Whether the overlay presentation feature is enabled" bug: "243518738" } + +flag { + namespace: "windowing_sdk" + name: "task_fragment_system_organizer_flag" + description: "Whether the TaskFragment system organizer feature is enabled" + bug: "284050041" +} diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 74f64b758ff78433f51836ccf9db868e3ccff5f0..9384f41e26f5049e29c7e5b172367957de2be0f6 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -967,6 +967,13 @@ static void nativeSetFrameRateCategory(JNIEnv* env, jclass clazz, jlong transact transaction->setFrameRateCategory(ctrl, static_cast<int8_t>(category)); } +static void nativeSetFrameRateSelectionStrategy(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jint strategy) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + transaction->setFrameRateSelectionStrategy(ctrl, static_cast<int8_t>(strategy)); +} + static void nativeSetFixedTransformHint(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint transformHint) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -2173,6 +2180,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDefaultFrameRateCompatibility}, {"nativeSetFrameRateCategory", "(JJI)V", (void*)nativeSetFrameRateCategory}, + {"nativeSetFrameRateSelectionStrategy", "(JJI)V", + (void*)nativeSetFrameRateSelectionStrategy}, {"nativeSetDisplaySurface", "(JLandroid/os/IBinder;J)V", (void*)nativeSetDisplaySurface }, {"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V", diff --git a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java index b5e5b258b7d6a43878f8d02ed64319108b44904d..cb988555d5f78f6ef96f9bf53d8a92c59e0087b0 100644 --- a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java +++ b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java @@ -364,6 +364,12 @@ public class ReadUtils { TestClassLoader loader = new TestClassLoader(); parcel.readParcelableArray(loader); }, + (parcel, provider) -> { + parcel.readParcelable(null); + }, + (parcel, provider) -> { + parcel.readParcelableArray(null); + }, // read lists (parcel, provider) -> { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index fe611a28dda970deb554498af5edc16a14c68970..b00fc699e515d2e212c941e3e8afe53760e8ce7e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -918,10 +918,10 @@ const SkM44& CanvasContext::getPixelSnapMatrix() const { } void CanvasContext::prepareAndDraw(RenderNode* node) { - ATRACE_CALL(); + int64_t vsyncId = mRenderThread.timeLord().lastVsyncId(); + ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos(); - int64_t vsyncId = mRenderThread.timeLord().lastVsyncId(); int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline(); int64_t frameInterval = mRenderThread.timeLord().frameIntervalNanos(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; diff --git a/omapi/java/android/se/omapi/SeFrameworkInitializer.java b/omapi/java/android/se/omapi/SeFrameworkInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..d09a35d85009ff37aeb4ff939437a1b0a574f3aa --- /dev/null +++ b/omapi/java/android/se/omapi/SeFrameworkInitializer.java @@ -0,0 +1,70 @@ +/* + * 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 android.se.omapi; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.nfc.Flags; + +/** + * Class for performing registration for SE service. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) +public class SeFrameworkInitializer { + private SeFrameworkInitializer() {} + + private static volatile SeServiceManager sSeServiceManager; + + /** + * Sets an instance of {@link SeServiceManager} that allows + * the se mainline module to register/obtain se binder services. This is called + * by the platform during the system initialization. + * + * @param seServiceManager instance of {@link SeServiceManager} that allows + * the se/nfc mainline module to register/obtain se binder services. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static void setSeServiceManager( + @NonNull SeServiceManager seServiceManager) { + if (sSeServiceManager != null) { + throw new IllegalStateException("setSeServiceManager called twice!"); + } + + if (seServiceManager == null) { + throw new IllegalArgumentException("seServiceManager must not be null"); + } + + sSeServiceManager = seServiceManager; + } + + /** + * Gets an instance of {@link SeServiceManager} that allows + * the se mainline module to register/obtain se binder services. + * + * @return instance of {@link SeServiceManager} that allows + * the se/nfc mainline module to register/obtain se binder services. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public static SeServiceManager getSeServiceManager() { + return sSeServiceManager; + } +} diff --git a/omapi/java/android/se/omapi/SeServiceManager.java b/omapi/java/android/se/omapi/SeServiceManager.java new file mode 100644 index 0000000000000000000000000000000000000000..7910737067529ffef77123614748b04215ae9d67 --- /dev/null +++ b/omapi/java/android/se/omapi/SeServiceManager.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + + +/********************************************************************** + * This file is not a part of the SE mainline module * + * *******************************************************************/ + +package android.se.omapi; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.SystemApi.Client; +import android.content.Context; +import android.nfc.Flags; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Provides a way to register and obtain the system service binder objects managed by the + * SecureElement service. + * + * @hide + */ +@SystemApi(client = Client.MODULE_LIBRARIES) +@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) +public class SeServiceManager { + + /** + * @hide + */ + public SeServiceManager() { + } + + /** + * A class that exposes the methods to register and obtain each system service. + * @hide + */ + @SystemApi(client = Client.MODULE_LIBRARIES) + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Register a system server binding object for a service. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void register(@NonNull IBinder service) { + ServiceManager.addService(mServiceName, service); + } + + /** + * Get the system server binding object for a service. + * + * <p>This blocks until the service instance is ready, + * or a timeout happens, in which case it returns null. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + + /** + * Get the system server binding object for a service. + * + * <p>This blocks until the service instance is ready, + * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public IBinder getOrThrow() throws ServiceNotFoundException { + try { + return ServiceManager.getServiceOrThrow(mServiceName); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new ServiceNotFoundException(mServiceName); + } + } + + /** + * Get the system server binding object for a service. If the specified service is + * not available, it returns null. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @Nullable + public IBinder tryGet() { + return ServiceManager.checkService(mServiceName); + } + } + + /** + * See {@link ServiceRegisterer#getOrThrow}. + * @hide + */ + @SystemApi(client = Client.MODULE_LIBRARIES) + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { + /** + * Constructor. + * + * @param name the name of the binder service that cannot be found. + * + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public ServiceNotFoundException(@NonNull String name) { + super(name); + } + } + + /** + * Returns {@link ServiceRegisterer} for the "secure_element" service. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + @NonNull + public ServiceRegisterer getSeManagerServiceRegisterer() { + return new ServiceRegisterer(Context.SECURE_ELEMENT_SERVICE); + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 0a98032e26d832ab1260bc38e0c05bd3be35936a..e9533e598336ae745d0257bdf9ea40204cd4dc3e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -137,6 +137,13 @@ final class SettingsState { private static final String ATTR_VALUE_BASE64 = "valueBase64"; private static final String ATTR_DEFAULT_VALUE_BASE64 = "defaultValueBase64"; + /** + * In the config table, there are special flags of the form {@code staged/namespace*flagName}. + * On boot, when the XML file is initially parsed, these transform into + * {@code namespace/flagName}, and the special staged flags are deleted. + */ + private static final String CONFIG_STAGED_PREFIX = "staged/"; + // This was used in version 120 and before. private static final String NULL_VALUE_OLD_STYLE = "null"; @@ -1191,6 +1198,42 @@ final class SettingsState { } } + /** + * Transforms a staged flag name to its real flag name. + * + * Staged flags take the form {@code staged/namespace*flagName}. If + * {@code stagedFlagName} takes the proper form, returns + * {@code namespace/flagName}. Otherwise, returns {@code stagedFlagName} + * unmodified, and logs an error message. + * + */ + @VisibleForTesting + public static String createRealFlagName(String stagedFlagName) { + int slashIndex = stagedFlagName.indexOf("/"); + if (slashIndex == -1 || slashIndex == stagedFlagName.length() - 1 + || slashIndex == 0) { + Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); + return stagedFlagName; + } + + String namespaceAndFlag = + stagedFlagName.substring(slashIndex + 1); + + int starIndex = namespaceAndFlag.indexOf("*"); + if (starIndex == -1 || starIndex == namespaceAndFlag.length() - 1 + || starIndex == 0) { + Slog.w(LOG_TAG, "invalid staged flag, not applying: " + stagedFlagName); + return stagedFlagName; + } + + String namespace = + namespaceAndFlag.substring(0, starIndex); + String flagName = + namespaceAndFlag.substring(starIndex + 1); + + return namespace + "/" + flagName; + } + @GuardedBy("mLock") private void parseSettingsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { @@ -1199,6 +1242,7 @@ final class SettingsState { final int outerDepth = parser.getDepth(); int type; + HashSet<String> flagsWithStagedValueApplied = new HashSet<String>(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -1221,6 +1265,18 @@ final class SettingsState { fromSystem = parser.getAttributeBoolean(null, ATTR_DEFAULT_SYS_SET, false); tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64); } + + if (isConfigSettingsKey(mKey)) { + if (flagsWithStagedValueApplied.contains(name)) { + continue; + } + + if (name.startsWith(CONFIG_STAGED_PREFIX)) { + name = createRealFlagName(name); + flagsWithStagedValueApplied.add(name); + } + } + mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, fromSystem, id, isPreservedInRestore)); @@ -1229,6 +1285,16 @@ final class SettingsState { } } } + + if (isConfigSettingsKey(mKey) && !flagsWithStagedValueApplied.isEmpty()) { + // On boot, the config table XML file includes special staged flags. On the initial + // boot XML -> HashMap parse, these staged flags get transformed into real flags. + // After this, the HashMap contains no special staged flags (only the transformed + // real flags), but the XML still does. We then have no need for the special staged + // flags in the XML, so we overwrite the XML with the latest contents of the + // HashMap. + writeStateAsyncLocked(); + } } @GuardedBy("mLock") diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 26cac37f8b354aeed08af66cf1fe5c52498a9847..df4d2a1ed89f1b7b963e7644e5ab6b63013ba948 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -51,6 +51,21 @@ public class SettingsStateTest extends AndroidTestCase { private static final String SYSTEM_PACKAGE = "android"; private static final String SETTING_NAME = "test_setting"; + private static final String FLAG_NAME_1 = "namespace123/flag456"; + private static final String FLAG_NAME_1_STAGED = "staged/namespace123*flag456"; + private static final String FLAG_NAME_2 = "not_staged/flag101"; + + private static final String INVALID_STAGED_FLAG_1 = "stagednamespace*flagName"; + private static final String INVALID_STAGED_FLAG_2 = "staged/"; + private static final String INVALID_STAGED_FLAG_3 = "staged/namespace*"; + private static final String INVALID_STAGED_FLAG_4 = "staged/*flagName"; + + private static final String VALID_STAGED_FLAG_1 = "staged/namespace*flagName"; + private static final String VALID_STAGED_FLAG_1_TRANSFORMED = "namespace/flagName"; + + private static final String VALUE1 = "5"; + private static final String VALUE2 = "6"; + private final Object mLock = new Object(); private File mSettingsFile; @@ -454,4 +469,68 @@ public class SettingsStateTest extends AndroidTestCase { } } } + + public void testApplyStagedConfigValues() { + int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + Object lock = new Object(); + SettingsState settingsState = new SettingsState( + getContext(), lock, mSettingsFile, configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + + synchronized (lock) { + settingsState.insertSettingLocked( + FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE); + settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE); + settingsState.persistSyncLocked(); + + assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); + assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); + } + + settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + + synchronized (lock) { + assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1).getValue()); + assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue()); + + assertEquals(null, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue()); + } + } + + public void testStagingTransformation() { + assertEquals(INVALID_STAGED_FLAG_1, + SettingsState.createRealFlagName(INVALID_STAGED_FLAG_1)); + assertEquals(INVALID_STAGED_FLAG_2, + SettingsState.createRealFlagName(INVALID_STAGED_FLAG_2)); + assertEquals(INVALID_STAGED_FLAG_3, + SettingsState.createRealFlagName(INVALID_STAGED_FLAG_3)); + assertEquals(INVALID_STAGED_FLAG_4, + SettingsState.createRealFlagName(INVALID_STAGED_FLAG_4)); + + assertEquals(VALID_STAGED_FLAG_1_TRANSFORMED, + SettingsState.createRealFlagName(VALID_STAGED_FLAG_1)); + } + + public void testInvalidStagedFlagsUnaffectedByReboot() { + int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + Object lock = new Object(); + SettingsState settingsState = new SettingsState( + getContext(), lock, mSettingsFile, configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + + synchronized (lock) { + settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1, + VALUE2, null, false, TEST_PACKAGE); + settingsState.persistSyncLocked(); + assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); + } + + settingsState = new SettingsState(getContext(), lock, mSettingsFile, configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + + synchronized (lock) { + assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue()); + } + } } diff --git a/packages/SystemUI/aconfig/OWNERS b/packages/SystemUI/aconfig/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..e1a7a0f84ad002471732dd27fa7a40b56f0bfa26 --- /dev/null +++ b/packages/SystemUI/aconfig/OWNERS @@ -0,0 +1 @@ +per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt index 8a8557aa1f436fc4e94368ffe5c1e23587bfaff1..df22a7023ebfafe19caf9dfe758da1a883279015 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt @@ -17,8 +17,11 @@ package com.android.systemui.bouncer.ui.composable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imeAnimationTarget import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.LocalTextStyle @@ -29,6 +32,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind @@ -44,6 +48,7 @@ import androidx.compose.ui.unit.dp import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel /** UI for the input part of a password-requiring version of the bouncer. */ +@OptIn(ExperimentalLayoutApi::class) @Composable internal fun PasswordBouncer( viewModel: PasswordBouncerViewModel, @@ -54,6 +59,10 @@ internal fun PasswordBouncer( val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() val animateFailure: Boolean by viewModel.animateFailure.collectAsState() + val density = LocalDensity.current + val isImeVisible by rememberUpdatedState(WindowInsets.imeAnimationTarget.getBottom(density) > 0) + LaunchedEffect(isImeVisible) { viewModel.onImeVisibilityChanged(isImeVisible) } + LaunchedEffect(Unit) { // When the UI comes up, request focus on the TextField to bring up the software keyboard. focusRequester.requestFocus() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 199036f09e9a10170599e50a748d758db3923d45..9b2f2baba94bde8fcec02e1c3229654331894a76 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -225,6 +225,20 @@ constructor( repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod())) } + /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */ + fun hide( + loggingReason: String, + ) { + if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) { + return + } + + sceneInteractor.changeScene( + scene = SceneModel(SceneKey.Lockscreen), + loggingReason = loggingReason, + ) + } + private fun promptMessage(authMethod: AuthenticationMethodModel): String { return when (authMethod) { is AuthenticationMethodModel.Pin -> diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index d95b70c85fe0f5f3f2781a4a5bf9374ccf8c7956..4546bea3b89b09271e9c2217b4a27ca810bf537a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.bouncer.ui.viewmodel +import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,6 +29,7 @@ sealed class AuthMethodBouncerViewModel( * being able to attempt to unlock the device. */ val isInputEnabled: StateFlow<Boolean>, + private val interactor: BouncerInteractor, ) { private val _animateFailure = MutableStateFlow(false) @@ -37,6 +39,9 @@ sealed class AuthMethodBouncerViewModel( */ val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow() + /** Whether the input method editor (for example, the software keyboard) is visible. */ + private var isImeVisible: Boolean = false + /** * Notifies that the failure animation has been shown. This should be called to consume a `true` * value in [animateFailure]. @@ -45,6 +50,21 @@ sealed class AuthMethodBouncerViewModel( _animateFailure.value = false } + /** + * Notifies that the input method editor (for example, the software keyboard) has been shown or + * hidden. + */ + fun onImeVisibilityChanged(isVisible: Boolean) { + if (isImeVisible && !isVisible) { + // The IME has gone from visible to invisible, dismiss the bouncer. + interactor.hide( + loggingReason = "IME hidden", + ) + } + + isImeVisible = isVisible + } + /** Ask the UI to show the failure animation. */ protected fun showFailureAnimation() { _animateFailure.value = true diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index d21479746744d91e8d2ae05f2c303ddbea2ee327..9e10f29a00f94c54c87bcbe38c8db160452ae938 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -31,6 +31,7 @@ class PasswordBouncerViewModel( ) : AuthMethodBouncerViewModel( isInputEnabled = isInputEnabled, + interactor = interactor, ) { private val _password = MutableStateFlow("") @@ -60,6 +61,10 @@ class PasswordBouncerViewModel( /** Notifies that the user has pressed the key for attempting to authenticate the password. */ fun onAuthenticateKeyPressed() { val password = _password.value.toCharArray().toList() + if (password.isEmpty()) { + return + } + _password.value = "" applicationScope.launch { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 1985c37e1d5da20ce64ece6803da134088c58a79..497276b479960d738e1b155e2e9f65dbd9164fa2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -42,6 +42,7 @@ class PatternBouncerViewModel( ) : AuthMethodBouncerViewModel( isInputEnabled = isInputEnabled, + interactor = interactor, ) { /** The number of columns in the dot grid. */ diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index dc5c5288df9f245642537b82dc319c66df4d113d..8e6421ed3f0ab1bc348d7b2917dc2aa26aa5ce60 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -37,6 +37,7 @@ class PinBouncerViewModel( ) : AuthMethodBouncerViewModel( isInputEnabled = isInputEnabled, + interactor = interactor, ) { val pinShapes = PinShapeAdapter(applicationContext) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index f97f6bd7613b35966f6bdfd52b26e9aede294460..e170849e487fea64bc915210fa6d0b8d0c43ddac 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -386,7 +386,8 @@ object Flags { // 600- status bar // TODO(b/291315866): Tracking Bug - @JvmField val SIGNAL_CALLBACK_DEPRECATION = unreleasedFlag("signal_callback_deprecation") + @JvmField val SIGNAL_CALLBACK_DEPRECATION = + unreleasedFlag("signal_callback_deprecation", teamfood = true) // TODO(b/265892345): Tracking Bug val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip") diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 6205c277bbd900c68973ab8b989178da8de096bf..77d8102fff2e4a4d40ef87a73f7594f6a8d1d42f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -353,6 +353,34 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } + @Test + fun hide_whenOnBouncerScene_hidesBouncerAndGoesToLockscreenScene() = + testScope.runTest { + sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "") + val currentScene by collectLastValue(sceneInteractor.desiredScene) + val bouncerSceneKey = currentScene?.key + assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer) + + underTest.hide("") + + assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun hide_whenNotOnBouncerScene_doesNothing() = + testScope.runTest { + sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "") + val currentScene by collectLastValue(sceneInteractor.desiredScene) + val notBouncerSceneKey = currentScene?.key + assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer) + + underTest.hide("") + + assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey) + } + private fun assertTryAgainMessage( message: String?, time: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 7af8a042540203fe131eaf08ff62060bf4eee868..9011c2f296c3753694e57a164cb5640880c03cc7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -22,6 +22,8 @@ import com.android.systemui.authentication.data.model.AuthenticationMethodModel import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest @@ -39,6 +41,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { utils.authenticationInteractor( utils.authenticationRepository(), ) + private val sceneInteractor = utils.sceneInteractor() private val underTest = PinBouncerViewModel( applicationContext = context, @@ -46,7 +49,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { interactor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = utils.sceneInteractor(), + sceneInteractor = sceneInteractor, ), isInputEnabled = MutableStateFlow(true), ) @@ -75,4 +78,22 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() assertThat(animateFailure).isFalse() } + + @Test + fun onImeVisibilityChanged() = + testScope.runTest { + val desiredScene by collectLastValue(sceneInteractor.desiredScene) + sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "") + assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer) + + underTest.onImeVisibilityChanged(false) + assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer) + + underTest.onImeVisibilityChanged(true) + assertThat(desiredScene?.key).isEqualTo(SceneKey.Bouncer) + + underTest.onImeVisibilityChanged(false) + assertThat(desiredScene?.key).isEqualTo(SceneKey.Lockscreen) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 8df29e4d4e585a2237a54cad5da00b9d4b8ba8c6..3375184c1cf6003f8401e2065840f63adb2ed210 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -156,6 +156,29 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } + @Test + fun onAuthenticateKeyPressed_whenEmpty() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.desiredScene) + val message by collectLastValue(bouncerViewModel.message) + val password by collectLastValue(underTest.password) + utils.authenticationRepository.setAuthenticationMethod( + AuthenticationMethodModel.Password + ) + utils.authenticationRepository.setUnlocked(false) + sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason") + sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason") + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + // Enter nothing. + + underTest.onAuthenticateKeyPressed() + + assertThat(password).isEqualTo("") + assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + @Test fun onAuthenticateKeyPressed_correctAfterWrong() = testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 9c8d14de1c74d69cce4c7310e31ab9aced584741..6b918c6ba15b5c5ea40f5b83e7b101ab57f46046 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -22,7 +22,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel -import com.android.systemui.authentication.domain.model.AuthenticationMethodModel import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.shared.model.WakefulnessState @@ -48,7 +47,9 @@ import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -159,6 +160,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { repository = keyguardRepository, ) + private var bouncerSceneJob: Job? = null + @Before fun setUp() { shadeHeaderViewModel = @@ -288,7 +291,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { @Test fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() = testScope.runTest { - setAuthMethod(AuthenticationMethodModel.None) + setAuthMethod(DomainLayerAuthenticationMethodModel.None) putDeviceToSleep(instantlyLockDevice = false) assertCurrentScene(SceneKey.Lockscreen) @@ -299,7 +302,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { @Test fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() = testScope.runTest { - setAuthMethod(AuthenticationMethodModel.Swipe) + setAuthMethod(DomainLayerAuthenticationMethodModel.Swipe) putDeviceToSleep(instantlyLockDevice = false) assertCurrentScene(SceneKey.Lockscreen) @@ -364,6 +367,23 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertCurrentScene(SceneKey.Lockscreen) } + @Test + fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() = + testScope.runTest { + setAuthMethod(DomainLayerAuthenticationMethodModel.Password) + val upDestinationSceneKey by + collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey) + assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer) + emulateUserDrivenTransition( + to = upDestinationSceneKey, + ) + + dismissIme() + + assertCurrentScene(SceneKey.Lockscreen) + emulateUiSceneTransition() + } + /** * Asserts that the current scene in the view-model matches what's expected. * @@ -396,7 +416,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { // Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the // lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit // is not an observable that can trigger a new evaluation. - authenticationRepository.setLockscreenEnabled(authMethod !is AuthenticationMethodModel.None) + authenticationRepository.setLockscreenEnabled( + authMethod !is DomainLayerAuthenticationMethodModel.None + ) authenticationRepository.setAuthenticationMethod(authMethod.toDataLayer()) if (!authMethod.isSecure) { // When the auth method is not secure, the device is never considered locked. @@ -455,6 +477,19 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { assertWithMessage("Visibility mismatch after scene transition from $from to ${to.key}!") .that(sceneContainerViewModel.isVisible.value) .isEqualTo(expectedVisible) + + bouncerSceneJob = + if (to.key == SceneKey.Bouncer) { + testScope.backgroundScope.launch { + bouncerViewModel.authMethod.collect { + // Do nothing. Need this to turn this otherwise cold flow, hot. + } + } + } else { + bouncerSceneJob?.cancel() + null + } + runCurrent() } /** @@ -573,4 +608,16 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { lockDevice() } } + + /** Emulates the dismissal of the IME (soft keyboard). */ + private fun TestScope.dismissIme( + showImeBeforeDismissing: Boolean = true, + ) { + if (showImeBeforeDismissing) { + bouncerViewModel.authMethod.value?.onImeVisibilityChanged(true) + } + + bouncerViewModel.authMethod.value?.onImeVisibilityChanged(false) + runCurrent() + } } diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java index ffb4632164a385ae0c3803bb0749ab9fa566e46b..db2e18a0fd578775884618889b63e472ae6dc0b7 100644 --- a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java @@ -17,12 +17,19 @@ package com.android.server.autofill; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN; import static com.android.server.autofill.Helper.sVerbose; +import android.annotation.IntDef; import android.util.Slog; import com.android.internal.util.FrameworkStatsLog; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Optional; /** @@ -36,6 +43,29 @@ public final class FieldClassificationEventLogger { mEventInternal = Optional.empty(); } + public static final int STATUS_SUCCESS = + AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS; + public static final int STATUS_UNKNOWN = + AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN; + public static final int STATUS_FAIL = + AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL; + public static final int STATUS_CANCELLED = + AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED; + + /** + * Status of the FieldClassification IPC request. These are wrappers around + * {@link com.android.os.AtomsProto.AutofillFieldClassificationEventReported.FieldClassificationRequestStatus}. + */ + @IntDef(prefix = {"STATUS"}, value = { + STATUS_UNKNOWN, + STATUS_SUCCESS, + STATUS_FAIL, + STATUS_CANCELLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FieldClassificationStatus { + } + /** * A factory constructor to create FieldClassificationEventLogger. */ @@ -56,7 +86,7 @@ public final class FieldClassificationEventLogger { } /** - * Set latency as long as mEventInternal presents. + * Set latency_millis as long as mEventInternal presents. */ public void maybeSetLatencyMillis(long timestamp) { mEventInternal.ifPresent(event -> { @@ -64,6 +94,69 @@ public final class FieldClassificationEventLogger { }); } + /** + * Set count_classifications as long as mEventInternal presents. + */ + public void maybeSetCountClassifications(int countClassifications) { + mEventInternal.ifPresent(event -> { + event.mCountClassifications = countClassifications; + }); + } + + /** + * Set session_id as long as mEventInternal presents. + */ + public void maybeSetSessionId(int sessionId) { + mEventInternal.ifPresent(event -> { + event.mSessionId = sessionId; + }); + } + + /** + * Set request_id as long as mEventInternal presents. + */ + public void maybeSetRequestId(int requestId) { + mEventInternal.ifPresent(event -> { + event.mRequestId = requestId; + }); + } + + /** + * Set next_fill_request_id as long as mEventInternal presents. + */ + public void maybeSetNextFillRequestId(int nextFillRequestId) { + mEventInternal.ifPresent(event -> { + event.mNextFillRequestId = nextFillRequestId; + }); + } + + /** + * Set app_package_uid as long as mEventInternal presents. + */ + public void maybeSetAppPackageUid(int uid) { + mEventInternal.ifPresent(event -> { + event.mAppPackageUid = uid; + }); + } + + /** + * Set status as long as mEventInternal presents. + */ + public void maybeSetRequestStatus(@FieldClassificationStatus int status) { + mEventInternal.ifPresent(event -> { + event.mStatus = status; + }); + } + + /** + * Set is_session_gc as long as mEventInternal presents. + */ + public void maybeSetSessionGc(boolean isSessionGc) { + mEventInternal.ifPresent(event -> { + event.mIsSessionGc = isSessionGc; + }); + } + /** * Log an AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED event. */ @@ -77,16 +170,37 @@ public final class FieldClassificationEventLogger { if (sVerbose) { Slog.v(TAG, "Log AutofillFieldClassificationEventReported:" + " mLatencyClassificationRequestMillis=" - + event.mLatencyClassificationRequestMillis); + + event.mLatencyClassificationRequestMillis + + " mCountClassifications=" + event.mCountClassifications + + " mSessionId=" + event.mSessionId + + " mRequestId=" + event.mRequestId + + " mNextFillRequestId=" + event.mNextFillRequestId + + " mAppPackageUid=" + event.mAppPackageUid + + " mStatus=" + event.mStatus + + " mIsSessionGc=" + event.mIsSessionGc); } FrameworkStatsLog.write( AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED, - event.mLatencyClassificationRequestMillis); + event.mLatencyClassificationRequestMillis, + event.mCountClassifications, + event.mSessionId, + event.mRequestId, + event.mNextFillRequestId, + event.mAppPackageUid, + event.mStatus, + event.mIsSessionGc); mEventInternal = Optional.empty(); } private static final class FieldClassificationEventInternal { long mLatencyClassificationRequestMillis = -1; + int mCountClassifications = -1; + int mSessionId = -1; + int mRequestId = -1; + int mNextFillRequestId = -1; + int mAppPackageUid = -1; + int mStatus; + boolean mIsSessionGc; FieldClassificationEventInternal() { } diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java index 11b45db29d5d0f5d2b008465a0349bdd6914257d..6b0fdb5f7fe0542cc3f1ef1812a991412f01f294 100644 --- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java @@ -211,15 +211,25 @@ public final class PresentationStatsEventLogger { public static final int DETECTION_PREFER_PCC = AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC; private final int mSessionId; + + /** + * For app_package_uid. + */ + private final int mCallingAppUid; private Optional<PresentationStatsEventInternal> mEventInternal; - private PresentationStatsEventLogger(int sessionId) { + private PresentationStatsEventLogger(int sessionId, int callingAppUid) { mSessionId = sessionId; + mCallingAppUid = callingAppUid; mEventInternal = Optional.empty(); } - public static PresentationStatsEventLogger forSessionId(int sessionId) { - return new PresentationStatsEventLogger(sessionId); + /** + * Create PresentationStatsEventLogger, populated with sessionId and the callingAppUid + */ + public static PresentationStatsEventLogger createPresentationLog( + int sessionId, int callingAppUid) { + return new PresentationStatsEventLogger(sessionId, callingAppUid); } public void startNewEvent() { @@ -508,6 +518,14 @@ public final class PresentationStatsEventLogger { return PICK_REASON_UNKNOWN; } + /** + * Set field_classification_request_id as long as mEventInternal presents. + */ + public void maybeSetFieldClassificationRequestId(int requestId) { + mEventInternal.ifPresent(event -> { + event.mFieldClassificationRequestId = requestId; + }); + } public void logAndEndEvent() { if (!mEventInternal.isPresent()) { @@ -547,7 +565,9 @@ public final class PresentationStatsEventLogger { + " mAvailablePccCount=" + event.mAvailablePccCount + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason - + " mDetectionPreference=" + event.mDetectionPreference); + + " mDetectionPreference=" + event.mDetectionPreference + + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId + + " mAppPackageUid=" + mCallingAppUid); } // TODO(b/234185326): Distinguish empty responses from other no presentation reasons. @@ -584,7 +604,9 @@ public final class PresentationStatsEventLogger { event.mAvailablePccCount, event.mAvailablePccOnlyCount, event.mSelectedDatasetPickedReason, - event.mDetectionPreference); + event.mDetectionPreference, + event.mFieldClassificationRequestId, + mCallingAppUid); mEventInternal = Optional.empty(); } @@ -617,6 +639,7 @@ public final class PresentationStatsEventLogger { int mAvailablePccOnlyCount = -1; @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN; @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN; + int mFieldClassificationRequestId = -1; PresentationStatsEventInternal() {} } diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java index bcca0069eb349a9e4aafb7daab8c7075caa6f84c..50fabfdaef83a88d8a8051fa2f8e6e200724addd 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java @@ -69,6 +69,9 @@ final class RemoteFieldClassificationService void onClassificationRequestFailure(int requestId, @Nullable CharSequence message); void onClassificationRequestTimeout(int requestId); void onServiceDied(@NonNull RemoteFieldClassificationService service); + void logFieldClassificationEvent( + long startTime, @NonNull FieldClassificationResponse response, + @FieldClassificationEventLogger.FieldClassificationStatus int status); } RemoteFieldClassificationService(Context context, ComponentName serviceName, @@ -149,15 +152,24 @@ final class RemoteFieldClassificationService new IFieldClassificationCallback.Stub() { @Override public void onCancellable(ICancellationSignal cancellation) { - logLatency(startTime); if (sDebug) { Log.d(TAG, "onCancellable"); } + FieldClassificationServiceCallbacks + fieldClassificationServiceCallbacks = + Helper.weakDeref( + fieldClassificationServiceCallbacksWeakRef, + TAG, "onCancellable " + ); + logFieldClassificationEvent( + startTime, + fieldClassificationServiceCallbacks, + FieldClassificationEventLogger.STATUS_CANCELLED, + null); } @Override public void onSuccess(FieldClassificationResponse response) { - logLatency(startTime); if (sDebug) { if (Build.IS_DEBUGGABLE) { Slog.d(TAG, "onSuccess Response: " + response); @@ -179,6 +191,11 @@ final class RemoteFieldClassificationService fieldClassificationServiceCallbacksWeakRef, TAG, "onSuccess " ); + logFieldClassificationEvent( + startTime, + fieldClassificationServiceCallbacks, + FieldClassificationEventLogger.STATUS_SUCCESS, + response); if (fieldClassificationServiceCallbacks == null) { return; } @@ -188,7 +205,6 @@ final class RemoteFieldClassificationService @Override public void onFailure() { - logLatency(startTime); if (sDebug) { Slog.d(TAG, "onFailure"); } @@ -198,6 +214,11 @@ final class RemoteFieldClassificationService fieldClassificationServiceCallbacksWeakRef, TAG, "onFailure " ); + logFieldClassificationEvent( + startTime, + fieldClassificationServiceCallbacks, + FieldClassificationEventLogger.STATUS_FAIL, + null); if (fieldClassificationServiceCallbacks == null) { return; } @@ -215,11 +236,24 @@ final class RemoteFieldClassificationService })); } - private void logLatency(long startTime) { - final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger(); - logger.startNewLogForRequest(); - logger.maybeSetLatencyMillis( - SystemClock.elapsedRealtime() - startTime); - logger.logAndEndEvent(); + private void logFieldClassificationEvent( + long startTime, + @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks, + @FieldClassificationEventLogger.FieldClassificationStatus int status, + FieldClassificationResponse response) { + if (fieldClassificationServiceCallbacks == null) { + final FieldClassificationEventLogger logger = + FieldClassificationEventLogger.createLogger(); + logger.startNewLogForRequest(); + logger.maybeSetLatencyMillis( + SystemClock.elapsedRealtime() - startTime); + logger.maybeSetSessionGc(true); + logger.maybeSetRequestStatus(status); + logger.logAndEndEvent(); + } else { + fieldClassificationServiceCallbacks.logFieldClassificationEvent( + startTime, response, status); + } + } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 1ae912544cc8a28723f2b70cdd4fa3d8c70d0719..4e5b0589446a1710357d68b8a7a8510f664b6005 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -229,6 +229,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private static final String PCC_HINTS_DELIMITER = ","; public static final String EXTRA_KEY_DETECTIONS = "detections"; + private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2; + private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2; final Object mLock; @@ -412,6 +414,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private long mUiShownTime; + /** + * Tracks the value of the fill request id at the time of issuing request for field + * classification. + */ + @GuardedBy("mLock") + private int mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT; + + /** + * Tracks the value of the field classification id at the time of issuing request for fill + * request. + */ + @GuardedBy("mLock") + private int mFieldClassificationIdSnapshot = DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT; + @GuardedBy("mLock") private final LocalLog mUiLatencyHistory; @@ -673,6 +689,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mPendingFillRequest == null) { return; } + mFieldClassificationIdSnapshot = sIdCounterForPcc.get(); if (mWaitForInlineRequest) { if (mPendingInlineSuggestionsRequest == null) { @@ -1239,6 +1256,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + ", flags=" + flags); } mPresentationStatsEventLogger.maybeSetRequestId(requestId); + mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( + mFieldClassificationIdSnapshot); mFillRequestEventLogger.maybeSetRequestId(requestId); mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid()); if (mSessionFlags.mInlineSupportedByService) { @@ -1327,6 +1346,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void requestAssistStructureForPccLocked(int flags) { if (!mClassificationState.shouldTriggerRequest()) return; + mFillRequestIdSnapshot = sIdCounter.get(); mClassificationState.updatePendingRequest(); // Get request id int requestId; @@ -1411,7 +1431,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mStartTime = SystemClock.elapsedRealtime(); mLatencyBaseTime = mStartTime; mRequestCount = 0; - mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId); + mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog( + sessionId, uid); mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId); mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId); mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId); @@ -4301,6 +4322,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (viewState.getResponse() != null) { FillResponse response = viewState.getResponse(); mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId()); + mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( + mFieldClassificationIdSnapshot); mPresentationStatsEventLogger.maybeSetAvailableCount( response.getDatasets(), mCurrentViewId); } @@ -4478,6 +4501,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, @Nullable AutofillValue value, int flags) { synchronized (mLock) { + mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( + mFieldClassificationIdSnapshot); if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillReady() rejected - session: " + id + " destroyed"); @@ -5389,6 +5414,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState List<Dataset> datasetList = newResponse.getDatasets(); + mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(sIdCounterForPcc.get()); mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId); mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList); @@ -6451,7 +6477,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid; } - // FieldClassificationServiceCallbacks + // FieldClassificationServiceCallbacks start public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) { mClassificationState.updateResponseReceived(response); } @@ -6472,6 +6498,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // forceRemoveFromServiceLocked(); } } - // DetectionServiceCallbacks end + + @Override + public void logFieldClassificationEvent( + long startTime, FieldClassificationResponse response, + @FieldClassificationEventLogger.FieldClassificationStatus int status) { + final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger(); + logger.startNewLogForRequest(); + logger.maybeSetLatencyMillis( + SystemClock.elapsedRealtime() - startTime); + logger.maybeSetAppPackageUid(uid); + logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1); + logger.maybeSetRequestId(sIdCounterForPcc.get()); + logger.maybeSetSessionId(id); + int count = -1; + if (response != null) { + count = response.getClassifications().size(); + } + logger.maybeSetRequestStatus(status); + logger.maybeSetCountClassifications(count); + logger.logAndEndEvent(); + mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT; + } + // FieldClassificationServiceCallbacks end } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index d94f4f22f2c94f4b6735517448320a916b00cf54..556eba6ced768e96780ca8b6e7d539092ce85c5b 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -167,6 +167,8 @@ public final class BatteryService extends SystemService { private int mBatteryNearlyFullLevel; private int mShutdownBatteryTemperature; + private static String sSystemUiPackage; + private int mPlugType; private int mLastPlugType = -1; // Extra state so we can detect first run @@ -228,6 +230,8 @@ public final class BatteryService extends SystemService { com.android.internal.R.integer.config_lowBatteryCloseWarningBump); mShutdownBatteryTemperature = mContext.getResources().getInteger( com.android.internal.R.integer.config_shutdownBatteryTemperature); + sSystemUiPackage = mContext.getResources().getString( + com.android.internal.R.string.config_systemUi); mBatteryLevelsEventQueue = new ArrayDeque<>(); mMetricsLogger = new MetricsLogger(); @@ -750,8 +754,21 @@ public final class BatteryService extends SystemService { + ", info:" + mHealthInfo.toString()); } - mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE, - mBatteryChangedOptions, UserHandle.USER_ALL)); + mHandler.post(() -> broadcastBatteryChangedIntent(intent, mBatteryChangedOptions)); + } + + private static void broadcastBatteryChangedIntent(Intent intent, Bundle options) { + // TODO (293959093): It is important that SystemUI receives this broadcast as soon as + // possible. Ideally, it should be using binder callbacks but until then, dispatch this + // as a foreground broadcast to SystemUI. + final Intent fgIntent = new Intent(intent); + fgIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + fgIntent.setPackage(sSystemUiPackage); + ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE, + options, UserHandle.USER_ALL); + + ActivityManager.broadcastStickyIntent(intent, new String[] {sSystemUiPackage}, + AppOpsManager.OP_NONE, options, UserHandle.USER_ALL); } private void sendBatteryLevelChangedIntentLocked() { diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 1012bc1828c007738a4569de0f1e5d381e11af95..fac727f1728363df842556119f333d7208bda583 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -37,7 +37,8 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.TaskStackListener; +import android.app.ActivityManagerInternal; +import android.app.IProcessObserver; import android.content.Context; import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateManager; @@ -184,7 +185,30 @@ public final class DeviceStateManagerService extends SystemService { private final SystemPropertySetter mSystemPropertySetter; @VisibleForTesting - TaskStackListener mOverrideRequestTaskStackListener = new OverrideRequestTaskStackListener(); + final IProcessObserver mProcessObserver = new IProcessObserver.Stub() { + @Override + public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { + synchronized (mLock) { + if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) { + return; + } + + OverrideRequest request = mActiveOverride.get(); + if (pid != request.getPid() || uid != request.getUid()) { + return; + } + if (!fg) { + mOverrideRequestController.cancelRequest(request); + } + } + } + + @Override + public void onProcessDied(int pid, int uid) {} + + @Override + public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {} + }; @VisibleForTesting ActivityTaskManagerInternal.ScreenObserver mOverrideRequestScreenObserver = new OverrideRequestScreenObserver(); @@ -239,8 +263,9 @@ public final class DeviceStateManagerService extends SystemService { mFoldedDeviceStates = readFoldedStates(); } - mActivityTaskManagerInternal.registerTaskStackListener(mOverrideRequestTaskStackListener); mActivityTaskManagerInternal.registerScreenObserver(mOverrideRequestScreenObserver); + LocalServices.getService(ActivityManagerInternal.class).registerProcessObserver( + mProcessObserver); } @VisibleForTesting @@ -1288,23 +1313,6 @@ public final class DeviceStateManagerService extends SystemService { return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP); } - private class OverrideRequestTaskStackListener extends TaskStackListener { - @Override - public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) - throws RemoteException { - synchronized (mLock) { - if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) { - return; - } - - OverrideRequest request = mActiveOverride.get(); - if (!isTopApp(request.getPid())) { - mOverrideRequestController.cancelRequest(request); - } - } - } - } - private class OverrideRequestScreenObserver implements ActivityTaskManagerInternal.ScreenObserver { diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java index f4c84e7fe464cf88655bd6228387a77e7327716c..f9aefd0535d0e4e767450c6de6990afe9c3fa962 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java @@ -109,6 +109,7 @@ class DeviceStateNotificationController extends BroadcastReceiver { .setPackage(mContext.getPackageName()); final PendingIntent pendingIntent = PendingIntent.getBroadcast( mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE); + showNotification( info.name, info.activeNotificationTitle, String.format(info.activeNotificationContent, requesterApplicationLabel), @@ -175,7 +176,7 @@ class DeviceStateNotificationController extends BroadcastReceiver { if (getNotificationInfos().get(state) == null) { return; } - mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + mHandler.post(() -> mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID)); } @Override @@ -219,8 +220,10 @@ class DeviceStateNotificationController extends BroadcastReceiver { builder.addAction(action); } - mNotificationManager.createNotificationChannel(channel); - mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + mHandler.post(() -> { + mNotificationManager.createNotificationChannel(channel); + mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + }); } private SparseArray<NotificationInfo> getNotificationInfos() { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 032778c797687d16510e19ff1d45810f16ea8300..d3ad6c437f788f1e3b5626abffe939757345bafa 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3303,6 +3303,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } // Changing to a different IME. + IInputMethodInvoker curMethod = getCurMethodLocked(); + if (curMethod != null) { + curMethod.removeStylusHandwritingWindow(); + } final long ident = Binder.clearCallingIdentity(); try { // Set a subtype to this input method. diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b001c6aa95efb61df417469630568b301fc0a90d..097656cac7f7b135cbb2f0312754764dcd5e8257 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3972,15 +3972,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { */ private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation, boolean notifyOccluded) { + int redoLayout = 0; if (notifyOccluded) { - final int redoLayout = applyKeyguardOcclusionChange(); - if (redoLayout != 0) return redoLayout; + redoLayout = applyKeyguardOcclusionChange(); } if (startKeyguardExitAnimation) { if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); startKeyguardExitAnimation(SystemClock.uptimeMillis()); } - return 0; + return redoLayout; } // There are several different flavors of "assistant" that can be launched from diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 8f88ba6ff9a78bc030b58fcd4464b360e8c12541..7d735632b675bd632812694fc19ff4126ffd0580 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -30,7 +30,6 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertThrows; -import android.app.ActivityManager; import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateRequest; import android.hardware.devicestate.IDeviceStateManagerCallback; @@ -582,10 +581,10 @@ public final class DeviceStateManagerServiceTest { // When the app is foreground, the state should not change () -> { int pid = Binder.getCallingPid(); - when(mWindowProcessController.getPid()).thenReturn(pid); + int uid = Binder.getCallingUid(); try { - mService.mOverrideRequestTaskStackListener.onTaskMovedToFront( - new ActivityManager.RunningTaskInfo()); + mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid, + true /* foregroundActivities */); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -594,8 +593,11 @@ public final class DeviceStateManagerServiceTest { () -> { when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID); try { - mService.mOverrideRequestTaskStackListener.onTaskMovedToFront( - new ActivityManager.RunningTaskInfo()); + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid, + false /* foregroundActivities */); + } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java index 728606f9f62855c18c3c660814b48682b920b65d..bb0de032d98756ad65519f8f266ef0ac362273ef 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java @@ -33,6 +33,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.platform.test.annotations.Presubmit; import android.util.SparseArray; @@ -89,11 +91,12 @@ public class DeviceStateNotificationControllerTest { @Before public void setup() throws Exception { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - Handler handler = mock(Handler.class); PackageManager packageManager = mock(PackageManager.class); Runnable cancelStateRunnable = mock(Runnable.class); ApplicationInfo applicationInfo = mock(ApplicationInfo.class); + Handler handler = new DeviceStateNotificationControllerTestHandler(Looper.getMainLooper()); + final SparseArray<DeviceStateNotificationController.NotificationInfo> notificationInfos = new SparseArray<>(); notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION, @@ -259,4 +262,16 @@ public class DeviceStateNotificationControllerTest { assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale()); clearInvocations(mNotificationInfoProvider); } + + private static class DeviceStateNotificationControllerTestHandler extends Handler { + DeviceStateNotificationControllerTestHandler(Looper looper) { + super(looper); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + msg.getCallback().run(); + return true; + } + } } diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp index 3272cef2eb796741399b99951c346cb9765ecfec..680952157fdcc3f5f6c5792c8e8e619678a7bdb5 100644 --- a/tests/CtsSurfaceControlTestsStaging/Android.bp +++ b/tests/CtsSurfaceControlTestsStaging/Android.bp @@ -36,6 +36,7 @@ android_test { "androidx.test.rules", "compatibility-device-util-axt", "com.google.android.material_material", + "SurfaceFlingerProperties", "truth-prebuilt", ], resource_dirs: ["src/main/res"], diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java index 4b12053af5c7aba9b30d182f99be5c716281d141..79348d104745f263c362531df7f088773516f30c 100644 --- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java +++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java @@ -30,6 +30,7 @@ import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.sysprop.SurfaceFlingerProperties; import android.util.Log; import android.view.Display; import android.view.Surface; @@ -45,6 +46,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; /** * An Activity to help with frame rate testing. @@ -217,6 +219,27 @@ public class GraphicsActivity extends Activity { postBuffer(); } + Surface getSurface() { + return mSurface; + } + + SurfaceControl getSurfaceControl() { + return mSurfaceControl; + } + + public int setFrameRate(float frameRate) { + Log.i(TAG, + String.format("Setting frame rate for %s: frameRate=%.2f", mName, frameRate)); + + int rc = 0; + try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) { + transaction.setFrameRate( + mSurfaceControl, frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT); + transaction.apply(); + } + return rc; + } + public int setFrameRateCategory(int category) { Log.i(TAG, String.format( @@ -230,6 +253,19 @@ public class GraphicsActivity extends Activity { return rc; } + public int setFrameRateSelectionStrategy(int strategy) { + Log.i(TAG, + String.format("Setting frame rate selection strategy for %s: strategy=%d", + mName, strategy)); + + int rc = 0; + try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) { + transaction.setFrameRateSelectionStrategy(mSurfaceControl, strategy); + transaction.apply(); + } + return rc; + } + public void setVisibility(boolean visible) { Log.i(TAG, String.format("Setting visibility for %s: %s", mName, @@ -342,6 +378,11 @@ public class GraphicsActivity extends Activity { uniqueFrameRates.add(frameRate); } } + Log.i(TAG, + "**** Available display refresh rates: " + + uniqueFrameRates.stream() + .map(Object::toString) + .collect(Collectors.joining(", "))); return uniqueFrameRates; } @@ -358,6 +399,10 @@ public class GraphicsActivity extends Activity { <= FRAME_RATE_TOLERANCE; } + private boolean frameRateEquals(float frameRate1, float frameRate2) { + return Math.abs(frameRate1 - frameRate2) <= FRAME_RATE_TOLERANCE; + } + // Waits until our SurfaceHolder has a surface and the activity is resumed. private void waitForPreconditions() throws InterruptedException { assertNotSame( @@ -447,9 +492,22 @@ public class GraphicsActivity extends Activity { verifyCompatibleAndStableFrameRate(0, surfaces); } - // Set expectedFrameRate to 0.0 to verify only stable frame rate. + private void verifyExactAndStableFrameRate( + float expectedFrameRate, + TestSurface... surfaces) throws InterruptedException { + verifyFrameRate(expectedFrameRate, false, surfaces); + } + private void verifyCompatibleAndStableFrameRate( - float expectedFrameRate, TestSurface... surfaces) throws InterruptedException { + float expectedFrameRate, + TestSurface... surfaces) throws InterruptedException { + verifyFrameRate(expectedFrameRate, true, surfaces); + } + + // Set expectedFrameRate to 0.0 to verify only stable frame rate. + private void verifyFrameRate( + float expectedFrameRate, boolean multiplesAllowed, + TestSurface... surfaces) throws InterruptedException { Log.i(TAG, "Verifying compatible and stable frame rate"); long nowNanos = System.nanoTime(); long gracePeriodEndTimeNanos = @@ -457,9 +515,19 @@ public class GraphicsActivity extends Activity { while (true) { if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0 // Wait until we switch to a compatible frame rate. - while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate) - && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) { - // Empty + Log.i(TAG, + "Verifying expected frame rate: actual (device)=" + mDeviceFrameRate + + " expected=" + expectedFrameRate); + if (multiplesAllowed) { + while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate) + && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) { + // Empty + } + } else { + while (!frameRateEquals(mDeviceFrameRate, expectedFrameRate) + && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) { + // Empty + } } nowNanos = System.nanoTime(); if (nowNanos >= gracePeriodEndTimeNanos) { @@ -604,12 +672,65 @@ public class GraphicsActivity extends Activity { "frame rate category=" + category); } + private void testSurfaceControlFrameRateSelectionStrategyInternal(int parentStrategy) + throws InterruptedException { + Log.i(TAG, + "**** Running testSurfaceControlFrameRateSelectionStrategy for strategy " + + parentStrategy); + TestSurface parent = null; + TestSurface child = null; + try { + parent = new TestSurface(mSurfaceView.getSurfaceControl(), mSurface, + "testSurfaceParent", mSurfaceView.getHolder().getSurfaceFrame(), + /*visible=*/true, Color.RED); + child = new TestSurface(parent.getSurfaceControl(), parent.getSurface(), + "testSurfaceChild", mSurfaceView.getHolder().getSurfaceFrame(), + /*visible=*/true, Color.BLUE); + + // Test + Display display = getDisplay(); + List<Float> frameRates = getRefreshRates(display.getMode(), display); + assumeTrue("**** SKIPPED due to frame rate override disabled", + SurfaceFlingerProperties.enable_frame_rate_override().orElse(true)); + float childFrameRate = Collections.max(frameRates); + float parentFrameRate = childFrameRate / 2; + int initialNumEvents = mModeChangedEvents.size(); + parent.setFrameRate(parentFrameRate); + parent.setFrameRateSelectionStrategy(parentStrategy); + child.setFrameRate(childFrameRate); + + // Verify + float expectedFrameRate = + parentStrategy == SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN + ? parentFrameRate + : childFrameRate; + verifyExactAndStableFrameRate(expectedFrameRate, parent, child); + verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size()); + } finally { + if (parent != null) { + parent.release(); + } + if (child != null) { + child.release(); + } + } + } + + public void testSurfaceControlFrameRateSelectionStrategy(int parentStrategy) + throws InterruptedException { + runTestsWithPreconditions( + () -> testSurfaceControlFrameRateSelectionStrategyInternal(parentStrategy), + "frame rate strategy=" + parentStrategy); + } + private float getExpectedFrameRate(int category) { Display display = getDisplay(); List<Float> frameRates = getRefreshRates(display.getMode(), display); - if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT - || category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) { + if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) { + // Max due to default vote and no other frame rate specifications. + return Collections.max(frameRates); + } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) { return Collections.min(frameRates); } diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java index ccbda7f2fdf0f62759a486592ff416e8a6d073e2..bed9cff75e1daedb8e2b7e043d72768e0232227d 100644 --- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java +++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java @@ -23,11 +23,14 @@ import android.hardware.display.DisplayManager; import android.os.SystemProperties; import android.support.test.uiautomator.UiDevice; import android.view.Surface; +import android.view.SurfaceControl; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; +import com.android.compatibility.common.util.DisplayUtil; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -59,19 +62,15 @@ public class SurfaceControlTest { uiDevice.executeShellCommand("wm dismiss-keyguard"); InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( - Manifest.permission.LOG_COMPAT_CHANGE, - Manifest.permission.READ_COMPAT_CHANGE_CONFIG, Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, - Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, - Manifest.permission.MANAGE_GAME_MODE); + Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS); // Prevent DisplayManager from limiting the allowed refresh rate range based on // non-app policies (e.g. low battery, user settings, etc). mDisplayManager = activity.getSystemService(DisplayManager.class); mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); - mInitialRefreshRateSwitchingType = - toSwitchingType(mDisplayManager.getMatchContentFrameRateUserPreference()); + mInitialRefreshRateSwitchingType = DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); mDisplayManager.setRefreshRateSwitchingType( DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS); } @@ -118,16 +117,18 @@ public class SurfaceControlTest { activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_DEFAULT); } - private int toSwitchingType(int matchContentFrameRateUserPreference) { - switch (matchContentFrameRateUserPreference) { - case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER: - return DisplayManager.SWITCHING_TYPE_NONE; - case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY: - return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; - case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS: - return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; - default: - return -1; - } + @Test + public void testSurfaceControlFrameRateSelectionStrategySelf() throws InterruptedException { + GraphicsActivity activity = mActivityRule.getActivity(); + activity.testSurfaceControlFrameRateSelectionStrategy( + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF); + } + + @Test + public void testSurfaceControlFrameRateSelectionStrategyOverrideChildren() + throws InterruptedException { + GraphicsActivity activity = mActivityRule.getActivity(); + activity.testSurfaceControlFrameRateSelectionStrategy( + SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); } }