diff options
204 files changed, 6921 insertions, 2445 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 3f834fa883c1..a5e695a4c315 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -910,6 +910,8 @@ java_aconfig_library {  aconfig_declarations {      name: "android.service.notification.flags-aconfig",      package: "android.service.notification", +    exportable: true, +    container: "system",      srcs: ["core/java/android/service/notification/flags.aconfig"],  } @@ -919,6 +921,18 @@ java_aconfig_library {      defaults: ["framework-minus-apex-aconfig-java-defaults"],  } +java_aconfig_library { +    name: "android.service.notification.flags-aconfig-export-java", +    aconfig_declarations: "android.service.notification.flags-aconfig", +    defaults: ["framework-minus-apex-aconfig-java-defaults"], +    mode: "exported", +    min_sdk_version: "30", +    apex_available: [ +        "//apex_available:platform", +        "com.android.extservices", +    ], +} +  // Smartspace  aconfig_declarations {      name: "android.app.smartspace.flags-aconfig", @@ -985,6 +999,11 @@ java_aconfig_library {      defaults: ["framework-minus-apex-aconfig-java-defaults"],  } +cc_aconfig_library { +    name: "android.tracing.flags_c_lib", +    aconfig_declarations: "android.tracing.flags-aconfig", +} +  // App Widgets  aconfig_declarations {      name: "android.appwidget.flags-aconfig", diff --git a/Android.bp b/Android.bp index 870df5a5723e..019bf6508774 100644 --- a/Android.bp +++ b/Android.bp @@ -508,7 +508,6 @@ java_library {      lint: {          baseline_filename: "lint-baseline.xml",      }, -    // For jarjar repackaging      jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",  } diff --git a/core/api/current.txt b/core/api/current.txt index 4a2abf69c503..9b89a656dbb1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7024,6 +7024,7 @@ package android.app {      method public void deleteNotificationChannelGroup(String);      method public android.service.notification.StatusBarNotification[] getActiveNotifications();      method public android.app.AutomaticZenRule getAutomaticZenRule(String); +    method @FlaggedApi("android.app.modes_api") public int getAutomaticZenRuleState(@NonNull String);      method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();      method public int getBubblePreference();      method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4d7ac7848649..627b70355f50 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2193,6 +2193,139 @@ package android.app.job {  } +package android.app.ondeviceintelligence { + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Content implements android.os.Parcelable { +    ctor public Content(@NonNull android.os.Bundle); +    method public int describeContents(); +    method @NonNull public android.os.Bundle getData(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Content> CREATOR; +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface DownloadCallback { +    method public void onDownloadCompleted(@NonNull android.os.PersistableBundle); +    method public void onDownloadFailed(int, @Nullable String, @NonNull android.os.PersistableBundle); +    method public default void onDownloadProgress(long); +    method public default void onDownloadStarted(long); +    field public static final int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; // 0x3 +    field public static final int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; // 0x2 +    field public static final int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; // 0x1 +    field public static final int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; // 0x4 +    field public static final int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; // 0x0 +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Feature implements android.os.Parcelable { +    method public int describeContents(); +    method @NonNull public android.os.PersistableBundle getFeatureParams(); +    method public int getId(); +    method @Nullable public String getModelName(); +    method @Nullable public String getName(); +    method public int getType(); +    method public int getVariant(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Feature> CREATOR; +  } + +  public static final class Feature.Builder { +    ctor public Feature.Builder(int, int, int, @NonNull android.os.PersistableBundle); +    method @NonNull public android.app.ondeviceintelligence.Feature build(); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setFeatureParams(@NonNull android.os.PersistableBundle); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setId(int); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setModelName(@NonNull String); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setName(@NonNull String); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setType(int); +    method @NonNull public android.app.ondeviceintelligence.Feature.Builder setVariant(int); +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FeatureDetails implements android.os.Parcelable { +    ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int, @NonNull android.os.PersistableBundle); +    ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int); +    method public int describeContents(); +    method @NonNull public android.os.PersistableBundle getFeatureDetailParams(); +    method @android.app.ondeviceintelligence.FeatureDetails.Status public int getStatus(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FeatureDetails> CREATOR; +    field public static final int FEATURE_STATUS_AVAILABLE = 3; // 0x3 +    field public static final int FEATURE_STATUS_DOWNLOADABLE = 1; // 0x1 +    field public static final int FEATURE_STATUS_DOWNLOADING = 2; // 0x2 +    field public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; // 0x4 +    field public static final int FEATURE_STATUS_UNAVAILABLE = 0; // 0x0 +  } + +  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public static @interface FeatureDetails.Status { +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FilePart implements android.os.Parcelable { +    ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull String) throws java.io.FileNotFoundException; +    ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull java.io.FileInputStream) throws java.io.IOException; +    method public int describeContents(); +    method @NonNull public java.io.FileInputStream getFileInputStream(); +    method @NonNull public String getFilePartKey(); +    method @NonNull public android.os.PersistableBundle getFilePartParams(); +    method public void writeToParcel(@NonNull android.os.Parcel, int); +    field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FilePart> CREATOR; +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public class OnDeviceIntelligenceManager { +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeature(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getVersion(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.LongConsumer); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void listFeatures(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback); +    method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenCount(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    field public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey"; +    field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2 +    field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0 +    field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1 +  } + +  public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException extends java.lang.Exception { +    ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull String, @NonNull android.os.PersistableBundle); +    ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull android.os.PersistableBundle); +    method public int getErrorCode(); +    method @NonNull public android.os.PersistableBundle getErrorParams(); +    field public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000; // 0x3e8 +  } + +  public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException extends android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException { +    ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull String, @NonNull android.os.PersistableBundle); +    ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull android.os.PersistableBundle); +    field public static final int PROCESSING_ERROR_BAD_DATA = 2; // 0x2 +    field public static final int PROCESSING_ERROR_BAD_REQUEST = 3; // 0x3 +    field public static final int PROCESSING_ERROR_BUSY = 9; // 0x9 +    field public static final int PROCESSING_ERROR_CANCELLED = 7; // 0x7 +    field public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; // 0x5 +    field public static final int PROCESSING_ERROR_INTERNAL = 14; // 0xe +    field public static final int PROCESSING_ERROR_IPC_ERROR = 6; // 0x6 +    field public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; // 0x8 +    field public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; // 0x4 +    field public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; // 0xc +    field public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; // 0xb +    field public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; // 0xa +    field public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; // 0xf +    field public static final int PROCESSING_ERROR_SUSPENDED = 13; // 0xd +    field public static final int PROCESSING_ERROR_UNKNOWN = 1; // 0x1 +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class ProcessingSignal { +    ctor public ProcessingSignal(); +    method public void sendSignal(@NonNull android.os.PersistableBundle); +    method public void setOnProcessingSignalCallback(@NonNull java.util.concurrent.Executor, @Nullable android.app.ondeviceintelligence.ProcessingSignal.OnProcessingSignalCallback); +  } + +  public static interface ProcessingSignal.OnProcessingSignalCallback { +    method public void onSignalReceived(@NonNull android.os.PersistableBundle); +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface StreamingResponseReceiver<R, T, E extends java.lang.Throwable> extends android.os.OutcomeReceiver<R,E> { +    method public void onNewContent(@NonNull T); +  } + +} +  package android.app.people {    public final class PeopleManager { @@ -3640,6 +3773,7 @@ package android.content {      field public static final String NETD_SERVICE = "netd";      field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";      field public static final String OEM_LOCK_SERVICE = "oem_lock"; +    field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";      field public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";      field public static final String PERMISSION_SERVICE = "permission";      field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; @@ -12824,6 +12958,34 @@ package android.service.oemlock {  } +package android.service.ondeviceintelligence { + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service { +    ctor public OnDeviceIntelligenceService(); +    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); +    method public abstract void onDownloadFeature(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback); +    method public abstract void onGetFeature(int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    method public abstract void onGetFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>); +    method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer); +    method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>); +    field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService"; +  } + +  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service { +    ctor public OnDeviceTrustedInferenceService(); +    method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>); +    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); +    method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); +    method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); +    method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>); +    method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException; +    method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException; +    field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService"; +  } + +} +  package android.service.persistentdata {    @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index ca9fab815167..1923641e2d4e 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -509,6 +509,12 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():      Methods must not throw generic exceptions (`java.lang.Throwable`) +InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceIntelligenceService#onBind(android.content.Intent) parameter #0: +    Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. +InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#onBind(android.content.Intent) parameter #0: +    Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. +InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String) parameter #0: +    Invalid nullability on parameter `filename` in method `openFileInput`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.  InvalidNullabilityOverride: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent) parameter #0:      Invalid nullability on parameter `intent` in method `onUnbind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.  InvalidNullabilityOverride: android.service.voice.HotwordDetectionService#getSystemService(String) parameter #0: @@ -565,6 +571,8 @@ MissingNullability: android.service.contentcapture.ContentCaptureService#dump(ja      Missing nullability on parameter `args` in method `dump`  MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0:      Missing nullability on parameter `base` in method `attachBaseContext` +MissingNullability: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String): +    Missing nullability on method `openFileInput` return  MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:      Missing nullability on parameter `intent` in method `onUnbind`  MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0: @@ -1877,6 +1885,8 @@ Todo: android.Manifest.permission#READ_PEOPLE_DATA:      Documentation mentions 'TODO'  Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):      Documentation mentions 'TODO' +Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback): +    Documentation mentions 'TODO'  Todo: android.hardware.camera2.params.StreamConfigurationMap:      Documentation mentions 'TODO'  Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 4ec476ed4ee4..48dcbe55b4db 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1703,6 +1703,7 @@ package android.hardware.display {      field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1      field public static final int VIRTUAL_DISPLAY_FLAG_OWN_FOCUS = 16384; // 0x4000      field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 +    field public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 64; // 0x40    }  } @@ -1751,6 +1752,10 @@ package android.hardware.input {      field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0    } +  public class VirtualKeyboard implements java.io.Closeable { +    method public int getInputDeviceId(); +  } +  }  package android.hardware.lights { diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index c1181f5b8233..27808cb3af88 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1931,6 +1931,8 @@ Todo: android.Manifest.permission#READ_PEOPLE_DATA:      Documentation mentions 'TODO'  Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):      Documentation mentions 'TODO' +Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback): +    Documentation mentions 'TODO'  Todo: android.hardware.camera2.params.StreamConfigurationMap:      Documentation mentions 'TODO'  Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int): diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 926e297a1098..e6a2c07b6ff2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -237,7 +237,6 @@ import com.android.internal.util.Preconditions;  import com.android.internal.util.function.pooled.PooledLambda;  import com.android.org.conscrypt.TrustedCertificateStore;  import com.android.server.am.MemInfoDumpProto; -import com.android.window.flags.Flags;  import dalvik.annotation.optimization.NeverCompile;  import dalvik.system.AppSpecializationHooks; @@ -3762,11 +3761,7 @@ public final class ActivityThread extends ClientTransactionHandler          final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);          final ActivityResultItem activityResultItem = ActivityResultItem.obtain(                  activityToken, list); -        if (Flags.bundleClientTransactionFlag()) { -            clientTransaction.addTransactionItem(activityResultItem); -        } else { -            clientTransaction.addCallback(activityResultItem); -        } +        clientTransaction.addTransactionItem(activityResultItem);          try {              mAppThread.scheduleTransaction(clientTransaction);          } catch (RemoteException e) { @@ -4553,11 +4548,7 @@ public final class ActivityThread extends ClientTransactionHandler          final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,                  r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,                  /* dontReport */ false, /* autoEnteringPip */ false); -        if (Flags.bundleClientTransactionFlag()) { -            transaction.addTransactionItem(pauseActivityItem); -        } else { -            transaction.setLifecycleStateRequest(pauseActivityItem); -        } +        transaction.addTransactionItem(pauseActivityItem);          executeTransaction(transaction);      } @@ -4565,11 +4556,7 @@ public final class ActivityThread extends ClientTransactionHandler          final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);          final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,                  /* isForward */ false, /* shouldSendCompatFakeFocus */ false); -        if (Flags.bundleClientTransactionFlag()) { -            transaction.addTransactionItem(resumeActivityItem); -        } else { -            transaction.setLifecycleStateRequest(resumeActivityItem); -        } +        transaction.addTransactionItem(resumeActivityItem);          executeTransaction(transaction);      } @@ -6189,13 +6176,8 @@ public final class ActivityThread extends ClientTransactionHandler                  TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);          // Schedule the transaction.          final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); -        if (Flags.bundleClientTransactionFlag()) { -            transaction.addTransactionItem(activityRelaunchItem); -            transaction.addTransactionItem(lifecycleRequest); -        } else { -            transaction.addCallback(activityRelaunchItem); -            transaction.setLifecycleStateRequest(lifecycleRequest); -        } +        transaction.addTransactionItem(activityRelaunchItem); +        transaction.addTransactionItem(lifecycleRequest);          executeTransaction(transaction);      } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index b5e355638ae8..8f81ae2ae7d6 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -225,6 +225,7 @@ interface INotificationManager      boolean removeAutomaticZenRule(String id, boolean fromUser);      boolean removeAutomaticZenRules(String packageName, boolean fromUser);      int getRuleInstanceCount(in ComponentName owner); +    int getAutomaticZenRuleState(String id);      void setAutomaticZenRuleState(String id, in Condition condition);      byte[] getBackupPayload(int user); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d6e8ae3e5dff..26f85f723bd9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -80,6 +80,7 @@ import android.os.Parcel;  import android.os.Parcelable;  import android.os.SystemClock;  import android.os.SystemProperties; +import android.os.Trace;  import android.os.UserHandle;  import android.os.UserManager;  import android.provider.Settings; @@ -3023,37 +3024,44 @@ public class Notification implements Parcelable       * @hide       */      public String loadHeaderAppName(Context context) { -        CharSequence name = null; -        // Check if there is a non-empty substitute app name and return that. -        if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) { -            name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME); -            if (!TextUtils.isEmpty(name)) { -                return name.toString(); +        Trace.beginSection("Notification#loadHeaderAppName"); + +        try { +            CharSequence name = null; +            // Check if there is a non-empty substitute app name and return that. +            if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) { +                name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME); +                if (!TextUtils.isEmpty(name)) { +                    return name.toString(); +                }              } -        } -        // If not, try getting the app info from extras. -        if (context == null) { -            return null; -        } -        final PackageManager pm = context.getPackageManager(); -        if (TextUtils.isEmpty(name)) { -            if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) { -                final ApplicationInfo info = extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO, -                        ApplicationInfo.class); -                if (info != null) { -                    name = pm.getApplicationLabel(info); +            // If not, try getting the app info from extras. +            if (context == null) { +                return null; +            } +            final PackageManager pm = context.getPackageManager(); +            if (TextUtils.isEmpty(name)) { +                if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) { +                    final ApplicationInfo info = extras.getParcelable( +                            EXTRA_BUILDER_APPLICATION_INFO, +                            ApplicationInfo.class); +                    if (info != null) { +                        name = pm.getApplicationLabel(info); +                    }                  }              } +            // If that's still empty, use the one from the context directly. +            if (TextUtils.isEmpty(name)) { +                name = pm.getApplicationLabel(context.getApplicationInfo()); +            } +            // If there's still nothing, ¯\_(ツ)_/¯ +            if (TextUtils.isEmpty(name)) { +                return null; +            } +            return name.toString(); +        } finally { +            Trace.endSection();          } -        // If that's still empty, use the one from the context directly. -        if (TextUtils.isEmpty(name)) { -            name = pm.getApplicationLabel(context.getApplicationInfo()); -        } -        // If there's still nothing, ¯\_(ツ)_/¯ -        if (TextUtils.isEmpty(name)) { -            return null; -        } -        return name.toString();      }      /** @@ -6722,23 +6730,29 @@ public class Notification implements Parcelable           */          @NonNull          public static Notification.Builder recoverBuilder(Context context, Notification n) { -            // Re-create notification context so we can access app resources. -            ApplicationInfo applicationInfo = n.extras.getParcelable( -                    EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class); -            Context builderContext; -            if (applicationInfo != null) { -                try { -                    builderContext = context.createApplicationContext(applicationInfo, -                            Context.CONTEXT_RESTRICTED); -                } catch (NameNotFoundException e) { -                    Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found"); -                    builderContext = context;  // try with our context +            Trace.beginSection("Notification.Builder#recoverBuilder"); + +            try { +                // Re-create notification context so we can access app resources. +                ApplicationInfo applicationInfo = n.extras.getParcelable( +                        EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class); +                Context builderContext; +                if (applicationInfo != null) { +                    try { +                        builderContext = context.createApplicationContext(applicationInfo, +                                Context.CONTEXT_RESTRICTED); +                    } catch (NameNotFoundException e) { +                        Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found"); +                        builderContext = context;  // try with our context +                    } +                } else { +                    builderContext = context; // try with given context                  } -            } else { -                builderContext = context; // try with given context -            } -            return new Builder(builderContext, n); +                return new Builder(builderContext, n); +            } finally { +                Trace.endSection(); +            }          }          /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 9dfb5b0dedbd..d49a2542eed8 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1406,6 +1406,26 @@ public class NotificationManager {      }      /** +     * Returns the current activation state of an {@link AutomaticZenRule}. +     * +     * <p>Returns {@link Condition#STATE_UNKNOWN} if the rule does not exist or the calling +     * package doesn't have access to it. +     * +     * @param id The id of the rule +     * @return the state of the rule. +     */ +    @FlaggedApi(Flags.FLAG_MODES_API) +    @Condition.State +    public int getAutomaticZenRuleState(@NonNull String id) { +        INotificationManager service = getService(); +        try { +            return service.getAutomaticZenRuleState(id); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /**       * Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.       * Use this method to put the system into Do Not Disturb mode or request that it exits Do Not       * Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}. diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index a045eae9e108..7903f1c0c5c3 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -16,7 +16,7 @@  package android.app; -import static android.app.Flags.enableNightModeCache; +import static android.app.Flags.enableNightModeBinderCache;  import android.annotation.CallbackExecutor;  import android.annotation.FlaggedApi; @@ -916,7 +916,7 @@ public class UiModeManager {       *       * @hide       */ -    @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_CACHE) +    @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_BINDER_CACHE)      public static void invalidateNightModeCache() {          IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,                  NIGHT_MODE_API); @@ -938,7 +938,7 @@ public class UiModeManager {       * @see #setNightMode(int)       */      public @NightMode int getNightMode() { -        if (enableNightModeCache()) { +        if (enableNightModeBinderCache()) {              return mNightModeCache.query(null);          } else {              return getNightModeFromServer(); diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 7d5d5c162271..986205a346f7 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -16,10 +16,11 @@  package android.app.admin; +import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED; +  import android.annotation.FlaggedApi;  import android.annotation.IntDef;  import android.annotation.NonNull; -import android.app.admin.flags.Flags;  import android.compat.annotation.UnsupportedAppUsage;  import android.content.ComponentName;  import android.content.Context; @@ -185,7 +186,7 @@ public final class DeviceAdminInfo implements Parcelable {       * <p>This mode only allows a single secondary user on the device blocking the creation of       * additional secondary users.       */ -    @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) +    @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)      public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;      @IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED, diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java index 3c56aaf33ef3..eeaf0b3706fc 100644 --- a/core/java/android/app/admin/DevicePolicyIdentifiers.java +++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java @@ -16,13 +16,13 @@  package android.app.admin; +import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;  import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;  import android.annotation.FlaggedApi;  import android.annotation.NonNull;  import android.annotation.SystemApi;  import android.annotation.TestApi; -import android.app.admin.flags.Flags;  import android.os.UserManager;  import java.util.Objects; @@ -188,13 +188,13 @@ public final class DevicePolicyIdentifiers {      /**       * String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.       */ -    @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED) +    @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)      public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";      /**       * String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.       */ -    @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED) +    @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)      public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";      /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a6fda9d23aca..083705bca09e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -53,8 +53,11 @@ import static android.Manifest.permission.QUERY_ADMIN_POLICY;  import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;  import static android.Manifest.permission.SET_TIME;  import static android.Manifest.permission.SET_TIME_ZONE; +import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;  import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;  import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED; +import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED; +import static android.app.admin.flags.Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED;  import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;  import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;  import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED; @@ -90,7 +93,6 @@ import android.app.Activity;  import android.app.IServiceConnection;  import android.app.KeyguardManager;  import android.app.admin.SecurityLog.SecurityEvent; -import android.app.admin.flags.Flags;  import android.app.compat.CompatChanges;  import android.compat.annotation.ChangeId;  import android.compat.annotation.EnabledSince; @@ -153,10 +155,10 @@ import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.infra.AndroidFuture;  import com.android.internal.net.NetworkUtilsInternal;  import com.android.internal.os.BackgroundThread; +import com.android.internal.os.Zygote;  import com.android.internal.util.ArrayUtils;  import com.android.internal.util.Preconditions;  import com.android.org.conscrypt.TrustedCertificateStore; -import com.android.internal.os.Zygote;  import java.io.ByteArrayInputStream;  import java.io.FileNotFoundException; @@ -2879,7 +2881,7 @@ public class DevicePolicyManager {       * @hide       */      @SystemApi -    @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) +    @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)      public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17;      /** @@ -13447,7 +13449,7 @@ public class DevicePolicyManager {       */      @RequiresPermission(value = MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional = true)      @SuppressLint("RequiresPermission") -    @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED) +    @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)      public @Nullable SystemUpdateInfo getPendingSystemUpdate(@Nullable ComponentName admin) {          throwIfParentInstance("getPendingSystemUpdate");          try { @@ -16608,7 +16610,7 @@ public class DevicePolicyManager {       */      @RequiresPermission(value = MANAGE_DEVICE_POLICY_CERTIFICATES, conditional = true)      @SuppressLint("RequiresPermission") -    @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED) +    @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)      @NonNull public String getEnrollmentSpecificId() {          throwIfParentInstance("getEnrollmentSpecificId");          if (mService == null) { @@ -17134,7 +17136,7 @@ public class DevicePolicyManager {       */      @SystemApi      @RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION) -    @FlaggedApi(Flags.FLAG_DEVICE_THEFT_API_ENABLED) +    @FlaggedApi(FLAG_DEVICE_THEFT_API_ENABLED)      public boolean isTheftDetectionTriggered() {          throwIfParentInstance("isTheftDetectionTriggered");          if (mService == null) { diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index ed1b8ca9b5bd..477f2e007b33 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -16,6 +16,8 @@  package android.app.admin; +import static android.app.admin.flags.Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED; +  import android.Manifest;  import android.annotation.FlaggedApi;  import android.annotation.IntDef; @@ -24,7 +26,6 @@ import android.annotation.Nullable;  import android.annotation.RequiresPermission;  import android.annotation.SystemApi;  import android.annotation.TestApi; -import android.app.admin.flags.Flags;  import android.compat.annotation.UnsupportedAppUsage;  import android.content.ComponentName;  import android.os.Build; @@ -610,7 +611,7 @@ public class SecurityLog {       * <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)       * @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)       */ -    @FlaggedApi(Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED) +    @FlaggedApi(FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)      public static final int TAG_BACKUP_SERVICE_TOGGLED =              SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;      /** diff --git a/core/java/android/app/ondeviceintelligence/Content.aidl b/core/java/android/app/ondeviceintelligence/Content.aidl new file mode 100644 index 000000000000..40f0ef9a8541 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/Content.aidl @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +/** +  * @hide +  */ +parcelable Content; diff --git a/core/java/android/app/ondeviceintelligence/Content.java b/core/java/android/app/ondeviceintelligence/Content.java new file mode 100644 index 000000000000..51bd156fc946 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/Content.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents content sent to and received from the on-device inference service. + * Can contain a collection of text, image, and binary parts or any combination of these. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public final class Content implements Parcelable { +    //TODO: Improve javadoc after adding validation logic. +    private static final String TAG = "Content"; +    private final Bundle mData; + +    /** +     * Create a content object using a Bundle of only known types that are read-only. +     */ +    public Content(@NonNull Bundle data) { +        Objects.requireNonNull(data); +        validateBundleData(data); +        this.mData = data; +    } + +    /** +     * Returns the Content's data represented as a Bundle. +     */ +    @NonNull +    public Bundle getData() { +        return mData; +    } + +    @Override +    public void writeToParcel(@NonNull Parcel dest, int flags) { +        dest.writeBundle(mData); +    } + +    @Override +    public int describeContents() { +        int mask = 0; +        mask |= mData.describeContents(); +        return mask; +    } + +    @NonNull +    public static final Creator<Content> CREATOR = new Creator<>() { +        @Override +        @NonNull +        public Content createFromParcel(@NonNull Parcel in) { +            return new Content(in.readBundle(getClass().getClassLoader())); +        } + +        @Override +        @NonNull +        public Content[] newArray(int size) { +            return new Content[size]; +        } +    }; + +    private void validateBundleData(Bundle unused) { +        // TODO: Validate there are only known types. +    } +} diff --git a/core/java/android/app/ondeviceintelligence/DownloadCallback.java b/core/java/android/app/ondeviceintelligence/DownloadCallback.java new file mode 100644 index 000000000000..684c71f9144c --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/DownloadCallback.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.PersistableBundle; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Callback functions used for feature downloading via the + * {@link OnDeviceIntelligenceManager#requestFeatureDownload}. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public interface DownloadCallback { +    int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; + +    /** +     * Sent when feature download could not succeed due to there being no available disk space on +     * the device. +     */ +    int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; + +    /** +     * Sent when feature download could not succeed due to a network error. +     */ +    int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; + +    /** +     * Sent when feature download has been initiated already, hence no need to request download +     * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check if +     * download has been completed. +     */ +    int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; + +    /** +     * Sent when feature download did not start due to errors (e.g. remote exception of features not +     * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check +     * if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}. +     */ +    int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; + +    /** @hide */ +    @IntDef(value = { +            DOWNLOAD_FAILURE_STATUS_UNKNOWN, +            DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE, +            DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE, +            DOWNLOAD_FAILURE_STATUS_DOWNLOADING, +            DOWNLOAD_FAILURE_STATUS_UNAVAILABLE +    }, open = true) +    @Retention(RetentionPolicy.SOURCE) +    @interface DownloadFailureStatus { +    } + +    /** +     * Called when model download started properly. +     * +     * @param bytesToDownload the total bytes to be downloaded for this {@link Feature} +     */ +    default void onDownloadStarted(long bytesToDownload) { +    } + +    /** +     * Called when model download failed. +     * +     * @param failureStatus the download failure status +     * @param errorMessage  the error message associated with the download failure +     */ +    void onDownloadFailed( +            @DownloadFailureStatus int failureStatus, +            @Nullable String errorMessage, +            @NonNull PersistableBundle errorParams); + +    /** +     * Called when model download is in progress. +     * +     * @param totalBytesDownloaded the already downloaded bytes for this {@link Feature} +     */ +    default void onDownloadProgress(long totalBytesDownloaded) { +    } + +    /** +     * Called when model download via MDD completed. The remote implementation can populate any +     * associated download params like file stats etc. in this callback to inform the client. +     * +     * @param downloadParams params containing info about the completed download. +     */ +    void onDownloadCompleted(@NonNull PersistableBundle downloadParams); +} diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/core/java/android/app/ondeviceintelligence/Feature.aidl new file mode 100644 index 000000000000..18494d754674 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/Feature.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +/** +  * @hide +  */ +parcelable Feature; diff --git a/core/java/android/app/ondeviceintelligence/Feature.java b/core/java/android/app/ondeviceintelligence/Feature.java new file mode 100644 index 000000000000..510735461553 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/Feature.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.PersistableBundle; + +/** + * Represents a typical feature associated with on-device intelligence. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public final class Feature implements Parcelable { +    // TODO(b/325315604) - Check if we can expose non-hidden IntDefs in Framework. +    private final int mId; +    @Nullable +    private final String mName; +    @Nullable +    private final String mModelName; +    private final int mType; +    private final int mVariant; +    @NonNull +    private final PersistableBundle mFeatureParams; + +    /* package-private */ Feature( +            int id, +            @Nullable String name, +            @Nullable String modelName, +            int type, +            int variant, +            @NonNull PersistableBundle featureParams) { +        this.mId = id; +        this.mName = name; +        this.mModelName = modelName; +        this.mType = type; +        this.mVariant = variant; +        this.mFeatureParams = featureParams; +        com.android.internal.util.AnnotationValidations.validate( +                NonNull.class, null, mFeatureParams); +    } + +    /** Returns the unique and immutable identifier of this feature. */ +    public int getId() { +        return mId; +    } + +    /** Returns human-readable name of this feature. */ +    public @Nullable String getName() { +        return mName; +    } + +    /** Returns base model name of this feature. */ +    public @Nullable String getModelName() { +        return mModelName; +    } + +    /** Returns type identifier of this feature. */ +    public int getType() { +        return mType; +    } + +    /** Returns variant kind for this feature. */ +    public int getVariant() { +        return mVariant; +    } + +    public @NonNull PersistableBundle getFeatureParams() { +        return mFeatureParams; +    } + +    @Override +    public String toString() { +        return "Feature { " + +                "id = " + mId + ", " + +                "name = " + mName + ", " + +                "modelName = " + mModelName + ", " + +                "type = " + mType + ", " + +                "variant = " + mVariant + ", " + +                "featureParams = " + mFeatureParams + +                " }"; +    } + +    @Override +    public boolean equals(@Nullable Object o) { +        if (this == o) return true; +        if (o == null || getClass() != o.getClass()) return false; +        @SuppressWarnings("unchecked") +        Feature that = (Feature) o; +        //noinspection PointlessBooleanExpression +        return true +                && mId == that.mId +                && java.util.Objects.equals(mName, that.mName) +                && java.util.Objects.equals(mModelName, that.mModelName) +                && mType == that.mType +                && mVariant == that.mVariant +                && java.util.Objects.equals(mFeatureParams, that.mFeatureParams); +    } + +    @Override +    public int hashCode() { +        int _hash = 1; +        _hash = 31 * _hash + mId; +        _hash = 31 * _hash + java.util.Objects.hashCode(mName); +        _hash = 31 * _hash + java.util.Objects.hashCode(mModelName); +        _hash = 31 * _hash + mType; +        _hash = 31 * _hash + mVariant; +        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureParams); +        return _hash; +    } + +    @Override +    public void writeToParcel(@NonNull Parcel dest, int flags) { +        byte flg = 0; +        if (mName != null) flg |= 0x2; +        if (mModelName != null) flg |= 0x4; +        dest.writeByte(flg); +        dest.writeInt(mId); +        if (mName != null) dest.writeString8(mName); +        if (mModelName != null) dest.writeString8(mModelName); +        dest.writeInt(mType); +        dest.writeInt(mVariant); +        dest.writeTypedObject(mFeatureParams, flags); +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    /** @hide */ +    @SuppressWarnings({"unchecked", "RedundantCast"}) +    /* package-private */ Feature(@NonNull Parcel in) { +        byte flg = in.readByte(); +        int id = in.readInt(); +        String name = (flg & 0x2) == 0 ? null : in.readString(); +        String modelName = (flg & 0x4) == 0 ? null : in.readString(); +        int type = in.readInt(); +        int variant = in.readInt(); +        PersistableBundle featureParams = (PersistableBundle) in.readTypedObject( +                PersistableBundle.CREATOR); + +        this.mId = id; +        this.mName = name; +        this.mModelName = modelName; +        this.mType = type; +        this.mVariant = variant; +        this.mFeatureParams = featureParams; +        com.android.internal.util.AnnotationValidations.validate( +                NonNull.class, null, mFeatureParams); +    } + +    public static final @NonNull Parcelable.Creator<Feature> CREATOR +            = new Parcelable.Creator<Feature>() { +        @Override +        public Feature[] newArray(int size) { +            return new Feature[size]; +        } + +        @Override +        public Feature createFromParcel(@NonNull Parcel in) { +            return new Feature(in); +        } +    }; + +    /** +     * A builder for {@link Feature} +     */ +    @SuppressWarnings("WeakerAccess") +    public static final class Builder { +        private int mId; +        private @Nullable String mName; +        private @Nullable String mModelName; +        private int mType; +        private int mVariant; +        private @NonNull PersistableBundle mFeatureParams; + +        private long mBuilderFieldsSet = 0L; + +        public Builder( +                int id, +                int type, +                int variant, +                @NonNull PersistableBundle featureParams) { +            mId = id; +            mType = type; +            mVariant = variant; +            mFeatureParams = featureParams; +            com.android.internal.util.AnnotationValidations.validate( +                    NonNull.class, null, mFeatureParams); +        } + +        public @NonNull Builder setId(int value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x1; +            mId = value; +            return this; +        } + +        public @NonNull Builder setName(@NonNull String value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x2; +            mName = value; +            return this; +        } + +        public @NonNull Builder setModelName(@NonNull String value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x4; +            mModelName = value; +            return this; +        } + +        public @NonNull Builder setType(int value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x8; +            mType = value; +            return this; +        } + +        public @NonNull Builder setVariant(int value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x10; +            mVariant = value; +            return this; +        } + +        public @NonNull Builder setFeatureParams(@NonNull PersistableBundle value) { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x20; +            mFeatureParams = value; +            return this; +        } + +        /** Builds the instance. This builder should not be touched after calling this! */ +        public @NonNull Feature build() { +            checkNotUsed(); +            mBuilderFieldsSet |= 0x40; // Mark builder used + +            Feature o = new Feature( +                    mId, +                    mName, +                    mModelName, +                    mType, +                    mVariant, +                    mFeatureParams); +            return o; +        } + +        private void checkNotUsed() { +            if ((mBuilderFieldsSet & 0x40) != 0) { +                throw new IllegalStateException( +                        "This Builder should not be reused. Use a new Builder instance instead"); +            } +        } +    } +} diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl new file mode 100644 index 000000000000..0589bf8bacb9 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +/** +  * @hide +  */ +parcelable FeatureDetails; diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.java b/core/java/android/app/ondeviceintelligence/FeatureDetails.java new file mode 100644 index 000000000000..92f351362f70 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import androidx.annotation.IntDef; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.text.MessageFormat; + +/** + * Represents a status of a requested {@link Feature}. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public final class FeatureDetails implements Parcelable { +    @Status +    private final int mStatus; +    @NonNull +    private final PersistableBundle mFeatureDetailParams; + +    /** Invalid or unavailable {@code AiFeature}. */ +    public static final int FEATURE_STATUS_UNAVAILABLE = 0; + +    /** Feature can be downloaded on request. */ +    public static final int FEATURE_STATUS_DOWNLOADABLE = 1; + +    /** Feature is being downloaded. */ +    public static final int FEATURE_STATUS_DOWNLOADING = 2; + +    /** Feature is fully downloaded and ready to use. */ +    public static final int FEATURE_STATUS_AVAILABLE = 3; + +    /** Underlying service is unavailable and feature status cannot be fetched. */ +    public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; + +    @IntDef(value = { +            FEATURE_STATUS_UNAVAILABLE, +            FEATURE_STATUS_DOWNLOADABLE, +            FEATURE_STATUS_DOWNLOADING, +            FEATURE_STATUS_AVAILABLE, +            FEATURE_STATUS_SERVICE_UNAVAILABLE +    }, open = true) +    @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +    @Retention(RetentionPolicy.SOURCE) +    public @interface Status { +    } + +    public FeatureDetails( +            @Status int status, +            @NonNull PersistableBundle featureDetailParams) { +        this.mStatus = status; +        com.android.internal.util.AnnotationValidations.validate( +                Status.class, null, mStatus); +        this.mFeatureDetailParams = featureDetailParams; +        com.android.internal.util.AnnotationValidations.validate( +                NonNull.class, null, mFeatureDetailParams); +    } + +    public FeatureDetails( +            @Status int status) { +        this.mStatus = status; +        com.android.internal.util.AnnotationValidations.validate( +                Status.class, null, mStatus); +        this.mFeatureDetailParams = new PersistableBundle(); +    } + + +    /** +     * Returns an integer value associated with the feature status. +     */ +    public @Status int getStatus() { +        return mStatus; +    } + + +    /** +     * Returns a persistable bundle contain any additional status related params. +     */ +    public @NonNull PersistableBundle getFeatureDetailParams() { +        return mFeatureDetailParams; +    } + +    @Override +    public String toString() { +        return MessageFormat.format("FeatureDetails '{' status = {0}, " +                        + "persistableBundle = {1} '}'", +                mStatus, +                mFeatureDetailParams); +    } + +    @Override +    public boolean equals(@android.annotation.Nullable Object o) { +        if (this == o) return true; +        if (o == null || getClass() != o.getClass()) return false; +        @SuppressWarnings("unchecked") +        FeatureDetails that = (FeatureDetails) o; +        return mStatus == that.mStatus +                && java.util.Objects.equals(mFeatureDetailParams, that.mFeatureDetailParams); +    } + +    @Override +    public int hashCode() { +        int _hash = 1; +        _hash = 31 * _hash + mStatus; +        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureDetailParams); +        return _hash; +    } + +    @Override +    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { +        dest.writeInt(mStatus); +        dest.writeTypedObject(mFeatureDetailParams, flags); +    } + +    @Override +    public int describeContents() { +        return 0; +    } + +    /** @hide */ +    @SuppressWarnings({"unchecked", "RedundantCast"}) +    FeatureDetails(@NonNull android.os.Parcel in) { +        int status = in.readInt(); +        PersistableBundle persistableBundle = (PersistableBundle) in.readTypedObject( +                PersistableBundle.CREATOR); + +        this.mStatus = status; +        com.android.internal.util.AnnotationValidations.validate( +                Status.class, null, mStatus); +        this.mFeatureDetailParams = persistableBundle; +        com.android.internal.util.AnnotationValidations.validate( +                NonNull.class, null, mFeatureDetailParams); +    } + + +    public static final @NonNull Parcelable.Creator<FeatureDetails> CREATOR = +            new Parcelable.Creator<>() { +                @Override +                public FeatureDetails[] newArray(int size) { +                    return new FeatureDetails[size]; +                } + +                @Override +                public FeatureDetails createFromParcel(@NonNull android.os.Parcel in) { +                    return new FeatureDetails(in); +                } +            }; + +} diff --git a/core/java/android/app/ondeviceintelligence/FilePart.java b/core/java/android/app/ondeviceintelligence/FilePart.java new file mode 100644 index 000000000000..e9fb5f2cb440 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/FilePart.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.PersistableBundle; + +import android.annotation.NonNull; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Objects; + +/** + * Represents file data with an associated file descriptor sent to and received from remote + * processing. The interface ensures that the underlying file-descriptor is always opened in + * read-only mode. + * + * @hide + */ +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +@SystemApi +public final class FilePart implements Parcelable { +    private final String mPartKey; +    private final PersistableBundle mPartParams; +    private final ParcelFileDescriptor mParcelFileDescriptor; + +    private FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams, +            @NonNull ParcelFileDescriptor parcelFileDescriptor) { +        Objects.requireNonNull(partKey); +        Objects.requireNonNull(partParams); +        this.mPartKey = partKey; +        this.mPartParams = partParams; +        this.mParcelFileDescriptor = Objects.requireNonNull(parcelFileDescriptor); +    } + +    /** +     * Create a file part using a filePath and any additional params. +     */ +    public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams, +            @NonNull String filePath) +            throws FileNotFoundException { +        this(partKey, partParams, Objects.requireNonNull(ParcelFileDescriptor.open( +                new File(Objects.requireNonNull(filePath)), ParcelFileDescriptor.MODE_READ_ONLY))); +    } + +    /** +     * Create a file part using a file input stream and any additional params. +     * It is the caller's responsibility to close the stream. It is safe to do so as soon as this +     * call returns. +     */ +    public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams, +            @NonNull FileInputStream fileInputStream) +            throws IOException { +        this(partKey, partParams, ParcelFileDescriptor.dup(fileInputStream.getFD())); +    } + +    /** +     * Returns a FileInputStream for the associated File. +     * Caller must close the associated stream when done reading from it. +     * +     * @return the FileInputStream associated with the FilePart. +     */ +    @NonNull +    public FileInputStream getFileInputStream() { +        return new FileInputStream(mParcelFileDescriptor.getFileDescriptor()); +    } + +    /** +     * Returns the unique key associated with the part. Each Part key added to a content object +     * should be ensured to be unique. +     */ +    @NonNull +    public String getFilePartKey() { +        return mPartKey; +    } + +    /** +     * Returns the params associated with Part. +     */ +    @NonNull +    public PersistableBundle getFilePartParams() { +        return mPartParams; +    } + + +    @Override +    public int describeContents() { +        return CONTENTS_FILE_DESCRIPTOR; +    } + +    @Override +    public void writeToParcel(@NonNull Parcel dest, int flags) { +        dest.writeString8(getFilePartKey()); +        dest.writePersistableBundle(getFilePartParams()); +        mParcelFileDescriptor.writeToParcel(dest, flags +                | Parcelable.PARCELABLE_WRITE_RETURN_VALUE); // This flag ensures that the sender's +        // copy of the Pfd is closed as soon as the Binder call succeeds. +    } + +    @NonNull +    public static final Creator<FilePart> CREATOR = new Creator<>() { +        @Override +        public FilePart createFromParcel(Parcel in) { +            return new FilePart(in.readString(), in.readTypedObject(PersistableBundle.CREATOR), +                    in.readParcelable( +                            getClass().getClassLoader(), ParcelFileDescriptor.class)); +        } + +        @Override +        public FilePart[] newArray(int size) { +            return new FilePart[size]; +        } +    }; +} diff --git a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl new file mode 100644 index 000000000000..aba563f84e1b --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import android.app.ondeviceintelligence.IProcessingSignal; +import android.os.PersistableBundle; + +/** + * Interface for Download callback to passed onto service implementation, + * + * @hide + */ +oneway interface IDownloadCallback { +  void onDownloadStarted(long bytesToDownload) = 1; +  void onDownloadProgress(long bytesDownloaded) = 2; +  void onDownloadFailed(int failureStatus, String errorMessage, in PersistableBundle errorParams) = 3; +  void onDownloadCompleted(in PersistableBundle downloadParams) = 4; +}
\ No newline at end of file diff --git a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl new file mode 100644 index 000000000000..93a84ec96757 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl @@ -0,0 +1,14 @@ +package android.app.ondeviceintelligence; + +import android.app.ondeviceintelligence.Feature; +import android.os.PersistableBundle; + +/** +  * Interface for receiving a feature for the given identifier. +  * +  * @hide +  */ +interface IFeatureCallback { +    void onSuccess(in Feature result) = 1; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl new file mode 100644 index 000000000000..d95029059f4a --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl @@ -0,0 +1,14 @@ +package android.app.ondeviceintelligence; + +import android.app.ondeviceintelligence.FeatureDetails; +import android.os.PersistableBundle; + +/** +  * Interface for receiving details about a given feature. . +  * +  * @hide +  */ +interface IFeatureDetailsCallback { +    void onSuccess(in FeatureDetails result) = 1; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl new file mode 100644 index 000000000000..374cb71977d5 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl @@ -0,0 +1,15 @@ +package android.app.ondeviceintelligence; + +import java.util.List; +import android.app.ondeviceintelligence.Feature; +import android.os.PersistableBundle; + +/** +  * Interface for receiving list of supported features. +  * +  * @hide +  */ +interface IListFeaturesCallback { +    void onSuccess(in List<Feature> result) = 1; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl new file mode 100644 index 000000000000..b925f4863de2 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.app.ondeviceintelligence; + + import com.android.internal.infra.AndroidFuture; + import android.os.ICancellationSignal; + import android.os.ParcelFileDescriptor; + import android.os.PersistableBundle; + import android.os.RemoteCallback; + import android.app.ondeviceintelligence.Content; + import android.app.ondeviceintelligence.Feature; + import android.app.ondeviceintelligence.FeatureDetails; + import android.app.ondeviceintelligence.IDownloadCallback; + import android.app.ondeviceintelligence.IListFeaturesCallback; + import android.app.ondeviceintelligence.IFeatureCallback; + import android.app.ondeviceintelligence.IFeatureDetailsCallback; + import android.app.ondeviceintelligence.IResponseCallback; + import android.app.ondeviceintelligence.IStreamingResponseCallback; + import android.app.ondeviceintelligence.IProcessingSignal; + import android.app.ondeviceintelligence.ITokenCountCallback; + + + /** +  * Interface for a OnDeviceIntelligenceManager for managing OnDeviceIntelligenceService and OnDeviceSandboxedInferenceService. +  * +  * @hide +  */ + oneway interface IOnDeviceIntelligenceManager { +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void getVersion(in RemoteCallback remoteCallback) = 1; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void getFeature(in int featureId, in IFeatureCallback remoteCallback) = 2; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void listFeatures(in IListFeaturesCallback listFeaturesCallback) = 3; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback) = 4; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void requestFeatureDownload(in Feature feature, ICancellationSignal signal, in IDownloadCallback callback) = 5; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void requestTokenCount(in Feature feature, in Content request, in  ICancellationSignal signal, +                                                        in ITokenCountCallback tokenCountcallback) = 6; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void processRequest(in Feature feature, in Content request, int requestType, in  ICancellationSignal cancellationSignal, in IProcessingSignal signal, +                                                        in IResponseCallback responseCallback) = 7; + +      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)") +      void processRequestStreaming(in Feature feature, +                    in Content request, int requestType, in  ICancellationSignal cancellationSignal, in  IProcessingSignal signal, +                    in IStreamingResponseCallback streamingCallback) = 8; + } diff --git a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl new file mode 100644 index 000000000000..03946eebd40b --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl @@ -0,0 +1,14 @@ +package android.app.ondeviceintelligence; + +import android.os.PersistableBundle; + +/** +* Signal to provide to the remote implementation in context of a given request or +* feature specific event. +* +* @hide +*/ + +oneway interface IProcessingSignal { +    void sendSignal(in PersistableBundle actionParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl new file mode 100644 index 000000000000..9848e1ddda5a --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl @@ -0,0 +1,15 @@ +package android.app.ondeviceintelligence; + +import android.app.ondeviceintelligence.Content; +import android.app.ondeviceintelligence.IProcessingSignal; +import android.os.PersistableBundle; + +/** +  * Interface for a IResponseCallback for receiving response from on-device intelligence service. +  * +  * @hide +  */ +interface IResponseCallback { +    void onSuccess(in Content result) = 1; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl new file mode 100644 index 000000000000..a6805749fa04 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl @@ -0,0 +1,18 @@ +package android.app.ondeviceintelligence; + +import android.app.ondeviceintelligence.Content; +import android.app.ondeviceintelligence.IResponseCallback; +import android.app.ondeviceintelligence.IProcessingSignal; +import android.os.PersistableBundle; + + +/** +  * This callback is a streaming variant of {@link IResponseCallback}. +  * +  * @hide +  */ +interface IStreamingResponseCallback { +    void onNewContent(in Content result) = 1; +    void onSuccess(in Content result) = 2; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 3; +} diff --git a/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl new file mode 100644 index 000000000000..b724e03fbca4 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl @@ -0,0 +1,13 @@ +package android.app.ondeviceintelligence; + +import android.os.PersistableBundle; + +/** +  * Interface for receiving the token count of a request for a given features. +  * +  * @hide +  */ +interface ITokenCountCallback { +    void onSuccess(long tokenCount) = 1; +    void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2; +} diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS index 6932ba23a8ac..85e9e653e6fb 100644 --- a/core/java/android/app/ondeviceintelligence/OWNERS +++ b/core/java/android/app/ondeviceintelligence/OWNERS @@ -4,4 +4,3 @@ sandeepbandaru@google.com  shivanker@google.com  hackz@google.com  volnov@google.com - diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java new file mode 100644 index 000000000000..4d8e0d55e355 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.Binder; +import android.os.CancellationSignal; +import android.os.ICancellationSignal; +import android.os.OutcomeReceiver; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.RemoteException; + +import androidx.annotation.IntDef; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.LongConsumer; + +/** + * Allows granted apps to manage on-device intelligence service configured on the device. Typical + * calling pattern will be to query and setup a required feature before proceeding to request + * processing. + * + * The contracts in this Manager class are designed to be open-ended in general, to allow + * interoperability. Therefore, it is recommended that implementations of this system-service + * expose this API to the clients via a separate sdk or library which has more defined contract. + * + * @hide + */ +@SystemApi +@SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE) +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public class OnDeviceIntelligenceManager { +    public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey"; +    private final Context mContext; +    private final IOnDeviceIntelligenceManager mService; + +    /** +     * @hide +     */ +    public OnDeviceIntelligenceManager(Context context, IOnDeviceIntelligenceManager service) { +        mContext = context; +        mService = service; +    } + +    /** +     * Asynchronously get the version of the underlying remote implementation. +     * +     * @param versionConsumer  consumer to populate the version of remote implementation. +     * @param callbackExecutor executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void getVersion( +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull LongConsumer versionConsumer) { +        // TODO explore modifying this method into getServicePackageDetails and return both +        //  version and package name of the remote service implementing this. +        try { +            RemoteCallback callback = new RemoteCallback(result -> { +                if (result == null) { +                    Binder.withCleanCallingIdentity( +                            () -> callbackExecutor.execute(() -> versionConsumer.accept(0))); +                } +                long version = result.getLong(API_VERSION_BUNDLE_KEY); +                Binder.withCleanCallingIdentity( +                        () -> callbackExecutor.execute(() -> versionConsumer.accept(version))); +            }); +            mService.getVersion(callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * Asynchronously get feature for a given id. +     * +     * @param featureId        the identifier pointing to the feature. +     * @param featureReceiver  callback to populate the feature object for given identifier. +     * @param callbackExecutor executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void getFeature( +            int featureId, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull OutcomeReceiver<Feature, OnDeviceIntelligenceManagerException> featureReceiver) { +        try { +            IFeatureCallback callback = +                    new IFeatureCallback.Stub() { +                        @Override +                        public void onSuccess(Feature result) { +                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                                    () -> featureReceiver.onResult(result))); +                        } + +                        @Override +                        public void onFailure(int errorCode, String errorMessage, +                                PersistableBundle errorParams) { +                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                                    () -> featureReceiver.onError( +                                            new OnDeviceIntelligenceManagerException( +                                                    errorCode, errorMessage, errorParams)))); +                        } +                    }; +            mService.getFeature(featureId, callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * Asynchronously get a list of features that are supported for the caller. +     * +     * @param featureListReceiver callback to populate the list of features. +     * @param callbackExecutor    executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void listFeatures( +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull OutcomeReceiver<List<Feature>, OnDeviceIntelligenceManagerException> featureListReceiver) { +        try { +            IListFeaturesCallback callback = +                    new IListFeaturesCallback.Stub() { +                        @Override +                        public void onSuccess(List<Feature> result) { +                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                                    () -> featureListReceiver.onResult(result))); +                        } + +                        @Override +                        public void onFailure(int errorCode, String errorMessage, +                                PersistableBundle errorParams) { +                            Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                                    () -> featureListReceiver.onError( +                                            new OnDeviceIntelligenceManagerException( +                                                    errorCode, errorMessage, errorParams)))); +                        } +                    }; +            mService.listFeatures(callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * This method should be used to fetch details about a feature which need some additional +     * computation, that can be inefficient to return in all calls to {@link #getFeature}. Callers +     * and implementation can utilize the {@link Feature#getFeatureParams()} to pass hint on what +     * details are expected by the caller. +     * +     * @param feature                the feature to check status for. +     * @param featureDetailsReceiver callback to populate the feature details to. +     * @param callbackExecutor       executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void getFeatureDetails(@NonNull Feature feature, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceManagerException> featureDetailsReceiver) { +        try { +            IFeatureDetailsCallback callback = new IFeatureDetailsCallback.Stub() { + +                @Override +                public void onSuccess(FeatureDetails result) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> featureDetailsReceiver.onResult(result))); +                } + +                @Override +                public void onFailure(int errorCode, String errorMessage, +                        PersistableBundle errorParams) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> featureDetailsReceiver.onError( +                                    new OnDeviceIntelligenceManagerException(errorCode, +                                            errorMessage, errorParams)))); +                } +            }; +            mService.getFeatureDetails(feature, callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * This method handles downloading all model and config files required to process requests +     * sent against a given feature. The caller can listen to updates on the download status via +     * the callback. +     * +     * Note: If a feature was already requested for downloaded previously, the onDownloadFailed +     * callback would be invoked with {@link DownloadCallback#DOWNLOAD_FAILURE_STATUS_DOWNLOADING}. +     * In such cases, clients should query the feature status via {@link #getFeatureStatus} to +     * check +     * on the feature's download status. +     * +     * @param feature            feature to request download for. +     * @param callback           callback to populate updates about download status. +     * @param cancellationSignal signal to invoke cancellation on the operation in the remote +     *                           implementation. +     * @param callbackExecutor   executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void requestFeatureDownload(@NonNull Feature feature, +            @Nullable CancellationSignal cancellationSignal, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull DownloadCallback callback) { +        try { +            IDownloadCallback downloadCallback = new IDownloadCallback.Stub() { + +                @Override +                public void onDownloadStarted(long bytesToDownload) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> callback.onDownloadStarted(bytesToDownload))); +                } + +                @Override +                public void onDownloadProgress(long bytesDownloaded) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> callback.onDownloadProgress(bytesDownloaded))); +                } + +                @Override +                public void onDownloadFailed(int failureStatus, String errorMessage, +                        PersistableBundle errorParams) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> callback.onDownloadFailed(failureStatus, errorMessage, +                                    errorParams))); +                } + +                @Override +                public void onDownloadCompleted(PersistableBundle downloadParams) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> onDownloadCompleted(downloadParams))); +                } +            }; + +            ICancellationSignal transport = null; +            if (cancellationSignal != null) { +                transport = CancellationSignal.createTransport(); +                cancellationSignal.setRemote(transport); +            } + +            mService.requestFeatureDownload(feature, transport, downloadCallback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * The methods computes the token-count for a given request payload using the provided Feature +     * details. +     * +     * @param feature            feature associated with the request. +     * @param request            request that contains the content data and associated params. +     * @param outcomeReceiver    callback to populate the token count or exception in case of +     *                           failure. +     * @param cancellationSignal signal to invoke cancellation on the operation in the remote +     *                           implementation. +     * @param callbackExecutor   executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void requestTokenCount(@NonNull Feature feature, @NonNull Content request, +            @Nullable CancellationSignal cancellationSignal, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull OutcomeReceiver<Long, +                    OnDeviceIntelligenceManagerException> outcomeReceiver) { +        try { +            ITokenCountCallback callback = new ITokenCountCallback.Stub() { +                @Override +                public void onSuccess(long tokenCount) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> outcomeReceiver.onResult(tokenCount))); +                } + +                @Override +                public void onFailure(int errorCode, String errorMessage, +                        PersistableBundle errorParams) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> outcomeReceiver.onError( +                                    new OnDeviceIntelligenceManagerProcessingException( +                                            errorCode, errorMessage, errorParams)))); +                } +            }; + +            ICancellationSignal transport = null; +            if (cancellationSignal != null) { +                transport = CancellationSignal.createTransport(); +                cancellationSignal.setRemote(transport); +            } + +            mService.requestTokenCount(feature, request, transport, callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + + +    /** +     * Asynchronously Process a request based on the associated params, to populate a +     * response in +     * {@link OutcomeReceiver#onResult} callback or failure callback status code if there +     * was a +     * failure. +     * +     * @param feature                 feature associated with the request. +     * @param request                 request that contains the Content data and +     *                                associated params. +     * @param requestType             type of request being sent for processing the content. +     * @param responseOutcomeReceiver callback to populate the response content and +     *                                associated +     *                                params. +     * @param processingSignal        signal to invoke custom actions in the +     *                                remote implementation. +     * @param cancellationSignal      signal to invoke cancellation or +     * @param callbackExecutor        executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) + +    public void processRequest(@NonNull Feature feature, @NonNull Content request, +            @RequestType int requestType, +            @Nullable CancellationSignal cancellationSignal, +            @Nullable ProcessingSignal processingSignal, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull OutcomeReceiver<Content, +                    OnDeviceIntelligenceManagerProcessingException> responseOutcomeReceiver) { +        try { +            IResponseCallback callback = new IResponseCallback.Stub() { +                @Override +                public void onSuccess(Content result) { +                    Binder.withCleanCallingIdentity(() -> { +                        callbackExecutor.execute(() -> responseOutcomeReceiver.onResult(result)); +                    }); +                } + +                @Override +                public void onFailure(int errorCode, String errorMessage, +                        PersistableBundle errorParams) { +                    Binder.withCleanCallingIdentity(() -> callbackExecutor.execute( +                            () -> responseOutcomeReceiver.onError( +                                    new OnDeviceIntelligenceManagerProcessingException( +                                            errorCode, errorMessage, errorParams)))); +                } +            }; + +            IProcessingSignal transport = null; +            if (processingSignal != null) { +                transport = ProcessingSignal.createTransport(); +                processingSignal.setRemote(transport); +            } + +            ICancellationSignal cancellationTransport = null; +            if (cancellationSignal != null) { +                cancellationTransport = CancellationSignal.createTransport(); +                cancellationSignal.setRemote(cancellationTransport); +            } + +            mService.processRequest(feature, request, requestType, cancellationTransport, transport, +                    callback); + +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + +    /** +     * Variation of {@link #processRequest} that asynchronously processes a request in a streaming +     * fashion, where new content is pushed to caller in chunks via the +     * {@link StreamingResponseReceiver#onNewContent}. After the streaming is complete, +     * the service should call {@link StreamingResponseReceiver#onResult} and can optionally +     * populate the complete {@link Response}'s Content as part of the callback when the final +     * {@link Response} contains an enhanced aggregation of the Contents already streamed. +     * +     * @param feature                   feature associated with the request. +     * @param request                   request that contains the Content data and associated +     *                                  params. +     * @param requestType               type of request being sent for processing the content. +     * @param processingSignal          signal to invoke  other custom actions in the +     *                                  remote implementation. +     * @param cancellationSignal        signal to invoke cancellation +     * @param streamingResponseReceiver streaming callback to populate the response content and +     *                                  associated params. +     * @param callbackExecutor          executor to run the callback on. +     */ +    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) +    public void processRequestStreaming(@NonNull Feature feature, @NonNull Content request, +            @RequestType int requestType, +            @Nullable CancellationSignal cancellationSignal, +            @Nullable ProcessingSignal processingSignal, +            @NonNull @CallbackExecutor Executor callbackExecutor, +            @NonNull StreamingResponseReceiver<Content, Content, +                    OnDeviceIntelligenceManagerProcessingException> streamingResponseReceiver) { +        try { +            IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() { +                @Override +                public void onNewContent(Content result) { +                    Binder.withCleanCallingIdentity(() -> { +                        callbackExecutor.execute( +                                () -> streamingResponseReceiver.onNewContent(result)); +                    }); +                } + +                @Override +                public void onSuccess(Content result) { +                    Binder.withCleanCallingIdentity(() -> { +                        callbackExecutor.execute(() -> streamingResponseReceiver.onResult(result)); +                    }); +                } + +                @Override +                public void onFailure(int errorCode, String errorMessage, +                        PersistableBundle errorParams) { +                    Binder.withCleanCallingIdentity(() -> { +                        callbackExecutor.execute( +                                () -> streamingResponseReceiver.onError( +                                        new OnDeviceIntelligenceManagerProcessingException( +                                                errorCode, errorMessage, errorParams))); +                    }); +                } +            }; + +            IProcessingSignal transport = null; +            if (processingSignal != null) { +                transport = ProcessingSignal.createTransport(); +                processingSignal.setRemote(transport); +            } + +            ICancellationSignal cancellationTransport = null; +            if (cancellationSignal != null) { +                cancellationTransport = CancellationSignal.createTransport(); +                cancellationSignal.setRemote(cancellationTransport); +            } + +            mService.processRequestStreaming( +                    feature, request, requestType, cancellationTransport, transport, callback); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +    } + + +    /** Request inference with provided Content and Params. */ +    public static final int REQUEST_TYPE_INFERENCE = 0; + +    /** +     * Prepares the remote implementation environment for e.g.loading inference runtime etc.which +     * are time consuming beforehand to remove overhead and allow quick processing of requests +     * thereof. +     */ +    public static final int REQUEST_TYPE_PREPARE = 1; + +    /** Request Embeddings of the passed-in Content. */ +    public static final int REQUEST_TYPE_EMBEDDINGS = 2; + +    /** +     * @hide +     */ +    @IntDef(value = { +            REQUEST_TYPE_INFERENCE, +            REQUEST_TYPE_PREPARE, +            REQUEST_TYPE_EMBEDDINGS +    }, open = true) +    @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) +    @Retention(RetentionPolicy.SOURCE) +    public @interface RequestType { +    } + + +    /** +     * Exception type to be populated in callbacks to the methods under +     * {@link OnDeviceIntelligenceManager}. +     */ +    public static class OnDeviceIntelligenceManagerException extends Exception { +        /** +         * Error code returned when the OnDeviceIntelligenceManager service is unavailable. +         */ +        public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000; + +        private final int mErrorCode; +        private final PersistableBundle errorParams; + +        public OnDeviceIntelligenceManagerException(int errorCode, @NonNull String errorMessage, +                @NonNull PersistableBundle errorParams) { +            super(errorMessage); +            this.mErrorCode = errorCode; +            this.errorParams = errorParams; +        } + +        public OnDeviceIntelligenceManagerException(int errorCode, +                @NonNull PersistableBundle errorParams) { +            this.mErrorCode = errorCode; +            this.errorParams = errorParams; +        } + +        public int getErrorCode() { +            return mErrorCode; +        } + +        @NonNull +        public PersistableBundle getErrorParams() { +            return errorParams; +        } +    } + +    /** +     * Exception type to be populated in callbacks to the methods under +     * {@link OnDeviceIntelligenceManager#processRequest} or +     * {@link OnDeviceIntelligenceManager#processRequestStreaming} . +     */ +    public static class OnDeviceIntelligenceManagerProcessingException extends +            OnDeviceIntelligenceManagerException { + +        public static final int PROCESSING_ERROR_UNKNOWN = 1; + +        /** Request passed contains bad data for e.g. format. */ +        public static final int PROCESSING_ERROR_BAD_DATA = 2; + +        /** Bad request for inputs. */ +        public static final int PROCESSING_ERROR_BAD_REQUEST = 3; + +        /** Whole request was classified as not safe, and no response will be generated. */ +        public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; + +        /** Underlying processing encountered an error and failed to compute results. */ +        public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; + +        /** Encountered an error while performing IPC */ +        public static final int PROCESSING_ERROR_IPC_ERROR = 6; + +        /** Request was cancelled either by user signal or by the underlying implementation. */ +        public static final int PROCESSING_ERROR_CANCELLED = 7; + +        /** Underlying processing in the remote implementation is not available. */ +        public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; + +        /** The service is currently busy. Callers should retry with exponential backoff. */ +        public static final int PROCESSING_ERROR_BUSY = 9; + +        /** Something went wrong with safety classification service. */ +        public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; + +        /** Response generated was classified unsafe. */ +        public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; + +        /** Request is too large to be processed. */ +        public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; + +        /** Inference suspended so that higher-priority inference can run. */ +        public static final int PROCESSING_ERROR_SUSPENDED = 13; + +        /** Underlying processing encountered an internal error, like a violated precondition. */ +        public static final int PROCESSING_ERROR_INTERNAL = 14; + +        /** +         * The processing was not able to be passed on to the remote implementation, as the service +         * was unavailable. +         */ +        public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; + +        /** +         * Error code of failed processing request. +         * +         * @hide +         */ +        @IntDef( +                value = { +                        PROCESSING_ERROR_UNKNOWN, +                        PROCESSING_ERROR_BAD_DATA, +                        PROCESSING_ERROR_BAD_REQUEST, +                        PROCESSING_ERROR_REQUEST_NOT_SAFE, +                        PROCESSING_ERROR_COMPUTE_ERROR, +                        PROCESSING_ERROR_IPC_ERROR, +                        PROCESSING_ERROR_CANCELLED, +                        PROCESSING_ERROR_NOT_AVAILABLE, +                        PROCESSING_ERROR_BUSY, +                        PROCESSING_ERROR_SAFETY_ERROR, +                        PROCESSING_ERROR_RESPONSE_NOT_SAFE, +                        PROCESSING_ERROR_REQUEST_TOO_LARGE, +                        PROCESSING_ERROR_SUSPENDED, +                        PROCESSING_ERROR_INTERNAL, +                        PROCESSING_ERROR_SERVICE_UNAVAILABLE +                }, open = true) +        @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) +        @interface ProcessingError { +        } + +        public OnDeviceIntelligenceManagerProcessingException( +                @ProcessingError int errorCode, @NonNull String errorMessage, +                @NonNull PersistableBundle errorParams) { +            super(errorCode, errorMessage, errorParams); +        } + +        public OnDeviceIntelligenceManagerProcessingException( +                @ProcessingError int errorCode, +                @NonNull PersistableBundle errorParams) { +            super(errorCode, errorParams); +        } +    } +} diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java new file mode 100644 index 000000000000..3e543d27143a --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.PersistableBundle; +import android.os.RemoteException; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayDeque; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * A signal to perform orchestration actions on the inference and optionally receive a output about + * the result of the signal. This is an extension of {@link android.os.CancellationSignal}. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public final class ProcessingSignal { +    private final Object mLock = new Object(); + +    private static final int MAX_QUEUE_SIZE = 10; + +    @GuardedBy("mLock") +    private final ArrayDeque<PersistableBundle> mActionParamsQueue; + +    @GuardedBy("mLock") +    private IProcessingSignal mRemote; + +    private OnProcessingSignalCallback mOnProcessingSignalCallback; +    private Executor mExecutor; + +    public ProcessingSignal() { +        mActionParamsQueue = new ArrayDeque<>(MAX_QUEUE_SIZE); +    } + +    /** +     * Interface definition for a callback to be invoked when processing signals are received. +     */ +    public interface OnProcessingSignalCallback { +        /** +         * Called when a custom signal was received. +         * This method allows the receiver to provide logic to be executed based on the signal +         * received. +         * +         * @param actionParams Parameters for the signal in the form of a {@link PersistableBundle}. +         */ + +        void onSignalReceived(@NonNull PersistableBundle actionParams); +    } + + +    /** +     * Sends a custom signal with the provided parameters. It also signals the remote callback +     * with the same params if already configured, if not the action is queued to be sent when a +     * remote is configured. Similarly, on the receiver side, the callback will be invoked if +     * already set, if not all actions are queued to be sent to callback when it is set. +     * +     * @param actionParams Parameters for the signal. +     */ +    public void sendSignal(@NonNull PersistableBundle actionParams) { +        final OnProcessingSignalCallback callback; +        final IProcessingSignal remote; +        synchronized (mLock) { +            if (mActionParamsQueue.size() > MAX_QUEUE_SIZE) { +                throw new RuntimeException( +                        "Maximum actions that can be queued are : " + MAX_QUEUE_SIZE); +            } + +            mActionParamsQueue.add(actionParams); +            callback = mOnProcessingSignalCallback; +            remote = mRemote; + +            if (callback != null) { +                while (!mActionParamsQueue.isEmpty()) { +                    PersistableBundle params = mActionParamsQueue.removeFirst(); +                    mExecutor.execute( +                            () -> callback.onSignalReceived(params)); +                } +            } +            if (remote != null) { +                while (!mActionParamsQueue.isEmpty()) { +                    try { +                        remote.sendSignal(mActionParamsQueue.removeFirst()); +                    } catch (RemoteException e) { +                        throw new RuntimeException(e); +                    } +                } +            } +        } +    } + + +    /** +     * Sets the processing signal callback to be called when signals are received. +     * +     * This method is intended to be used by the recipient of a processing signal +     * such as the remote implementation for {@link OnDeviceIntelligenceManager} to handle +     * cancellation requests while performing a long-running operation.  This method is not +     * intended +     * to be used by applications themselves. +     * +     * If {@link ProcessingSignal#sendSignal} has already been called, then the provided callback +     * is invoked immediately and all previously queued actions are passed to remote signal. +     * +     * This method is guaranteed that the callback will not be called after it +     * has been removed. +     * +     * @param callback The processing signal callback, or null to remove the current callback. +     * @param executor Executor to the run the callback methods on. +     */ +    public void setOnProcessingSignalCallback( +            @NonNull @CallbackExecutor Executor executor, +            @Nullable OnProcessingSignalCallback callback) { +        Objects.requireNonNull(executor); +        synchronized (mLock) { +            if (mOnProcessingSignalCallback == callback) { +                return; +            } + +            mOnProcessingSignalCallback = callback; +            mExecutor = executor; +            if (callback == null || mActionParamsQueue.isEmpty()) { +                return; +            } + +            while (!mActionParamsQueue.isEmpty()) { +                PersistableBundle params = mActionParamsQueue.removeFirst(); +                mExecutor.execute(() -> callback.onSignalReceived(params)); +            } +        } +    } + +    /** +     * Sets the remote transport. +     * +     * If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also +     * sequentially sent to the remote. +     * +     * This method is guaranteed that the remote transport will not be called after it +     * has been removed. +     * +     * @param remote The remote transport, or null to remove. +     * @hide +     */ +    void setRemote(IProcessingSignal remote) { +        synchronized (mLock) { +            mRemote = remote; +            if (mActionParamsQueue.isEmpty() || remote == null) { +                return; +            } + +            while (!mActionParamsQueue.isEmpty()) { +                try { +                    remote.sendSignal(mActionParamsQueue.removeFirst()); +                } catch (RemoteException e) { +                    throw new RuntimeException("Failed to send action to remote signal", e); +                } +            } +        } +    } + +    /** +     * Creates a transport that can be returned back to the caller of +     * a Binder function and subsequently used to dispatch a processing signal. +     * +     * @return The new processing signal transport. +     * @hide +     */ +    public static IProcessingSignal createTransport() { +        return new Transport(); +    } + +    /** +     * Given a locally created transport, returns its associated cancellation signal. +     * +     * @param transport The locally created transport, or null if none. +     * @return The associated processing signal, or null if none. +     * @hide +     */ +    public static ProcessingSignal fromTransport(IProcessingSignal transport) { +        if (transport instanceof Transport) { +            return ((Transport) transport).mProcessingSignal; +        } +        return null; +    } + +    private static final class Transport extends IProcessingSignal.Stub { +        final ProcessingSignal mProcessingSignal = new ProcessingSignal(); + +        @Override +        public void sendSignal(PersistableBundle actionParams) { +            mProcessingSignal.sendSignal(actionParams); +        } +    } + +}
\ No newline at end of file diff --git a/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java new file mode 100644 index 000000000000..ebcf61c8c0c2 --- /dev/null +++ b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.OutcomeReceiver; + +/** + * Streaming variant of outcome receiver to populate response while processing a given request, + * possibly in + * chunks to provide a async processing behaviour to the caller. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public interface StreamingResponseReceiver<R, T, E extends Throwable> extends +        OutcomeReceiver<R, E> { +    /** +     * Callback to be invoked when a part of the response i.e. some {@link Content} is already +     * processed and +     * needs to be passed onto the caller. +     */ +    void onNewContent(@NonNull T content); +} diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 612d433adcd6..79696e047904 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -29,6 +29,7 @@ import android.os.Parcelable;  import android.os.RemoteException;  import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags;  import java.io.PrintWriter;  import java.util.ArrayList; @@ -85,12 +86,15 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {       * @param item A single message that can contain a client activity/window request/callback.       */      public void addTransactionItem(@NonNull ClientTransactionItem item) { -        if (mTransactionItems == null) { -            mTransactionItems = new ArrayList<>(); +        if (Flags.bundleClientTransactionFlag()) { +            if (mTransactionItems == null) { +                mTransactionItems = new ArrayList<>(); +            } +            mTransactionItems.add(item);          } -        mTransactionItems.add(item);          // TODO(b/324203798): cleanup after remove UnsupportedAppUsage +        // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.          if (item.isActivityLifecycleItem()) {              setLifecycleStateRequest((ActivityLifecycleItem) item);          } else { @@ -114,7 +118,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {       */      // TODO(b/324203798): cleanup after remove UnsupportedAppUsage      @Deprecated -    public void addCallback(@NonNull ClientTransactionItem activityCallback) { +    private void addCallback(@NonNull ClientTransactionItem activityCallback) {          if (mActivityCallbacks == null) {              mActivityCallbacks = new ArrayList<>();          } @@ -169,7 +173,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {       */      // TODO(b/324203798): cleanup after remove UnsupportedAppUsage      @Deprecated -    public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) { +    private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {          if (mLifecycleStateRequest != null) {              return;          } diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java index 9f97f6ff7c39..1a8136e06c28 100644 --- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java +++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java @@ -23,7 +23,6 @@ import static java.util.Objects.requireNonNull;  import android.annotation.NonNull;  import android.app.ActivityThread;  import android.hardware.display.DisplayManagerGlobal; -import android.os.Process;  import com.android.internal.annotations.VisibleForTesting; @@ -67,7 +66,7 @@ public class ClientTransactionListenerController {       * window configuration.       */      public void onDisplayChanged(int displayId) { -        if (!isBundleClientTransactionFlagEnabled()) { +        if (!bundleClientTransactionFlag()) {              return;          }          if (ActivityThread.isSystem()) { @@ -76,10 +75,4 @@ public class ClientTransactionListenerController {          }          mDisplayManager.handleDisplayChangeFromWindowManager(displayId);      } - -    /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */ -    public boolean isBundleClientTransactionFlagEnabled() { -        // Can't read flag from isolated process. -        return !Process.isIsolated() && bundleClientTransactionFlag(); -    }  } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 406e00a84710..fa73c99be2b8 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -40,7 +40,6 @@ import android.app.ClientTransactionHandler;  import android.content.Context;  import android.content.res.Configuration;  import android.os.IBinder; -import android.os.Process;  import android.os.Trace;  import android.util.ArrayMap;  import android.util.ArraySet; @@ -218,8 +217,6 @@ public class TransactionExecutor {          final boolean shouldTrackConfigUpdatedContext =                  // No configuration change for local transaction.                  !mTransactionHandler.isExecutingLocalTransaction() -                        // Can't read flag from isolated process. -                        && !Process.isIsolated()                          && bundleClientTransactionFlag();          final Context configUpdatedContext = shouldTrackConfigUpdatedContext                  ? item.getContextToUpdate(mTransactionHandler) diff --git a/core/java/android/app/ui_mode_manager.aconfig b/core/java/android/app/ui_mode_manager.aconfig index 1ae5264a7e8e..27a38cc2bfd6 100644 --- a/core/java/android/app/ui_mode_manager.aconfig +++ b/core/java/android/app/ui_mode_manager.aconfig @@ -1,8 +1,11 @@  package: "android.app"  flag { -     namespace: "system_performance" -     name: "enable_night_mode_cache" +     namespace: "systemui" +     name: "enable_night_mode_binder_cache"       description: "Enables the use of binder caching for system night mode."       bug: "255999432" +     metadata { +         purpose: PURPOSE_BUGFIX +     }  }
\ No newline at end of file diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index ab8db6e59ddb..24d6a5cfc42d 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -29,3 +29,18 @@ flag {       bug: "291737188"       is_fixed_read_only: true  } + +flag { +     namespace: "virtual_devices" +     name: "metrics_collection" +     description: "Enable collection of VDM-related metrics" +     bug: "324842215" +     is_fixed_read_only: true +} + +flag { +     namespace: "virtual_devices" +     name: "camera_device_awareness" +     description: "Enable device awareness in camera service" +     bug: "305170199" +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 58cea0d46681..e6e7fa883d54 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6451,6 +6451,19 @@ public abstract class Context {      @SystemApi      public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing"; + +    /** +     * Use with {@link #getSystemService(String)} to retrieve a +     * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}. +     * +     * @see #getSystemService(String) +     * @see OnDeviceIntelligenceManager +     * @hide +     */ +    @SystemApi +    @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +    public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence"; +      /**       * Use with {@link #getSystemService(String)} to retrieve a       * {@link android.health.connect.HealthConnectManager}. diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java index 53a9a75fbca0..c09106206c25 100644 --- a/core/java/android/hardware/display/BrightnessInfo.java +++ b/core/java/android/hardware/display/BrightnessInfo.java @@ -80,6 +80,11 @@ public final class BrightnessInfo implements Parcelable {       */      public static final int BRIGHTNESS_MAX_REASON_POWER_IC = 2; +    /** +     * Maximum brightness is restricted due to the Wear bedtime mode. +     */ +    public static final int BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE = 3; +      /** Brightness */      public final float brightness; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 8f0e0c911f56..eb26a768f2a6 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -28,6 +28,7 @@ import android.annotation.LongDef;  import android.annotation.NonNull;  import android.annotation.Nullable;  import android.annotation.RequiresPermission; +import android.annotation.SuppressLint;  import android.annotation.SystemApi;  import android.annotation.SystemService;  import android.annotation.TestApi; @@ -367,6 +368,8 @@ public final class DisplayManager {       * @see #createVirtualDisplay       * @hide       */ +    @SuppressLint("UnflaggedApi") // @TestApi without associated feature. +    @TestApi      public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;      /** diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java index 1cc910c87b4f..e47a48d7aabc 100644 --- a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java +++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java @@ -48,14 +48,13 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {      private static final int GRAVITY_RIGHT = 0x2;      private static final int GRAVITY_TOP = 0x4;      private static final int GRAVITY_BOTTOM = 0x8; -    private static final int GRAVITY_CENTER = -            GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM; -    private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT; +    private static final int TEXT_PADDING_IN_DP = 1;      private static final int KEY_PADDING_IN_DP = 3;      private static final int KEYBOARD_PADDING_IN_DP = 10;      private static final int KEY_RADIUS_IN_DP = 5;      private static final int KEYBOARD_RADIUS_IN_DP = 10; -    private static final int GLYPH_TEXT_SIZE_IN_SP = 10; +    private static final int MIN_GLYPH_TEXT_SIZE_IN_SP = 10; +    private static final int MAX_GLYPH_TEXT_SIZE_IN_SP = 20;      private final List<KeyDrawable> mKeyDrawables = new ArrayList<>(); @@ -107,6 +106,8 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {          }          int rowCount = keys.length;          float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount; +        // Based on key height calculate the max text size that can fit for typing keys +        mResourceProvider.calculateBestTextSizeForKey(keyHeight);          float isoEnterKeyLeft = 0;          float isoEnterKeyTop = 0;          float isoEnterWidthUnit = 0; @@ -136,16 +137,19 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {                  }                  if (PhysicalKeyLayout.isSpecialKey(row[j])) {                      mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius, +                            mResourceProvider.getTextPadding(),                              mResourceProvider.getSpecialKeyPaint(),                              mResourceProvider.getSpecialKeyPaint(),                              mResourceProvider.getSpecialKeyPaint()));                  } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {                      mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect, -                            keyRadius, mResourceProvider.getTypingKeyPaint(), +                            keyRadius, mResourceProvider.getTextPadding(), +                            mResourceProvider.getTypingKeyPaint(),                              mResourceProvider.getPrimaryGlyphPaint(),                              mResourceProvider.getSecondaryGlyphPaint()));                  } else {                      mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius, +                            mResourceProvider.getTextPadding(),                              mResourceProvider.getTypingKeyPaint(),                              mResourceProvider.getPrimaryGlyphPaint(),                              mResourceProvider.getSecondaryGlyphPaint())); @@ -192,15 +196,18 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {          private final RectF mKeyRect;          private final float mKeyRadius; +        private final float mTextPadding;          private final Paint mKeyPaint;          private final Paint mBaseTextPaint;          private final Paint mModifierTextPaint;          private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();          private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect, -                float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) { +                float keyRadius, float textPadding, Paint keyPaint, Paint baseTextPaint, +                Paint modifierTextPaint) {              mKeyRect = keyRect;              mKeyRadius = keyRadius; +            mTextPadding = textPadding;              mKeyPaint = keyPaint;              mBaseTextPaint = baseTextPaint;              mModifierTextPaint = modifierTextPaint; @@ -219,20 +226,17 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {              if (!glyphData.hasBaseText()) {                  return;              } -            boolean isCenter = !glyphData.hasValidAltGrText() && !glyphData.hasValidAltShiftText();              mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(), -                    GRAVITY_BOTTOM | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT), -                    mBaseTextPaint)); +                    GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));              if (glyphData.hasValidShiftText()) {                  mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(), -                        GRAVITY_TOP | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT), -                        mModifierTextPaint)); +                        GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));              }              if (glyphData.hasValidAltGrText()) {                  mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),                          GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));              } -            if (glyphData.hasValidAltShiftText()) { +            if (glyphData.hasValidAltGrShiftText()) {                  mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrShiftText(), new RectF(),                          GRAVITY_TOP | GRAVITY_RIGHT, mModifierTextPaint));              } @@ -246,15 +250,19 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {                  float centerY = keyHeight / 2;                  if ((glyph.gravity & GRAVITY_LEFT) != 0) {                      centerX -= keyWidth / 4; +                    centerX += mTextPadding / 2;                  }                  if ((glyph.gravity & GRAVITY_RIGHT) != 0) {                      centerX += keyWidth / 4; +                    centerX -= mTextPadding / 2;                  }                  if ((glyph.gravity & GRAVITY_TOP) != 0) {                      centerY -= keyHeight / 4; +                    centerY += mTextPadding / 2;                  }                  if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {                      centerY += keyHeight / 4; +                    centerY -= mTextPadding / 2;                  }                  Rect textBounds = new Rect();                  glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds); @@ -285,9 +293,9 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {      private static class UnsureTypingKey extends TypingKey {          private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, -                RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint, -                Paint modifierTextPaint) { -            super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint), +                RectF keyRect, float keyRadius, float textPadding, Paint keyPaint, +                Paint baseTextPaint, Paint modifierTextPaint) { +            super(glyphData, keyRect, keyRadius, textPadding, createGreyedOutPaint(keyPaint),                      createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));          }      } @@ -402,8 +410,11 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {          private final Paint mSecondaryGlyphPaint;          private final int mKeyPadding;          private final int mKeyboardPadding; +        private final float mTextPadding;          private final float mKeyRadius;          private final float mBackgroundRadius; +        private final float mSpToPxMultiplier; +        private final Paint.FontMetrics mFontMetrics;          private ResourceProvider(Context context) {              mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, @@ -414,8 +425,10 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {                      KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());              mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                      KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics()); -            int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, -                    GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics()); +            mSpToPxMultiplier = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1, +                    context.getResources().getDisplayMetrics()); +            mTextPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, +                    TEXT_PADDING_IN_DP, context.getResources().getDisplayMetrics());              boolean isDark = (context.getResources().getConfiguration().uiMode                      & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;              int typingKeyColor = context.getColor( @@ -430,15 +443,37 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {              int backgroundColor = context.getColor(                      isDark ? android.R.color.system_surface_container_dark                              : android.R.color.system_surface_container_light); -            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize, +            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, +                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,                      Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)); -            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize, +            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, +                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,                      Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL)); +            mFontMetrics = mPrimaryGlyphPaint.getFontMetrics();              mTypingKeyPaint = createFillPaint(typingKeyColor);              mSpecialKeyPaint = createFillPaint(specialKeyColor);              mBackgroundPaint = createFillPaint(backgroundColor);          } +        private void calculateBestTextSizeForKey(float keyHeight) { +            int textSize = (int) (mSpToPxMultiplier * MIN_GLYPH_TEXT_SIZE_IN_SP) + 1; +            while (textSize < mSpToPxMultiplier * MAX_GLYPH_TEXT_SIZE_IN_SP) { +                updateTextSize(textSize); +                if (mFontMetrics.bottom - mFontMetrics.top + 3 * mTextPadding > keyHeight / 2) { +                    textSize--; +                    break; +                } +                textSize++; +            } +            updateTextSize(textSize); +        } + +        private void updateTextSize(float textSize) { +            mPrimaryGlyphPaint.setTextSize(textSize); +            mSecondaryGlyphPaint.setTextSize(textSize); +            mPrimaryGlyphPaint.getFontMetrics(mFontMetrics); +        } +          private Paint getBackgroundPaint() {              return mBackgroundPaint;          } @@ -467,6 +502,10 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {              return mKeyboardPadding;          } +        private float getTextPadding() { +            return mTextPadding; +        } +          private float getKeyRadius() {              return mKeyRadius;          } @@ -476,7 +515,8 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {          }      } -    private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) { +    private static Paint createTextPaint(@ColorInt int textColor, float textSize, +            Typeface typeface) {          Paint paint = new Paint();          paint.setColor(textColor);          paint.setStyle(Paint.Style.FILL); diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java index 844e02f00777..cff444fc84bf 100644 --- a/core/java/android/hardware/input/PhysicalKeyLayout.java +++ b/core/java/android/hardware/input/PhysicalKeyLayout.java @@ -336,11 +336,13 @@ final class PhysicalKeyLayout {              return "";          }          int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK); +        if (utf8Char == 0) { +            return ""; +        }          if (Character.isValidCodePoint(utf8Char)) {              return String.valueOf(Character.toChars(utf8Char)); -        } else { -            return String.valueOf(kcm.getDisplayLabel(keyCode));          } +        return "â–¡";      }      private static LayoutKey getKey(int keyCode, float keyWeight) { @@ -434,10 +436,11 @@ final class PhysicalKeyLayout {          }          public boolean hasValidAltGrText() { -            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText); +            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText) +                    && !TextUtils.equals(mShiftText, mAltGrText);          } -        public boolean hasValidAltShiftText() { +        public boolean hasValidAltGrShiftText() {              return !TextUtils.isEmpty(mAltGrShiftText)                      && !TextUtils.equals(mBaseText, mAltGrShiftText)                      && !TextUtils.equals(mAltGrText, mAltGrShiftText) diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java index 6eb2ae38ed82..6a7d19535ac9 100644 --- a/core/java/android/hardware/input/VirtualKeyboard.java +++ b/core/java/android/hardware/input/VirtualKeyboard.java @@ -18,7 +18,9 @@ package android.hardware.input;  import android.annotation.NonNull;  import android.annotation.RequiresPermission; +import android.annotation.SuppressLint;  import android.annotation.SystemApi; +import android.annotation.TestApi;  import android.companion.virtual.IVirtualDevice;  import android.os.IBinder;  import android.os.RemoteException; @@ -66,4 +68,15 @@ public class VirtualKeyboard extends VirtualInputDevice {              throw e.rethrowFromSystemServer();          }      } + +    /** +     * @return The id of the {@link android.view.InputDevice} corresponding to this keyboard. +     * @hide +     */ +    @SuppressLint("UnflaggedApi") // @TestApi without associated feature. +    @TestApi +    @Override +    public int getInputDeviceId() { +        return super.getInputDeviceId(); +    }  } diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig index 446fe3de6482..c5acc2ceb968 100644 --- a/core/java/android/service/notification/flags.aconfig +++ b/core/java/android/service/notification/flags.aconfig @@ -1,4 +1,5 @@  package: "android.service.notification" +container: "system"  flag {    name: "ranking_update_ashmem" @@ -12,6 +13,7 @@ flag {    namespace: "systemui"    description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"    bug: "306271190" +  is_exported: true  }  flag { diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl new file mode 100644 index 000000000000..bbb4bc6c0272 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import android.os.PersistableBundle; +import android.os.ParcelFileDescriptor; +import android.os.ICancellationSignal; +import android.os.RemoteCallback; +import android.app.ondeviceintelligence.IDownloadCallback; +import android.app.ondeviceintelligence.Feature; +import android.app.ondeviceintelligence.IFeatureCallback; +import android.app.ondeviceintelligence.IListFeaturesCallback; +import android.app.ondeviceintelligence.IFeatureDetailsCallback; +import com.android.internal.infra.AndroidFuture; + + +/** + * Interface for a concrete implementation to provide on device intelligence services. + * + * @hide + */ +oneway interface IOnDeviceIntelligenceService { +    void getVersion(in RemoteCallback remoteCallback); +    void getFeature(in int featureId, in IFeatureCallback featureCallback); +    void listFeatures(in IListFeaturesCallback listFeaturesCallback); +    void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback); +    void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future); +    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback); +    void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback); +}
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl new file mode 100644 index 000000000000..08eb9278fcc4 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import android.app.ondeviceintelligence.IStreamingResponseCallback; +import android.app.ondeviceintelligence.IResponseCallback; +import android.app.ondeviceintelligence.ITokenCountCallback; +import android.app.ondeviceintelligence.IProcessingSignal; +import android.app.ondeviceintelligence.Content; +import android.app.ondeviceintelligence.Feature; +import android.os.ICancellationSignal; +import android.service.ondeviceintelligence.IRemoteStorageService; + + +/** + * Interface for a concrete implementation to provide on device trusted inference. + * + * @hide + */ +oneway interface IOnDeviceTrustedInferenceService { +    void registerRemoteStorageService(in IRemoteStorageService storageService); +    void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal cancellationSignal, +                            in ITokenCountCallback tokenCountCallback); +    void processRequest(in Feature feature, in Content request, in int requestType, +                        in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal, +                        in IResponseCallback callback); +    void processRequestStreaming(in Feature feature, in Content request, in int requestType, +                                in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal, +                                in IStreamingResponseCallback callback); +}
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl new file mode 100644 index 000000000000..a6f49e17d80e --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import android.app.ondeviceintelligence.Feature; +import android.os.ParcelFileDescriptor; +import android.os.RemoteCallback; + +import com.android.internal.infra.AndroidFuture; + +/** + * Interface for a concrete implementation to provide access to storage read access + * for the isolated process. + * + * @hide + */ +oneway interface IRemoteStorageService { +    void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future); +    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback); +}
\ No newline at end of file diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java new file mode 100644 index 000000000000..0cba1d37721a --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.app.Service; +import android.app.ondeviceintelligence.DownloadCallback; +import android.app.ondeviceintelligence.Feature; +import android.app.ondeviceintelligence.FeatureDetails; +import android.app.ondeviceintelligence.IDownloadCallback; +import android.app.ondeviceintelligence.IFeatureCallback; +import android.app.ondeviceintelligence.IFeatureDetailsCallback; +import android.app.ondeviceintelligence.IListFeaturesCallback; +import android.app.ondeviceintelligence.OnDeviceIntelligenceManager; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.IBinder; +import android.os.ICancellationSignal; +import android.os.OutcomeReceiver; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.LongConsumer; + +/** + * Abstract base class for performing setup for on-device inference and providing file access to + * the isolated counter part {@link OnDeviceTrustedInferenceService}. + * + * <p> A service that provides configuration and model files relevant to performing inference on + * device. The system's default OnDeviceIntelligenceService implementation is configured in + * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is + * returned. + * + * <pre> + * {@literal + * <service android:name=".SampleOnDeviceIntelligenceService" + *          android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE"> + * </service>} + * </pre> + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public abstract class OnDeviceIntelligenceService extends Service { +    private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName(); + +    /** +     * The {@link Intent} that must be declared as handled by the service. To be supported, the +     * service must also require the +     * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE} +     * permission so that other applications can not abuse it. +     */ +    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) +    public static final String SERVICE_INTERFACE = +            "android.service.ondeviceintelligence.OnDeviceIntelligenceService"; + +    /** +     * @hide +     */ +    @Nullable +    @Override +    public final IBinder onBind(@NonNull Intent intent) { +        if (SERVICE_INTERFACE.equals(intent.getAction())) { +            return new IOnDeviceIntelligenceService.Stub() { +                /** {@inheritDoc} */ +                @Override +                public void getVersion(RemoteCallback remoteCallback) { +                    Objects.requireNonNull(remoteCallback); +                    OnDeviceIntelligenceService.this.onGetVersion(l -> { +                        Bundle b = new Bundle(); +                        b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l); +                        remoteCallback.sendResult(b); +                    }); +                } + +                @Override +                public void listFeatures(IListFeaturesCallback listFeaturesCallback) { +                    Objects.requireNonNull(listFeaturesCallback); +                    OnDeviceIntelligenceService.this.onListFeatures( +                            wrapListFeaturesCallback(listFeaturesCallback)); +                } + +                @Override +                public void getFeature(int id, IFeatureCallback featureCallback) { +                    Objects.requireNonNull(featureCallback); +                    OnDeviceIntelligenceService.this.onGetFeature(id, +                            wrapFeatureCallback(featureCallback)); +                } + + +                @Override +                public void getFeatureDetails(Feature feature, +                        IFeatureDetailsCallback featureDetailsCallback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(featureDetailsCallback); + +                    OnDeviceIntelligenceService.this.onGetFeatureDetails(feature, +                            wrapFeatureDetailsCallback(featureDetailsCallback)); +                } + +                @Override +                public void requestFeatureDownload(Feature feature, +                        ICancellationSignal cancellationSignal, +                        IDownloadCallback downloadCallback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(downloadCallback); + +                    OnDeviceIntelligenceService.this.onDownloadFeature(feature, +                            CancellationSignal.fromTransport(cancellationSignal), +                            wrapDownloadCallback(downloadCallback)); +                } + +                @Override +                public void getReadOnlyFileDescriptor(String fileName, +                        AndroidFuture<ParcelFileDescriptor> future) { +                    Objects.requireNonNull(fileName); +                    Objects.requireNonNull(future); + +                    OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(fileName, +                            future); +                } + +                @Override +                public void getReadOnlyFeatureFileDescriptorMap( +                        Feature feature, RemoteCallback remoteCallback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(remoteCallback); + +                    OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap( +                            feature, parcelFileDescriptorMap -> { +                                Bundle bundle = new Bundle(); +                                parcelFileDescriptorMap.forEach(bundle::putParcelable); +                                remoteCallback.sendResult(bundle); +                            }); +                } +            }; +        } +        Slog.w(TAG, "Incorrect service interface, returning null."); +        return null; +    } + +    private OutcomeReceiver<Feature, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback( +            IFeatureCallback featureCallback) { +        return new OutcomeReceiver<>() { +            @Override +            public void onResult(@NonNull Feature feature) { +                try { +                    featureCallback.onSuccess(feature); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending feature: " + e); +                } +            } + +            @Override +            public void onError( +                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) { +                try { +                    featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(), +                            exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download feature: " + e); +                } +            } +        }; + +    } + +    private OutcomeReceiver<List<Feature>, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapListFeaturesCallback( +            IListFeaturesCallback listFeaturesCallback) { +        return new OutcomeReceiver<>() { +            @Override +            public void onResult(@NonNull List<Feature> features) { +                try { +                    listFeaturesCallback.onSuccess(features); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending feature: " + e); +                } +            } + +            @Override +            public void onError( +                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) { +                try { +                    listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(), +                            exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download feature: " + e); +                } +            } +        }; +    } + +    private OutcomeReceiver<FeatureDetails, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureDetailsCallback( +            IFeatureDetailsCallback featureStatusCallback) { +        return new OutcomeReceiver<>() { +            @Override +            public void onResult(FeatureDetails result) { +                try { +                    featureStatusCallback.onSuccess(result); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending feature status: " + e); +                } +            } + +            @Override +            public void onError( +                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) { +                try { +                    featureStatusCallback.onFailure(exception.getErrorCode(), +                            exception.getMessage(), exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending feature status: " + e); +                } +            } +        }; +    } + + +    private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) { +        return new DownloadCallback() { +            @Override +            public void onDownloadStarted(long bytesToDownload) { +                try { +                    downloadCallback.onDownloadStarted(bytesToDownload); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download status: " + e); +                } +            } + +            @Override +            public void onDownloadFailed(int failureStatus, +                    String errorMessage, @NonNull PersistableBundle errorParams) { +                try { +                    downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download status: " + e); +                } +            } + +            @Override +            public void onDownloadProgress(long totalBytesDownloaded) { +                try { +                    downloadCallback.onDownloadProgress(totalBytesDownloaded); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download status: " + e); +                } +            } + +            @Override +            public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) { +                try { +                    downloadCallback.onDownloadCompleted(persistableBundle); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending download status: " + e); +                } +            } +        }; +    } + +    private void onGetReadOnlyFileDescriptor(@NonNull String fileName, +            @NonNull AndroidFuture<ParcelFileDescriptor> future) { +        Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName); +        Binder.withCleanCallingIdentity(() -> { +            Slog.v(TAG, +                    "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage."); +            File f = new File(getBaseContext().getFilesDir(), fileName); +            ParcelFileDescriptor pfd = null; +            try { +                pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); +                Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor."); +            } catch (FileNotFoundException e) { +                Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned."); +            } finally { +                future.complete(pfd); +            } +        }); +    } + +    /** +     * Provide implementation for a scenario when caller wants to get all feature related +     * file-descriptors that might be required for processing a request for the corresponding the +     * feature. +     * +     * @param feature                   the feature for which files need to be opened. +     * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and +     *                                  corresponding ParcelDescriptor to be used in a remote +     *                                  service. +     */ +    public abstract void onGetReadOnlyFeatureFileDescriptorMap( +            @NonNull Feature feature, +            @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer); + +    /** +     * Request download for feature that is requested and listen to download progress updates. If +     * the download completes successfully, success callback should be populated. +     * +     * @param feature            the feature for which files need to be downlaoded. +     *                           process. +     * @param cancellationSignal signal to attach a listener to, and receive cancellation signals +     *                           from thw client. +     * @param downloadCallback   callback to populate download updates for clients to listen on.. +     */ +    public abstract void onDownloadFeature( +            @NonNull Feature feature, +            @Nullable CancellationSignal cancellationSignal, +            @NonNull DownloadCallback downloadCallback); + +    /** +     * Provide feature details for the passed in feature. Usually the client and remote +     * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what +     * details the client is looking for. +     * +     * @param feature               the feature for which status needs to be known. +     * @param featureStatusCallback callback to populate the resulting feature status. +     */ +    public abstract void onGetFeatureDetails(@NonNull Feature feature, +            @NonNull OutcomeReceiver<FeatureDetails, +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureStatusCallback); + + +    /** +     * Get feature using the provided identifier to the remote implementation. +     * +     * @param featureCallback callback to populate the features list. +     */ +    public abstract void onGetFeature(int featureId, +            @NonNull OutcomeReceiver<Feature, +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureCallback); + +    /** +     * List all features which are available in the remote implementation. The implementation might +     * choose to provide only a certain list of features based on the caller. +     * +     * @param listFeaturesCallback callback to populate the features list. +     */ +    public abstract void onListFeatures(@NonNull OutcomeReceiver<List<Feature>, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> listFeaturesCallback); + +    /** +     * Provides a long value representing the version of the remote implementation processing +     * requests. +     * +     * @param versionConsumer consumer to populate the version. +     */ +    public abstract void onGetVersion(@NonNull LongConsumer versionConsumer); +} diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java new file mode 100644 index 000000000000..96982e3d7829 --- /dev/null +++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.ondeviceintelligence; + +import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.app.Service; +import android.app.ondeviceintelligence.Content; +import android.app.ondeviceintelligence.Feature; +import android.app.ondeviceintelligence.IProcessingSignal; +import android.app.ondeviceintelligence.IResponseCallback; +import android.app.ondeviceintelligence.IStreamingResponseCallback; +import android.app.ondeviceintelligence.ITokenCountCallback; +import android.app.ondeviceintelligence.OnDeviceIntelligenceManager; +import android.app.ondeviceintelligence.ProcessingSignal; +import android.app.ondeviceintelligence.StreamingResponseReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.CancellationSignal; +import android.os.IBinder; +import android.os.ICancellationSignal; +import android.os.OutcomeReceiver; +import android.os.ParcelFileDescriptor; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Abstract base class for performing inference in a isolated process. This service exposes its + * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}. + * + * <p> A service that provides methods to perform on-device inference both in streaming and + * non-streaming fashion. Also, provides a way to register a storage service that will be used to + * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p> + * + * <pre> + * {@literal + * <service android:name=".SampleTrustedInferenceService" + *          android:permission="android.permission.BIND_ONDEVICE_TRUSTED_INFERENCE_SERVICE" + *          android:isolatedProcess="true"> + * </service>} + * </pre> + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) +public abstract class OnDeviceTrustedInferenceService extends Service { +    private static final String TAG = OnDeviceTrustedInferenceService.class.getSimpleName(); + +    /** +     * The {@link Intent} that must be declared as handled by the service. To be supported, the +     * service must also require the +     * {@link android.Manifest.permission#BIND_ON_DEVICE_TRUSTED_INFERENCE_SERVICE} +     * permission so that other applications can not abuse it. +     */ +    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) +    public static final String SERVICE_INTERFACE = +            "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService"; + +    private IRemoteStorageService mRemoteStorageService; + +    /** +     * @hide +     */ +    @Nullable +    @Override +    public final IBinder onBind(@NonNull Intent intent) { +        if (SERVICE_INTERFACE.equals(intent.getAction())) { +            return new IOnDeviceTrustedInferenceService.Stub() { +                @Override +                public void registerRemoteStorageService(IRemoteStorageService storageService) { +                    Objects.requireNonNull(storageService); +                    mRemoteStorageService = storageService; +                } + +                @Override +                public void requestTokenCount(Feature feature, Content request, +                        ICancellationSignal cancellationSignal, +                        ITokenCountCallback tokenCountCallback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(tokenCountCallback); +                    OnDeviceTrustedInferenceService.this.onCountTokens(feature, +                            request, +                            CancellationSignal.fromTransport(cancellationSignal), +                            wrapTokenCountCallback(tokenCountCallback)); +                } + +                @Override +                public void processRequestStreaming(Feature feature, Content request, +                        int requestType, ICancellationSignal cancellationSignal, +                        IProcessingSignal processingSignal, +                        IStreamingResponseCallback callback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(request); +                    Objects.requireNonNull(callback); + +                    OnDeviceTrustedInferenceService.this.onProcessRequestStreaming(feature, +                            request, +                            requestType, +                            CancellationSignal.fromTransport(cancellationSignal), +                            ProcessingSignal.fromTransport(processingSignal), +                            wrapStreamingResponseCallback(callback) +                    ); +                } + +                @Override +                public void processRequest(Feature feature, Content request, +                        int requestType, ICancellationSignal cancellationSignal, +                        IProcessingSignal processingSignal, +                        IResponseCallback callback) { +                    Objects.requireNonNull(feature); +                    Objects.requireNonNull(request); +                    Objects.requireNonNull(callback); + + +                    OnDeviceTrustedInferenceService.this.onProcessRequest(feature, request, +                            requestType, CancellationSignal.fromTransport(cancellationSignal), +                            ProcessingSignal.fromTransport(processingSignal), +                            wrapResponseCallback(callback) +                    ); +                } +            }; +        } +        Slog.w(TAG, "Incorrect service interface, returning null."); +        return null; +    } + +    /** +     * Invoked when caller  wants to obtain a count of number of tokens present in the passed in +     * Request associated with the provided feature. +     * The expectation from the implementation is that when processing is complete, it +     * should provide the token count in the {@link OutcomeReceiver#onResult}. +     * +     * @param feature            feature which is associated with the request. +     * @param request            request that requires processing. +     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and +     *                           configure a listener to. +     * @param callback           callback to populate failure and full response for the provided +     *                           request. +     */ +    @NonNull +    public abstract void onCountTokens( +            @NonNull Feature feature, +            @NonNull Content request, +            @Nullable CancellationSignal cancellationSignal, +            @NonNull OutcomeReceiver<Long, +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback); + +    /** +     * Invoked when caller provides a request for a particular feature to be processed in a +     * streaming manner. The expectation from the implementation is that when processing the +     * request, +     * it periodically populates the {@link StreamingResponseReceiver#onNewContent} to continuously +     * provide partial Content results for the caller to utilize. Optionally the implementation can +     * provide the complete response in the {@link StreamingResponseReceiver#onResult} upon +     * processing completion. +     * +     * @param feature            feature which is associated with the request. +     * @param request            request that requires processing. +     * @param requestType        identifier representing the type of request. +     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and +     *                           configure a listener to. +     * @param processingSignal   Signal to receive custom action instructions from client. +     * @param callback           callback to populate the partial responses, failure and optionally +     *                           full response for the provided request. +     */ +    @NonNull +    public abstract void onProcessRequestStreaming( +            @NonNull Feature feature, +            @NonNull Content request, +            @OnDeviceIntelligenceManager.RequestType int requestType, +            @Nullable CancellationSignal cancellationSignal, +            @Nullable ProcessingSignal processingSignal, +            @NonNull StreamingResponseReceiver<Content, Content, +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback); + +    /** +     * Invoked when caller provides a request for a particular feature to be processed in one shot +     * completely. +     * The expectation from the implementation is that when processing the request is complete, it +     * should +     * provide the complete response in the {@link OutcomeReceiver#onResult}. +     * +     * @param feature            feature which is associated with the request. +     * @param request            request that requires processing. +     * @param requestType        identifier representing the type of request. +     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and +     *                           configure a listener to. +     * @param processingSignal   Signal to receive custom action instructions from client. +     * @param callback           callback to populate failure and full response for the provided +     *                           request. +     */ +    @NonNull +    public abstract void onProcessRequest( +            @NonNull Feature feature, +            @NonNull Content request, +            @OnDeviceIntelligenceManager.RequestType int requestType, +            @Nullable CancellationSignal cancellationSignal, +            @Nullable ProcessingSignal processingSignal, +            @NonNull OutcomeReceiver<Content, +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback); + +    /** +     * Overrides {@link Context#openFileInput} to read files with the given file names under the +     * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in +     * {@link Context#getFilesDir()} can be opened. +     */ +    @Override +    public final FileInputStream openFileInput(@NonNull String filename) throws +            FileNotFoundException { +        try { +            AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>(); +            mRemoteStorageService.getReadOnlyFileDescriptor(filename, future); +            ParcelFileDescriptor pfd = future.get(); +            return new FileInputStream(pfd.getFileDescriptor()); +        } catch (RemoteException | ExecutionException | InterruptedException e) { +            Log.w(TAG, "Cannot open file due to remote service failure"); +            throw new FileNotFoundException(e.getMessage()); +        } +    } + +    /** +     * Provides read-only access to the internal app storage via the +     * {@link OnDeviceIntelligenceService}. This is an asynchronous implementation for +     * {@link #openFileInput(String)}. +     * +     * @param fileName       File name relative to the {@link Context#getFilesDir()}. +     * @param resultConsumer Consumer to populate the corresponding file stream in. +     */ +    public final void openFileInputAsync(@NonNull String fileName, +            @NonNull @CallbackExecutor Executor executor, +            @NonNull Consumer<FileInputStream> resultConsumer) throws FileNotFoundException { +        AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>(); +        try { +            mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future); +        } catch (RemoteException e) { +            Log.w(TAG, "Cannot open file due to remote service failure"); +            throw new FileNotFoundException(e.getMessage()); +        } +        future.whenCompleteAsync((pfd, err) -> { +            if (err != null) { +                Log.e(TAG, "Failure when reading file: " + fileName + err); +                executor.execute(() -> resultConsumer.accept(null)); +            } else { +                executor.execute( +                        () -> resultConsumer.accept(new FileInputStream(pfd.getFileDescriptor()))); +            } +        }, executor); +    } + +    /** +     * Provides access to all file streams required for feature via the +     * {@link OnDeviceIntelligenceService}. +     * +     * @param feature        Feature for which the associated files should be fetched. +     * @param executor       Executor to run the consumer callback on. +     * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input +     *                       stream. +     */ +    public final void fetchFeatureFileInputStreamMap(@NonNull Feature feature, +            @NonNull @CallbackExecutor Executor executor, +            @NonNull Consumer<Map<String, FileInputStream>> resultConsumer) { +        try { +            mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature, +                    wrapResultReceiverAsReadOnly(resultConsumer, executor)); +        } catch (RemoteException e) { +            throw new RuntimeException(e); +        } +    } + +    private RemoteCallback wrapResultReceiverAsReadOnly( +            @NonNull Consumer<Map<String, FileInputStream>> resultConsumer, +            @NonNull Executor executor) { +        return new RemoteCallback(result -> { +            if (result == null) { +                executor.execute(() -> resultConsumer.accept(new HashMap<>())); +            } else { +                Map<String, FileInputStream> bundleMap = new HashMap<>(); +                result.keySet().forEach(key -> { +                    ParcelFileDescriptor pfd = result.getParcelable(key, +                            ParcelFileDescriptor.class); +                    if (pfd != null) { +                        bundleMap.put(key, new FileInputStream(pfd.getFileDescriptor())); +                    } +                }); +                executor.execute(() -> resultConsumer.accept(bundleMap)); +            } +        }); +    } + +    private OutcomeReceiver<Content, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapResponseCallback( +            IResponseCallback callback) { +        return new OutcomeReceiver<>() { +            @Override +            public void onResult(@androidx.annotation.NonNull Content response) { +                try { +                    callback.onSuccess(response); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } + +            @Override +            public void onError( +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) { +                try { +                    callback.onFailure(exception.getErrorCode(), exception.getMessage(), +                            exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } +        }; +    } + +    private StreamingResponseReceiver<Content, Content, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapStreamingResponseCallback( +            IStreamingResponseCallback callback) { +        return new StreamingResponseReceiver<>() { +            @Override +            public void onNewContent(@androidx.annotation.NonNull Content content) { +                try { +                    callback.onNewContent(content); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } + +            @Override +            public void onResult(@androidx.annotation.NonNull Content response) { +                try { +                    callback.onSuccess(response); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } + +            @Override +            public void onError( +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) { +                try { +                    callback.onFailure(exception.getErrorCode(), exception.getMessage(), +                            exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } +        }; +    } + +    private OutcomeReceiver<Long, +            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapTokenCountCallback( +            ITokenCountCallback tokenCountCallback) { +        return new OutcomeReceiver<>() { +            @Override +            public void onResult(Long tokenCount) { +                try { +                    tokenCountCallback.onSuccess(tokenCount); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending result: " + e); +                } +            } + +            @Override +            public void onError( +                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) { +                try { +                    tokenCountCallback.onFailure(exception.getErrorCode(), exception.getMessage(), +                            exception.getErrorParams()); +                } catch (RemoteException e) { +                    Slog.e(TAG, "Error sending failure: " + e); +                } +            } +        }; +    } +} diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index bd9f5049321d..83683caa2ea8 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2364,6 +2364,7 @@ public final class AutofillManager {          synchronized (mLock) {              if (!isActiveLocked()) { +                Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");                  return;              }              mState = STATE_ACTIVE; @@ -2380,6 +2381,7 @@ public final class AutofillManager {              }              if (data == null) {                  // data is set to null when result is not RESULT_OK +                Log.i(TAG, "onAuthenticationResult(): empty intent");                  return;              } diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java index dc5e0e5c62aa..88ca2a48d2f4 100644 --- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java @@ -66,6 +66,7 @@ final class IInputMethodManagerGlobalInvoker {      @Nullable      private static volatile IImeTracker sTrackerServiceCache = null; +    private static int sCurStartInputSeq = 0;      /**       * @return {@code true} if {@link IInputMethodManager} is available. @@ -327,6 +328,7 @@ final class IInputMethodManagerGlobalInvoker {          }      } +    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.      @AnyThread      @NonNull      @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) @@ -353,6 +355,41 @@ final class IInputMethodManagerGlobalInvoker {          }      } +    /** +     * Returns a sequence number for startInput. +     */ +    @AnyThread +    @NonNull +    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true) +    static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason, +            @NonNull IInputMethodClient client, @Nullable IBinder windowToken, +            @StartInputFlags int startInputFlags, +            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, +            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo, +            @Nullable IRemoteInputConnection remoteInputConnection, +            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, +            int unverifiedTargetSdkVersion, @UserIdInt int userId, +            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { +        final IInputMethodManager service = getService(); +        if (service == null) { +            return -1; +        } +        try { +            service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken, +                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection, +                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId, +                    imeDispatcher, advanceAngGetStartInputSequenceNumber()); +        } catch (RemoteException e) { +            throw e.rethrowFromSystemServer(); +        } +        return sCurStartInputSeq; +    } + +    private static int advanceAngGetStartInputSequenceNumber() { +        return ++sCurStartInputSeq; +    } + +      @AnyThread      static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,              int auxiliarySubtypeMode) { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index f4b09df35705..72125ba999ad 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -321,6 +321,22 @@ public final class InputMethodManager {      };      /** +     * A runnable that reports {@link InputConnection} opened event for calls to +     * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}. +     */ +    private abstract static class ReportInputConnectionOpenedRunner implements Runnable { +        /** +         * Sequence number to track startInput requests to +         * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync} +         */ +        int mSequenceNum; +        ReportInputConnectionOpenedRunner(int sequenceNum) { +            this.mSequenceNum = sequenceNum; +        } +    } +    private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner; + +    /**       * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly       * or indirectly relied on {@link #sInstance} via reflection or something like that.       * @@ -691,6 +707,7 @@ public final class InputMethodManager {      private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;      private static final int MSG_SET_INTERACTIVE = 13;      private static final int MSG_ON_SHOW_REQUESTED = 31; +    private static final int MSG_START_INPUT_RESULT = 40;      /**       * Calling this will invalidate Local stylus handwriting availability Cache which @@ -1045,7 +1062,7 @@ public final class InputMethodManager {                      return;                  }                  case MSG_BIND: { -                    final InputBindResult res = (InputBindResult)msg.obj; +                    final InputBindResult res = (InputBindResult) msg.obj;                      if (DEBUG) {                          Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);                      } @@ -1071,6 +1088,60 @@ public final class InputMethodManager {                      startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);                      return;                  } + +                case MSG_START_INPUT_RESULT: { +                    final InputBindResult res = (InputBindResult) msg.obj; +                    final int startInputSeq = msg.arg1; +                    if (res == null) { +                        // IMMS logs .wtf already. +                        return; +                    } +                    if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); +                    synchronized (mH) { +                        if (res.id != null) { +                            updateInputChannelLocked(res.channel); +                            mCurMethod = res.method; // for @UnsupportedAppUsage +                            mCurBindState = new BindState(res); +                            mAccessibilityInputMethodSession.clear(); +                            if (res.accessibilitySessions != null) { +                                for (int i = 0; i < res.accessibilitySessions.size(); i++) { +                                    IAccessibilityInputMethodSessionInvoker wrapper = +                                            IAccessibilityInputMethodSessionInvoker.createOrNull( +                                                    res.accessibilitySessions.valueAt(i)); +                                    if (wrapper != null) { +                                        mAccessibilityInputMethodSession.append( +                                                res.accessibilitySessions.keyAt(i), wrapper); +                                    } +                                } +                            } +                            mCurId = res.id; // for @UnsupportedAppUsage +                        } else if (res.channel != null && res.channel != mCurChannel) { +                            res.channel.dispose(); +                        } +                        switch (res.result) { +                            case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW: +                                mRestartOnNextWindowFocus = true; +                                mServedView = null; +                                break; +                        } +                        if (mCompletions != null) { +                            if (isImeSessionAvailableLocked()) { +                                mCurBindState.mImeSession.displayCompletions(mCompletions); +                            } +                        } + +                        if (res != null +                                && res.method != null +                                && mServedView != null +                                && mReportInputConnectionOpenedRunner != null +                                && mReportInputConnectionOpenedRunner.mSequenceNum +                                        == startInputSeq) { +                            mReportInputConnectionOpenedRunner.run(); +                        } +                        mReportInputConnectionOpenedRunner = null; +                    } +                    return; +                }                  case MSG_UNBIND: {                      final int sequence = msg.arg1;                      @UnbindReason @@ -1322,6 +1393,12 @@ public final class InputMethodManager {          }          @Override +        public void onStartInputResult(InputBindResult res, int startInputSeq) { +            mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res) +                    .sendToTarget(); +        } + +        @Override          public void onBindAccessibilityService(InputBindResult res, int id) {              mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();          } @@ -2010,6 +2087,7 @@ public final class InputMethodManager {              mServedConnecting = false;              clearConnectionLocked();          } +        mReportInputConnectionOpenedRunner = null;          // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.          mImeDispatcher.clear();      } @@ -3080,14 +3158,52 @@ public final class InputMethodManager {              final int targetUserId = editorInfo.targetInputMethodUser != null                      ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();              Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus"); -            res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( -                    startInputReason, mClient, windowGainingFocus, startInputFlags, -                    softInputMode, windowFlags, editorInfo, servedInputConnection, -                    servedInputConnection == null ? null -                            : servedInputConnection.asIRemoteAccessibilityInputConnection(), -                    view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, -                    mImeDispatcher); + +            int startInputSeq = -1; +            if (Flags.useZeroJankProxy()) { +                // async result delivered via MSG_START_INPUT_RESULT. +                startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync( +                        startInputReason, mClient, windowGainingFocus, startInputFlags, +                        softInputMode, windowFlags, editorInfo, servedInputConnection, +                        servedInputConnection == null ? null +                                : servedInputConnection.asIRemoteAccessibilityInputConnection(), +                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, +                        mImeDispatcher); +            } else { +                res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus( +                        startInputReason, mClient, windowGainingFocus, startInputFlags, +                        softInputMode, windowFlags, editorInfo, servedInputConnection, +                        servedInputConnection == null ? null +                                : servedInputConnection.asIRemoteAccessibilityInputConnection(), +                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId, +                        mImeDispatcher); +            }              Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); +            if (Flags.useZeroJankProxy()) { +                // Create a runnable for delayed notification to the app that the InputConnection is +                // initialized and ready for use. +                if (ic != null) { +                    final int seqId = startInputSeq; +                    mReportInputConnectionOpenedRunner = +                            new ReportInputConnectionOpenedRunner(startInputSeq) { +                                @Override +                                public void run() { +                                    if (DEBUG) { +                                        Log.v(TAG, "Calling View.onInputConnectionOpened: view= " +                                                + view +                                                + ", ic=" + ic + ", editorInfo=" + editorInfo +                                                + ", handler=" +                                                + icHandler + ", startInputSeq=" + seqId); +                                    } +                                    reportInputConnectionOpened(ic, editorInfo, icHandler, view); +                                } +                            }; +                } else { +                    mReportInputConnectionOpenedRunner = null; +                } +                return true; +            } +              if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);              if (res == null) {                  Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" @@ -3118,6 +3234,7 @@ public final class InputMethodManager {              } else if (res.channel != null && res.channel != mCurChannel) {                  res.channel.dispose();              } +              switch (res.result) {                  case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:                      mRestartOnNextWindowFocus = true; diff --git a/core/java/com/android/internal/colorextraction/OWNERS b/core/java/com/android/internal/colorextraction/OWNERS index ffade1ec4ebd..041559cd048d 100644 --- a/core/java/com/android/internal/colorextraction/OWNERS +++ b/core/java/com/android/internal/colorextraction/OWNERS @@ -1,3 +1,2 @@ -dupin@google.com  cinek@google.com -jamesoleary@google.com +arteiro@google.com diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl index 9251d2d5dc31..babd9a0950fd 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl @@ -24,6 +24,7 @@ import com.android.internal.inputmethod.InputBindResult;   */  oneway interface IInputMethodClient {      void onBindMethod(in InputBindResult res); +    void onStartInputResult(in InputBindResult res, int startInputSeq);      void onBindAccessibilityService(in InputBindResult res, int id);      void onUnbindMethod(int sequence, int unbindReason);      void onUnbindAccessibilityService(int sequence, int id); diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java index b6eca07a0858..243b1031bd4b 100644 --- a/core/java/com/android/internal/inputmethod/InputBindResult.java +++ b/core/java/com/android/internal/inputmethod/InputBindResult.java @@ -271,6 +271,7 @@ public final class InputBindResult implements Parcelable {      public String toString() {          return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id                  + " sequence=" + sequence +                + " result=" + result                  + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker                  + "}";      } diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java index 48c455aa70f2..3662d69e1974 100644 --- a/core/java/com/android/internal/jank/Cuj.java +++ b/core/java/com/android/internal/jank/Cuj.java @@ -120,7 +120,7 @@ public class Cuj {      public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;      public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;      public static final int CUJ_PREDICTIVE_BACK_HOME = 86; -    public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87; +    // 87 is reserved - previously assigned to deprecated CUJ_LAUNCHER_SEARCH_QSB_OPEN.      public static final int CUJ_BACK_PANEL_ARROW = 88;      public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;      public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90; @@ -209,7 +209,6 @@ public class Cuj {              CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,              CUJ_PREDICTIVE_BACK_CROSS_TASK,              CUJ_PREDICTIVE_BACK_HOME, -            CUJ_LAUNCHER_SEARCH_QSB_OPEN,              CUJ_BACK_PANEL_ARROW,              CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,              CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH, @@ -304,7 +303,6 @@ public class Cuj {          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; -        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;          CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH; @@ -480,8 +478,6 @@ public class Cuj {                  return "PREDICTIVE_BACK_CROSS_TASK";              case CUJ_PREDICTIVE_BACK_HOME:                  return "PREDICTIVE_BACK_HOME"; -            case CUJ_LAUNCHER_SEARCH_QSB_OPEN: -                return "LAUNCHER_SEARCH_QSB_OPEN";              case CUJ_BACK_PANEL_ARROW:                  return "BACK_PANEL_ARROW";              case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK: diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index ed43b8190f93..d463b62e62a3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -376,4 +376,10 @@ oneway interface IStatusBar       * @param packageName of the session for which the output switcher is shown.       */      void showMediaOutputSwitcher(String packageName); + +    /** Enters desktop mode. +    * +    * @param displayId the id of the current display. +    */ +    void enterDesktop(int displayId);  } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index e95127be8543..b90f8bf5ea00 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -69,6 +69,8 @@ interface IInputMethodManager {      boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,              in @nullable ImeTracker.Token statsToken, int flags,              in @nullable ResultReceiver resultReceiver, int reason); + +    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.      // If windowToken is null, this just does startInput().  Otherwise this reports that a window      // has gained focus, and if 'editorInfo' is non-null then also does startInput.      // @NonNull @@ -85,6 +87,21 @@ interface IInputMethodManager {              int unverifiedTargetSdkVersion, int userId,              in ImeOnBackInvokedDispatcher imeDispatcher); +    // If windowToken is null, this just does startInput().  Otherwise this reports that a window +    // has gained focus, and if 'editorInfo' is non-null then also does startInput. +    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " +            + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)") +    void startInputOrWindowGainedFocusAsync( +            /* @StartInputReason */ int startInputReason, +            in IInputMethodClient client, in @nullable IBinder windowToken, +            /* @StartInputFlags */ int startInputFlags, +            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, +            /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags, +            in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection, +            in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, +            int unverifiedTargetSdkVersion, int userId, +            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq); +      void showInputMethodPickerFromClient(in IInputMethodClient client,              int auxiliarySubtypeMode); @@ -156,6 +173,7 @@ interface IInputMethodManager {                  in String delegatePackageName,                  in String delegatorPackageName); +    // TODO(b/293640003): introduce a new API method to provide async way to return boolean.      /** Accepts and starts a stylus handwriting session for the delegate view **/      boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,              in String delegatePackageName, in String delegatorPackageName, int flags); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c6bc589cffcb..1f06b0b7c62b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4670,6 +4670,13 @@     -->      <string name="config_defaultWearableSensingService" translatable="false"></string> + +    <!-- The component name for the default system on-device intelligence service, --> +    <string name="config_defaultOnDeviceIntelligenceService" translatable="false"></string> + +    <!-- The component name for the default system on-device trusted inference service. --> +    <string name="config_defaultOnDeviceTrustedInferenceService" translatable="false"></string> +      <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for           wearable sensing. -->      <string translatable="false" name="config_defaultWearableSensingConsentComponent"></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9d7acffee68b..cf9c02a93267 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3909,6 +3909,8 @@    <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />    <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />    <java-symbol type="string" name="config_defaultWearableSensingService" /> +  <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" /> +  <java-symbol type="string" name="config_defaultOnDeviceTrustedInferenceService" />    <java-symbol type="string" name="config_retailDemoPackage" />    <java-symbol type="string" name="config_retailDemoPackageSignature" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 48ef7e6f11a8..ebf4cca9ebc2 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -43,7 +43,6 @@ import android.app.PictureInPictureParams;  import android.app.PictureInPictureUiState;  import android.app.ResourcesManager;  import android.app.servertransaction.ActivityConfigurationChangeItem; -import android.app.servertransaction.ActivityLifecycleItem;  import android.app.servertransaction.ActivityRelaunchItem;  import android.app.servertransaction.ClientTransaction;  import android.app.servertransaction.ClientTransactionItem; @@ -75,7 +74,6 @@ import androidx.test.rule.ActivityTestRule;  import androidx.test.runner.AndroidJUnit4;  import com.android.internal.content.ReferrerIntent; -import com.android.window.flags.Flags;  import org.junit.After;  import org.junit.Before; @@ -230,7 +228,7 @@ public class ActivityThreadTest {          try {              // Send process level config change.              ClientTransaction transaction = newTransaction(activityThread); -            addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( +            transaction.addTransactionItem(ConfigurationChangeItem.obtain(                      newConfig, DEVICE_ID_INVALID));              appThread.scheduleTransaction(transaction);              InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -247,7 +245,7 @@ public class ActivityThreadTest {              newConfig.seq++;              newConfig.smallestScreenWidthDp++;              transaction = newTransaction(activityThread); -            addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( +            transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(                      activity.getActivityToken(), newConfig));              appThread.scheduleTransaction(transaction);              InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -448,16 +446,16 @@ public class ActivityThreadTest {          activity.mTestLatch = new CountDownLatch(1);          ClientTransaction transaction = newTransaction(activityThread); -        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( +        transaction.addTransactionItem(ConfigurationChangeItem.obtain(                  processConfigLandscape, DEVICE_ID_INVALID));          appThread.scheduleTransaction(transaction);          transaction = newTransaction(activityThread); -        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( +        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(                  activity.getActivityToken(), activityConfigLandscape)); -        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( +        transaction.addTransactionItem(ConfigurationChangeItem.obtain(                  processConfigPortrait, DEVICE_ID_INVALID)); -        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( +        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(                  activity.getActivityToken(), activityConfigPortrait));          appThread.scheduleTransaction(transaction); @@ -847,8 +845,8 @@ public class ActivityThreadTest {                          false /* shouldSendCompatFakeFocus*/);          final ClientTransaction transaction = newTransaction(activity); -        addClientTransactionItem(transaction, callbackItem); -        addClientTransactionItem(transaction, resumeStateRequest); +        transaction.addTransactionItem(callbackItem); +        transaction.addTransactionItem(resumeStateRequest);          return transaction;      } @@ -860,7 +858,7 @@ public class ActivityThreadTest {                          false /* shouldSendCompatFakeFocus */);          final ClientTransaction transaction = newTransaction(activity); -        addClientTransactionItem(transaction, resumeStateRequest); +        transaction.addTransactionItem(resumeStateRequest);          return transaction;      } @@ -871,7 +869,7 @@ public class ActivityThreadTest {                  activity.getActivityToken(), 0 /* configChanges */);          final ClientTransaction transaction = newTransaction(activity); -        addClientTransactionItem(transaction, stopStateRequest); +        transaction.addTransactionItem(stopStateRequest);          return transaction;      } @@ -883,7 +881,7 @@ public class ActivityThreadTest {                  activity.getActivityToken(), config);          final ClientTransaction transaction = newTransaction(activity); -        addClientTransactionItem(transaction, item); +        transaction.addTransactionItem(item);          return transaction;      } @@ -895,7 +893,7 @@ public class ActivityThreadTest {                  resume);          final ClientTransaction transaction = newTransaction(activity); -        addClientTransactionItem(transaction, item); +        transaction.addTransactionItem(item);          return transaction;      } @@ -910,17 +908,6 @@ public class ActivityThreadTest {          return ClientTransaction.obtain(activityThread.getApplicationThread());      } -    private static void addClientTransactionItem(@NonNull ClientTransaction transaction, -            @NonNull ClientTransactionItem item) { -        if (Flags.bundleClientTransactionFlag()) { -            transaction.addTransactionItem(item); -        } else if (item.isActivityLifecycleItem()) { -            transaction.setLifecycleStateRequest((ActivityLifecycleItem) item); -        } else { -            transaction.addCallback(item); -        } -    } -      // Test activity      public static class TestActivity extends Activity {          static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter"; diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java index 95d50499b92f..213fd7bd494d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java @@ -16,10 +16,13 @@  package android.app.servertransaction; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; +  import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG; +  import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy;  import static org.mockito.Mockito.verify;  import android.hardware.display.DisplayManager; @@ -28,12 +31,14 @@ import android.hardware.display.IDisplayManager;  import android.os.Handler;  import android.os.RemoteException;  import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule;  import android.view.DisplayInfo;  import androidx.test.filters.SmallTest;  import androidx.test.runner.AndroidJUnit4;  import org.junit.Before; +import org.junit.Rule;  import org.junit.Test;  import org.junit.runner.RunWith;  import org.mockito.Mock; @@ -49,6 +54,10 @@ import org.mockito.MockitoAnnotations;  @SmallTest  @Presubmit  public class ClientTransactionListenerControllerTest { + +    @Rule +    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); +      @Mock      private IDisplayManager mIDisplayManager;      @Mock @@ -60,12 +69,12 @@ public class ClientTransactionListenerControllerTest {      @Before      public void setup() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); +          MockitoAnnotations.initMocks(this);          mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);          mHandler = getInstrumentation().getContext().getMainThreadHandler(); -        mController = spy(ClientTransactionListenerController.createInstanceForTesting( -                mDisplayManager)); -        doReturn(true).when(mController).isBundleClientTransactionFlagEnabled(); +        mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);      }      @Test diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java index d10cf1691408..527241613a7a 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java @@ -16,16 +16,22 @@  package android.app.servertransaction; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG; + +import static org.mockito.Mockito.doReturn;  import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times;  import static org.mockito.Mockito.verify;  import android.app.ClientTransactionHandler;  import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule;  import androidx.test.filters.SmallTest;  import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule;  import org.junit.Test;  import org.junit.runner.RunWith; @@ -43,31 +49,28 @@ import org.junit.runner.RunWith;  @Presubmit  public class ClientTransactionTests { +    @Rule +    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); +      @Test      public void testPreExecute() { -        final ClientTransactionItem callback1 = mock(ClientTransactionItem.class); -        final ClientTransactionItem callback2 = mock(ClientTransactionItem.class); -        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); -        final ClientTransactionHandler clientTransactionHandler = -                mock(ClientTransactionHandler.class); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.addCallback(callback1); -        transaction.addCallback(callback2); -        transaction.setLifecycleStateRequest(stateRequest); +        testPreExecuteInner(); +    } -        transaction.preExecute(clientTransactionHandler); +    @Test +    public void testPreExecute_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        verify(callback1, times(1)).preExecute(clientTransactionHandler); -        verify(callback2, times(1)).preExecute(clientTransactionHandler); -        verify(stateRequest, times(1)).preExecute(clientTransactionHandler); +        testPreExecuteInner();      } -    @Test -    public void testPreExecuteTransactionItems() { +    private void testPreExecuteInner() {          final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);          final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);          final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class); +        doReturn(true).when(stateRequest).isActivityLifecycleItem();          final ClientTransactionHandler clientTransactionHandler =                  mock(ClientTransactionHandler.class); @@ -78,8 +81,8 @@ public class ClientTransactionTests {          transaction.preExecute(clientTransactionHandler); -        verify(callback1, times(1)).preExecute(clientTransactionHandler); -        verify(callback2, times(1)).preExecute(clientTransactionHandler); -        verify(stateRequest, times(1)).preExecute(clientTransactionHandler); +        verify(callback1).preExecute(clientTransactionHandler); +        verify(callback2).preExecute(clientTransactionHandler); +        verify(stateRequest).preExecute(clientTransactionHandler);      }  } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index 2315a58eb487..adb6f2a23847 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -25,6 +25,9 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_START;  import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;  import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;  import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;  import static org.junit.Assert.assertArrayEquals;  import static org.junit.Assert.assertEquals; @@ -51,12 +54,14 @@ import android.os.IBinder;  import android.os.Parcel;  import android.os.Parcelable;  import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule;  import android.util.ArrayMap;  import androidx.test.filters.SmallTest;  import androidx.test.runner.AndroidJUnit4;  import org.junit.Before; +import org.junit.Rule;  import org.junit.Test;  import org.junit.runner.RunWith;  import org.mockito.InOrder; @@ -83,6 +88,9 @@ import java.util.stream.Collectors;  @Presubmit  public class TransactionExecutorTests { +    @Rule +    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); +      @Mock      private ClientTransactionHandler mTransactionHandler;      @Mock @@ -240,29 +248,19 @@ public class TransactionExecutorTests {      @Test      public void testTransactionResolution() { -        ClientTransactionItem callback1 = mock(ClientTransactionItem.class); -        when(callback1.getPostExecutionState()).thenReturn(UNDEFINED); -        ClientTransactionItem callback2 = mock(ClientTransactionItem.class); -        when(callback2.getPostExecutionState()).thenReturn(UNDEFINED); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.addCallback(callback1); -        transaction.addCallback(callback2); -        transaction.setLifecycleStateRequest(mActivityLifecycleItem); +        testTransactionResolutionInner(); +    } -        transaction.preExecute(mTransactionHandler); -        mExecutor.execute(transaction); +    @Test +    public void testTransactionResolution_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, -                mActivityLifecycleItem); -        inOrder.verify(callback1).execute(eq(mTransactionHandler), any()); -        inOrder.verify(callback2).execute(eq(mTransactionHandler), any()); -        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord), -                any()); +        testTransactionResolutionInner();      } -    @Test -    public void testExecuteTransactionItems_transactionResolution() { +    private void testTransactionResolutionInner() {          ClientTransactionItem callback1 = mock(ClientTransactionItem.class);          when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);          ClientTransactionItem callback2 = mock(ClientTransactionItem.class); @@ -286,38 +284,19 @@ public class TransactionExecutorTests {      @Test      public void testDoNotLaunchDestroyedActivity() { -        final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>(); -        when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed); -        // Assume launch transaction is still in queue, so there is no client record. -        when(mTransactionHandler.getActivityClient(any())).thenReturn(null); - -        // An incoming destroy transaction enters binder thread (preExecute). -        final IBinder token = mock(IBinder.class); -        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */); -        destroyTransaction.setLifecycleStateRequest( -                DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */)); -        destroyTransaction.preExecute(mTransactionHandler); -        // The activity should be added to to-be-destroyed container. -        assertEquals(1, activitiesToBeDestroyed.size()); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        // A previous queued launch transaction runs on main thread (execute). -        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */); -        final LaunchActivityItem launchItem = -                spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build()); -        launchTransaction.addCallback(launchItem); -        mExecutor.execute(launchTransaction); +        testDoNotLaunchDestroyedActivityInner(); +    } -        // The launch transaction should not be executed because its token is in the -        // to-be-destroyed container. -        verify(launchItem, never()).execute(any(), any()); +    @Test +    public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        // After the destroy transaction has been executed, the token should be removed. -        mExecutor.execute(destroyTransaction); -        assertTrue(activitiesToBeDestroyed.isEmpty()); +        testDoNotLaunchDestroyedActivityInner();      } -    @Test -    public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() { +    private void testDoNotLaunchDestroyedActivityInner() {          final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();          when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);          // Assume launch transaction is still in queue, so there is no client record. @@ -350,26 +329,19 @@ public class TransactionExecutorTests {      @Test      public void testActivityResultRequiredStateResolution() { -        when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class)); - -        PostExecItem postExecItem = new PostExecItem(ON_RESUME); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.addCallback(postExecItem); +        testActivityResultRequiredStateResolutionInner(); +    } -        // Verify resolution that should get to onPause -        mClientRecord.setState(ON_RESUME); -        mExecutor.executeCallbacks(transaction); -        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction)); +    @Test +    public void testActivityResultRequiredStateResolution_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        // Verify resolution that should get to onStart -        mClientRecord.setState(ON_STOP); -        mExecutor.executeCallbacks(transaction); -        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction)); +        testActivityResultRequiredStateResolutionInner();      } -    @Test -    public void testExecuteTransactionItems_activityResultRequiredStateResolution() { +    private void testActivityResultRequiredStateResolutionInner() {          when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));          PostExecItem postExecItem = new PostExecItem(ON_RESUME); @@ -379,12 +351,12 @@ public class TransactionExecutorTests {          // Verify resolution that should get to onPause          mClientRecord.setState(ON_RESUME); -        mExecutor.executeTransactionItems(transaction); +        mExecutor.execute(transaction);          verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));          // Verify resolution that should get to onStart          mClientRecord.setState(ON_STOP); -        mExecutor.executeTransactionItems(transaction); +        mExecutor.execute(transaction);          verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));      } @@ -523,18 +495,19 @@ public class TransactionExecutorTests {      @Test(expected = IllegalArgumentException.class)      public void testActivityItemNullRecordThrowsException() { -        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class); -        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED); -        final IBinder token = mock(IBinder.class); -        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.addCallback(activityItem); -        when(mTransactionHandler.getActivityClient(token)).thenReturn(null); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        mExecutor.executeCallbacks(transaction); +        testActivityItemNullRecordThrowsExceptionInner();      }      @Test(expected = IllegalArgumentException.class) -    public void testExecuteTransactionItems_activityItemNullRecordThrowsException() { +    public void testActivityItemNullRecordThrowsException_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + +        testActivityItemNullRecordThrowsExceptionInner(); +    } + +    private void testActivityItemNullRecordThrowsExceptionInner() {          final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);          when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);          final IBinder token = mock(IBinder.class); @@ -542,28 +515,24 @@ public class TransactionExecutorTests {          transaction.addTransactionItem(activityItem);          when(mTransactionHandler.getActivityClient(token)).thenReturn(null); -        mExecutor.executeTransactionItems(transaction); +        mExecutor.execute(transaction);      }      @Test      public void testActivityItemExecute() { -        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class); -        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED); -        when(activityItem.getActivityToken()).thenReturn(mActivityToken); -        transaction.addCallback(activityItem); -        transaction.setLifecycleStateRequest(mActivityLifecycleItem); - -        mExecutor.execute(transaction); +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); -        final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem); -        inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any()); -        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord), -                any()); +        testActivityItemExecuteInner();      }      @Test -    public void testExecuteTransactionItems_activityItemExecute() { +    public void testActivityItemExecute_bundleClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + +        testActivityItemExecuteInner(); +    } + +    private void testActivityItemExecuteInner() {          final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);          final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);          when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index c30d216208f9..aa8001310008 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -20,6 +20,9 @@ import static android.app.servertransaction.TestUtils.config;  import static android.app.servertransaction.TestUtils.mergedConfig;  import static android.app.servertransaction.TestUtils.referrerIntentList;  import static android.app.servertransaction.TestUtils.resultInfoList; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;  import static org.junit.Assert.assertEquals; @@ -36,11 +39,13 @@ import android.os.Parcel;  import android.os.Parcelable;  import android.os.PersistableBundle;  import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule;  import androidx.test.filters.SmallTest;  import androidx.test.runner.AndroidJUnit4;  import org.junit.Before; +import org.junit.Rule;  import org.junit.Test;  import org.junit.runner.RunWith; @@ -60,6 +65,9 @@ import java.util.ArrayList;  @Presubmit  public class TransactionParcelTests { +    @Rule +    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); +      private Parcel mParcel;      private IBinder mActivityToken; @@ -275,6 +283,8 @@ public class TransactionParcelTests {      @Test      public void testClientTransaction() { +        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); +          // Write to parcel          NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);          ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( @@ -300,14 +310,16 @@ public class TransactionParcelTests {      @Test      public void testClientTransactionCallbacksOnly() { +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); +          // Write to parcel          NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);          ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(                  mActivityToken, config());          ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.addCallback(callback1); -        transaction.addCallback(callback2); +        transaction.addTransactionItem(callback1); +        transaction.addTransactionItem(callback2);          writeAndPrepareForReading(transaction); @@ -321,12 +333,14 @@ public class TransactionParcelTests {      @Test      public void testClientTransactionLifecycleOnly() { +        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); +          // Write to parcel          StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,                  78 /* configChanges */);          ClientTransaction transaction = ClientTransaction.obtain(null /* client */); -        transaction.setLifecycleStateRequest(lifecycleRequest); +        transaction.addTransactionItem(lifecycleRequest);          writeAndPrepareForReading(transaction); diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 310300d2f32a..d66c925de376 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -206,6 +206,8 @@ android_robolectric_test {      srcs: [          "multivalentTests/src/**/*.kt",      ], +    // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed. +    exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],      static_libs: [          "junit",          "androidx.test.runner", diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt new file mode 100644 index 000000000000..8989fc543044 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.graphics.drawable.Icon +import android.os.UserHandle +import android.view.IWindowManager +import android.view.WindowManager +import android.view.WindowManagerGlobal +import androidx.test.annotation.UiThreadTest +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.internal.protolog.common.ProtoLog +import com.android.launcher3.icons.BubbleIconFactory +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.Bubbles.SysuiProxy +import com.android.wm.shell.common.FloatingContentCoordinator +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.taskview.TaskView +import com.android.wm.shell.taskview.TaskViewTaskController +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import java.util.concurrent.Semaphore +import java.util.concurrent.TimeUnit +import java.util.function.Consumer +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +/** Unit tests for [BubbleStackView]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubbleStackViewTest { + +    private val context = ApplicationProvider.getApplicationContext<Context>() +    private lateinit var positioner: BubblePositioner +    private lateinit var iconFactory: BubbleIconFactory +    private lateinit var expandedViewManager: FakeBubbleExpandedViewManager +    private lateinit var bubbleStackView: BubbleStackView +    private lateinit var shellExecutor: ShellExecutor +    private lateinit var windowManager: IWindowManager +    private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory +    private lateinit var bubbleData: BubbleData + +    @Before +    fun setUp() { +        // Disable protolog tool when running the tests from studio +        ProtoLog.REQUIRE_PROTOLOGTOOL = false +        windowManager = WindowManagerGlobal.getWindowManagerService()!! +        shellExecutor = TestShellExecutor() +        val windowManager = context.getSystemService(WindowManager::class.java) +        iconFactory = +            BubbleIconFactory( +                context, +                context.resources.getDimensionPixelSize(R.dimen.bubble_size), +                context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size), +                Color.BLACK, +                context.resources.getDimensionPixelSize( +                    com.android.internal.R.dimen.importance_ring_stroke_width +                ) +            ) +        positioner = BubblePositioner(context, windowManager) +        val bubbleStackViewManager = FakeBubbleStackViewManager() +        bubbleData = +            BubbleData( +                context, +                BubbleLogger(UiEventLoggerFake()), +                positioner, +                BubbleEducationController(context), +                shellExecutor +            ) + +        val sysuiProxy = mock<SysuiProxy>() +        expandedViewManager = FakeBubbleExpandedViewManager() +        bubbleTaskViewFactory = FakeBubbleTaskViewFactory() +        bubbleStackView = +            BubbleStackView( +                context, +                bubbleStackViewManager, +                positioner, +                bubbleData, +                null, +                FloatingContentCoordinator(), +                { sysuiProxy }, +                shellExecutor +            ) +    } + +    @UiThreadTest +    @Test +    fun addBubble() { +        val bubble = createAndInflateBubble() +        bubbleStackView.addBubble(bubble) +        assertThat(bubbleStackView.bubbleCount).isEqualTo(1) +    } + +    @UiThreadTest +    @Test +    fun tapBubbleToExpand() { +        val bubble = createAndInflateBubble() +        bubbleStackView.addBubble(bubble) +        assertThat(bubbleStackView.bubbleCount).isEqualTo(1) + +        bubble.iconView!!.performClick() +        // we're checking the expanded state in BubbleData because that's the source of truth. This +        // will eventually propagate an update back to the stack view, but setting the entire +        // pipeline is outside the scope of a unit test. +        assertThat(bubbleData.isExpanded).isTrue() +    } + +    private fun createAndInflateBubble(): Bubble { +        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) +        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button) +        val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor()) +        bubble.setInflateSynchronously(true) +        bubbleData.notificationEntryUpdated(bubble, true, false) + +        val semaphore = Semaphore(0) +        val callback: BubbleViewInfoTask.Callback = +            BubbleViewInfoTask.Callback { semaphore.release() } +        bubble.inflate( +            callback, +            context, +            expandedViewManager, +            bubbleTaskViewFactory, +            positioner, +            bubbleStackView, +            null, +            iconFactory, +            false +        ) + +        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() +        assertThat(bubble.isInflated).isTrue() +        return bubble +    } + +    private class FakeBubbleStackViewManager : BubbleStackViewManager { + +        override fun onAllBubblesAnimatedOut() {} + +        override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {} + +        override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {} + +        override fun hideCurrentInputMethod() {} +    } + +    private class TestShellExecutor : ShellExecutor { + +        override fun execute(runnable: Runnable) { +            runnable.run() +        } + +        override fun executeDelayed(r: Runnable, delayMillis: Long) { +            r.run() +        } + +        override fun removeCallbacks(r: Runnable) {} + +        override fun hasCallback(r: Runnable): Boolean = false +    } + +    private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory { +        override fun create(): BubbleTaskView { +            val taskViewTaskController = mock<TaskViewTaskController>() +            val taskView = TaskView(context, taskViewTaskController) +            return BubbleTaskView(taskView, shellExecutor) +        } +    } + +    private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager { + +        override val overflowBubbles: List<Bubble> +            get() = emptyList() + +        override fun setOverflowListener(listener: BubbleData.Listener) {} + +        override fun collapseStack() {} + +        override fun updateWindowFlagsForBackpress(intercept: Boolean) {} + +        override fun promoteBubbleFromOverflow(bubble: Bubble) {} + +        override fun removeBubble(key: String, reason: Int) {} + +        override fun dismissBubble(bubble: Bubble, reason: Int) {} + +        override fun setAppBubbleTaskId(key: String, taskId: Int) {} + +        override fun isStackExpanded(): Boolean = false + +        override fun isShowingAsBubbleBar(): Boolean = false +    } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java index 81d13999e73c..7c280994042b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java @@ -20,6 +20,7 @@ import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;  import android.annotation.NonNull;  import android.annotation.Nullable; +import android.app.AppCompatTaskInfo;  import android.app.TaskInfo;  import android.content.Context;  import android.content.Intent; @@ -88,7 +89,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract          mShellExecutor = shellExecutor;          mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker;          mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer; -        mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo); +        mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton( +                taskInfo.appCompatTaskInfo, taskInfo.baseIntent);          mCompatUIHintsState = compatUIHintsState;          mOnButtonClicked = onButtonClicked;          mDisappearTimeSupplier = disappearTimeSupplier; @@ -134,7 +136,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract      public boolean updateCompatInfo(@NonNull TaskInfo taskInfo,              @NonNull ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {          final boolean prevHasUserAspectRatioSettingsButton = mHasUserAspectRatioSettingsButton; -        mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo); +        mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton( +                taskInfo.appCompatTaskInfo, taskInfo.baseIntent);          if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {              return false; @@ -227,12 +230,21 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract          return SystemClock.uptimeMillis() + hideDelay;      } -    private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) { -        final Intent intent = taskInfo.baseIntent; -        return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton -                && (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed -                    || taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled) -                && !taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled +    private boolean shouldShowUserAspectRatioSettingsButton(@NonNull AppCompatTaskInfo taskInfo, +            @NonNull Intent intent) { +        final Rect stableBounds = getTaskStableBounds(); +        final int letterboxHeight = taskInfo.topActivityLetterboxHeight; +        final int letterboxWidth = taskInfo.topActivityLetterboxWidth; +        // App is not visibly letterboxed if it covers status bar/bottom insets or matches the +        // stable bounds, so don't show the button +        if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth) { +            return false; +        } + +        return taskInfo.topActivityEligibleForUserAspectRatioButton +                && (taskInfo.topActivityBoundsLetterboxed +                    || taskInfo.isUserFullscreenOverrideEnabled) +                && !taskInfo.isSystemFullscreenOverrideEnabled                  && Intent.ACTION_MAIN.equals(intent.getAction())                  && intent.hasCategory(Intent.CATEGORY_LAUNCHER)                  && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index e732a0354806..8305fa6b0fbf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -47,4 +47,8 @@ public interface DesktopMode {      default void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,              Executor callbackExecutor) { } + +    /** Called when requested to go to desktop mode from the current focused app. */ +    void enterDesktop(int displayId); +  } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 11304ec587e7..6de5d741f436 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME  import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD  import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM  import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW  import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED  import android.app.WindowConfiguration.WindowingMode  import android.content.Context @@ -240,6 +241,43 @@ class DesktopTasksController(          return desktopModeTaskRepository.getVisibleTaskCount(displayId)      } +    /** Enter desktop by using the focused task in given `displayId` */ +    fun enterDesktop(displayId: Int) { +        val allFocusedTasks = +            shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo -> +                taskInfo.isFocused && +                        (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN || +                                taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) && +                        taskInfo.activityType != ACTIVITY_TYPE_HOME +            } +        if (allFocusedTasks.isNotEmpty()) { +            when (allFocusedTasks.size) { +                2 -> { +                    // Split-screen case where there are two focused tasks, then we find the child +                    // task to move to desktop. +                    val splitFocusedTask = findChildFocusedTask(allFocusedTasks) +                    moveToDesktop(splitFocusedTask) +                } +                1 -> { +                    // Fullscreen case where we move the current focused task. +                    moveToDesktop(allFocusedTasks[0].taskId) +                } +                else -> { +                    KtProtoLog.v( +                        WM_SHELL_DESKTOP_MODE, +                        "DesktopTasksController: Cannot enter desktop expected less " + +                                "than 3 focused tasks but found " + allFocusedTasks.size +                    ) +                } +            } +        } +    } + +    private fun findChildFocusedTask(allFocusedTasks: List<RunningTaskInfo>): RunningTaskInfo { +        if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId) return allFocusedTasks[1] +        return allFocusedTasks[0] +    } +      /** Move a task with given `taskId` to desktop */      fun moveToDesktop(              taskId: Int, @@ -1012,6 +1050,12 @@ class DesktopTasksController(                  this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)              }          } + +        override fun enterDesktop(displayId: Int) { +            mainExecutor.execute { +                this@DesktopTasksController.enterDesktop(displayId) +            } +        }      }      /** The interface for calls from outside the host process. */ diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml index 47a116be1b66..2ef425cf3d41 100644 --- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml @@ -21,6 +21,7 @@      <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />      <uses-permission android:name="android.permission.VIBRATE"/> +    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>      <application android:debuggable="true" android:largeHeap="true">          <uses-library android:name="android.test.mock" /> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java index 9fe2cb11e804..81ba4b37d13b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java @@ -113,8 +113,22 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {          mExecutor = new TestShellExecutor();          mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */                  false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); +        final DisplayInfo displayInfo = new DisplayInfo(); +        final int displayWidth = 1000; +        final int displayHeight = 1200; +        displayInfo.logicalWidth = displayWidth; +        displayInfo.logicalHeight = displayHeight; +        final DisplayLayout displayLayout = new DisplayLayout(displayInfo, +                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); +        InsetsState insetsState = new InsetsState(); +        insetsState.setDisplayFrame(new Rect(0, 0, displayWidth, displayHeight)); +        InsetsSource insetsSource = new InsetsSource( +                InsetsSource.createId(null, 0, navigationBars()), navigationBars()); +        insetsSource.setFrame(0, displayHeight - 200, displayWidth, displayHeight); +        insetsState.addSource(insetsSource); +        displayLayout.setInsets(mContext.getResources(), insetsState);          mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, -                mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), +                mSyncTransactionQueue, mTaskListener, displayLayout, new CompatUIHintsState(),                  mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,                  mUserAspectRatioButtonShownChecker, s -> {});          spyOn(mWindowManager); @@ -253,6 +267,31 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {      }      @Test +    public void testEligibleButtonHiddenIfLetterboxBoundsEqualToStableBounds() { +        TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ +                true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); + +        final Rect stableBounds = mWindowManager.getTaskStableBounds(); +        final int stableHeight = stableBounds.height(); + +        // Letterboxed activity bounds equal to stable bounds, layout shouldn't be inflated +        taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight; +        taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = stableBounds.width(); + +        mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + +        verify(mWindowManager, never()).inflateLayout(); + +        // Letterboxed activity bounds smaller than stable bounds, layout should be inflated +        taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight - 100; + +        clearInvocations(mWindowManager); +        mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); + +        verify(mWindowManager).inflateLayout(); +    } + +    @Test      public void testUpdateDisplayLayout() {          final DisplayInfo displayInfo = new DisplayInfo();          displayInfo.logicalWidth = 1000; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 63618f4d0673..cb64c52444ac 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -734,6 +734,46 @@ class DesktopTasksControllerTest : ShellTestCase() {          shellExecutor.flushAll()          verify(launchAdjacentController).launchAdjacentEnabled = true      } +    @Test +    fun enterDesktop_fullscreenTaskIsMovedToDesktop() { +        val task1 = setUpFullscreenTask() +        val task2 = setUpFullscreenTask() +        val task3 = setUpFullscreenTask() + +        task1.isFocused = true +        task2.isFocused = false +        task3.isFocused = false + +        controller.enterDesktop(DEFAULT_DISPLAY) + +        val wct = getLatestMoveToDesktopWct() +        assertThat(wct.changes[task1.token.asBinder()]?.windowingMode) +                .isEqualTo(WINDOWING_MODE_FREEFORM) +    } + +    @Test +    fun enterDesktop_splitScreenTaskIsMovedToDesktop() { +        val task1 = setUpSplitScreenTask() +        val task2 = setUpFullscreenTask() +        val task3 = setUpFullscreenTask() +        val task4 = setUpSplitScreenTask() + +        task1.isFocused = true +        task2.isFocused = false +        task3.isFocused = false +        task4.isFocused = true + +        task4.parentTaskId = task1.taskId + +        controller.enterDesktop(DEFAULT_DISPLAY) + +        val wct = getLatestMoveToDesktopWct() +        assertThat(wct.changes[task4.token.asBinder()]?.windowingMode) +                .isEqualTo(WINDOWING_MODE_FREEFORM) +        verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(), +            eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE) +        ) +    }      private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {          val task = createFreeformTask(displayId) diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 845a8f97db10..f111327ef619 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -206,9 +206,9 @@ package android.nfc.cardemulation {      method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);      method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);      method public boolean removeAidsForService(android.content.ComponentName, String); +    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);      method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);      method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); -    method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);      method public boolean supportsAidPrefixRegistration();      method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);      method public boolean unsetPreferredService(android.app.Activity); diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl index 791bd8c9e6f4..65d0625f251e 100644 --- a/nfc/java/android/nfc/INfcCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcCardEmulation.aidl @@ -30,7 +30,7 @@ interface INfcCardEmulation      boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);      boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);      boolean setDefaultForNextTap(int userHandle, in ComponentName service); -    boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable); +    boolean setDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);      boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);      boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);      boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement); diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index f264b16347f9..32dba5f746a9 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -105,7 +105,6 @@ public final class ApduServiceInfo implements Parcelable {       */      private final HashMap<String, AidGroup> mDynamicAidGroups; -    private final ArrayList<String> mPollingLoopFilters;      private final Map<String, Boolean> mAutoTransact; @@ -181,7 +180,6 @@ public final class ApduServiceInfo implements Parcelable {          this.mDescription = description;          this.mStaticAidGroups = new HashMap<String, AidGroup>();          this.mDynamicAidGroups = new HashMap<String, AidGroup>(); -        this.mPollingLoopFilters = new ArrayList<String>();          this.mAutoTransact = new HashMap<String, Boolean>();          this.mOffHostName = offHost;          this.mStaticOffHostName = staticOffHost; @@ -302,7 +300,6 @@ public final class ApduServiceInfo implements Parcelable {              mStaticAidGroups = new HashMap<String, AidGroup>();              mDynamicAidGroups = new HashMap<String, AidGroup>(); -            mPollingLoopFilters = new ArrayList<String>();              mAutoTransact = new HashMap<String, Boolean>();              mOnHost = onHost; @@ -393,7 +390,6 @@ public final class ApduServiceInfo implements Parcelable {                      String plf =                              a.getString(com.android.internal.R.styleable.PollingLoopFilter_name)                              .toUpperCase(Locale.ROOT); -                    mPollingLoopFilters.add(plf);                      boolean autoTransact = a.getBoolean(                              com.android.internal.R.styleable.PollingLoopFilter_autoTransact,                              false); @@ -461,7 +457,7 @@ public final class ApduServiceInfo implements Parcelable {      @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)      @NonNull      public List<String> getPollingLoopFilters() { -        return mPollingLoopFilters; +        return new ArrayList<>(mAutoTransact.keySet());      }      /** @@ -672,12 +668,15 @@ public final class ApduServiceInfo implements Parcelable {      /**       * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be -     * delivered to {@link HostApduService#processPollingFrames(List)}. -     * @param pollingLoopFilter this polling loop filter to add. +     * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this or +     * {@link  ApduServiceInfo#addPollingLoopFilterToAutoTransact(String)} multiple times will +     * cause the value to be overwritten each time. +     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string       */      @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)      public void addPollingLoopFilter(@NonNull String pollingLoopFilter) { -        mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT)); +        mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), false); +      }      /** @@ -685,13 +684,14 @@ public final class ApduServiceInfo implements Parcelable {       * device to exit observe mode, just as if       * {@link android.nfc.NfcAdapter#setTransactionAllowed(boolean)} had been called with true,       * allowing transactions to proceed. The matching frame will also be delivered to -     * {@link HostApduService#processPollingFrames(List)}. +     * {@link HostApduService#processPollingFrames(List)}. Adding a key with this or +     * {@link  ApduServiceInfo#addPollingLoopFilter(String)} multiple times will +     * cause the value to be overwritten each time.       * -     * @param pollingLoopFilter this polling loop filter to add. +     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string       */      @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)      public void addPollingLoopFilterToAutoTransact(@NonNull String pollingLoopFilter) { -        mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));          mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), true);      } @@ -702,7 +702,7 @@ public final class ApduServiceInfo implements Parcelable {       */      @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)      public void removePollingLoopFilter(@NonNull String pollingLoopFilter) { -        mPollingLoopFilters.remove(pollingLoopFilter.toUpperCase(Locale.ROOT)); +        mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));      }      /** diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 53288cc032a8..e681a8568300 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -338,19 +338,20 @@ public final class CardEmulation {          }      }      /** -     * Sets whether the system should default to observe mode or not when the service is in the -     * foreground or the default payment service. The default is to not enable observe mode when -     * a service either the foreground default service or the default payment service so not -     * calling this method will preserve that behavior. +     * Sets whether when this service becomes the preferred service, if the NFC stack +     * should enable observe mode or disable observe mode. The default is to not enable observe +     * mode when a service either the foreground default service or the default payment service so +     * not calling this method will preserve that behavior.       *       * @param service The component name of the service -     * @param enable Whether the servic should default to observe mode or not +     * @param enable Whether the service should default to observe mode or not       * @return whether the change was successful.       */      @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE) -    public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) { +    public boolean setDefaultToObserveModeForService(@NonNull ComponentName service, +            boolean enable) {          try { -            return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(), +            return sService.setDefaultToObserveModeForService(mContext.getUser().getIdentifier(),                      service, enable);          } catch (RemoteException e) {              Log.e(TAG, "Failed to reach CardEmulationService."); diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index ec519ca61021..463e9be391b6 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath  allprojects {      extra["androidTop"] = androidTop -    extra["jetpackComposeVersion"] = "1.7.0-alpha01" +    extra["jetpackComposeVersion"] = "1.7.0-alpha02"  }  subprojects { diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml index f6fbc02de3b0..fe378c27523c 100644 --- a/packages/SettingsLib/Spa/gradle/libs.versions.toml +++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml @@ -16,7 +16,7 @@  [versions]  agp = "8.2.2" -compose-compiler = "1.5.8" +compose-compiler = "1.5.9"  dexmaker-mockito = "2.28.3"  jvm = "17"  kotlin = "1.9.22" diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 08a87973468b..2259bd74d56e 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -57,13 +57,13 @@ dependencies {      api("androidx.slice:slice-builders:1.1.0-alpha02")      api("androidx.slice:slice-core:1.1.0-alpha02")      api("androidx.slice:slice-view:1.1.0-alpha02") -    api("androidx.compose.material3:material3:1.2.0-rc01") +    api("androidx.compose.material3:material3:1.2.0")      api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")      api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")      api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")      api("androidx.lifecycle:lifecycle-livedata-ktx")      api("androidx.lifecycle:lifecycle-runtime-compose") -    api("androidx.navigation:navigation-compose:2.8.0-alpha01") +    api("androidx.navigation:navigation-compose:2.8.0-alpha02")      api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")      api("com.google.android.material:material:1.7.0-alpha03")      debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt index a0149da8f4b8..1a04bb8351db 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt @@ -37,11 +37,13 @@ import androidx.compose.ui.Modifier  import androidx.compose.ui.semantics.Role  import com.android.settingslib.spa.framework.theme.SettingsDimension  import com.android.settingslib.spa.widget.dialog.SettingsDialog +import com.android.settingslib.spa.widget.ui.SettingsBody  import com.android.settingslib.spa.widget.ui.SettingsDialogItem  data class ListPreferenceOption(      val id: Int,      val text: String, +    val summary: String = String()  )  /** @@ -129,6 +131,14 @@ private fun Radio(      ) {          RadioButton(selected = selected, onClick = null, enabled = enabled)          Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd)) -        SettingsDialogItem(text = option.text, enabled = enabled) +        Column { +            SettingsDialogItem(text = option.text, enabled = enabled) +            if (option.summary != String()) { +                SettingsBody( +                    body = option.summary, +                    maxLines = 1 +                ) +            } +        }      }  } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt index e36572fdff65..3216e37b5db4 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.padding  import androidx.compose.foundation.layout.size  import androidx.compose.material3.MaterialTheme  import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember  import androidx.compose.ui.Alignment  import androidx.compose.ui.Modifier  import androidx.compose.ui.unit.dp @@ -46,14 +45,14 @@ internal fun TwoTargetPreference(          verticalAlignment = Alignment.CenterVertically,      ) {          Box(modifier = Modifier.weight(1f)) { -            Preference(remember { +            Preference(                  object : PreferenceModel {                      override val title = title                      override val summary = summary                      override val icon = icon                      override val onClick = onClick                  } -            }) +            )          }          PreferenceDivider()          widget() diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt index 796ac4851cb5..417ce6ed830b 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt @@ -123,6 +123,26 @@ class ListPreferenceTest {      }      @Test +    fun click_optionsNotEmptyAndItemHasSummary_itemShowSummary() { +        composeTestRule.setContent { +            ListPreference(remember { +                object : ListPreferenceModel { +                    override val title = TITLE +                    override val options = +                        listOf(ListPreferenceOption(id = 1, text = "A", summary = "A_Summary")) +                    override val selectedId = mutableIntStateOf(1) +                    override val onIdSelected: (Int) -> Unit = {} +                } +            }) +        } + +        composeTestRule.onNodeWithText(TITLE).performClick() + +        composeTestRule.onDialogText(TITLE).assertIsDisplayed() +        composeTestRule.onNodeWithText("A_Summary").assertIsDisplayed() +    } + +    @Test      fun select() {          val selectedId = mutableIntStateOf(1)          composeTestRule.setContent { diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 1092a16216f9..9588e502f28e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1129,7 +1129,7 @@      <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->      <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>      <!-- [CHAR_LIMIT=80] Label for battery charging future pause --> -    <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string> +    <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging</string>      <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->      <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 249fa7f0df77..e489bc552b25 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -1702,7 +1702,8 @@ public class ApplicationsState {          }          public boolean isPrivateProfile() { -            return UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType); +            return android.os.Flags.allowPrivateProfile() +                    && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);          }          /** diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 581c7dea002c..a376c1f77567 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -92,13 +92,13 @@ public abstract class InfoMediaManager extends MediaManager {          }      } -    protected String mPackageName; +    @NonNull protected final String mPackageName;      private MediaDevice mCurrentConnectedDevice;      private final LocalBluetoothManager mBluetoothManager;      private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =              new ConcurrentHashMap<>(); -    public InfoMediaManager( +    /* package */ InfoMediaManager(              Context context,              @NonNull String packageName,              Notification notification, @@ -112,7 +112,7 @@ public abstract class InfoMediaManager extends MediaManager {      /** Creates an instance of InfoMediaManager. */      public static InfoMediaManager createInstance(              Context context, -            String packageName, +            @Nullable String packageName,              Notification notification,              LocalBluetoothManager localBluetoothManager) { @@ -148,8 +148,7 @@ public abstract class InfoMediaManager extends MediaManager {      }      private void updateRouteListingPreference() { -        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -                && !TextUtils.isEmpty(mPackageName)) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {              RouteListingPreference routeListingPreference =                      getRouteListingPreference();              Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, @@ -218,11 +217,7 @@ public abstract class InfoMediaManager extends MediaManager {      protected final void rebuildDeviceList() {          mMediaDevices.clear();          mCurrentConnectedDevice = null; -        if (TextUtils.isEmpty(mPackageName)) { -            buildAllRoutes(); -        } else { -            buildAvailableRoutes(); -        } +        buildAvailableRoutes();      }      protected final void notifyCurrentConnectedDeviceChanged() { @@ -250,12 +245,8 @@ public abstract class InfoMediaManager extends MediaManager {              return;          } -        if (TextUtils.isEmpty(mPackageName)) { -            connectDeviceWithoutPackageName(device); -        } else { -            device.setConnectedRecord(); -            transferToRoute(device.mRouteInfo); -        } +        device.setConnectedRecord(); +        transferToRoute(device.mRouteInfo);      }      /** @@ -265,11 +256,6 @@ public abstract class InfoMediaManager extends MediaManager {       * @return If add device successful return {@code true}, otherwise return {@code false}       */      boolean addDeviceToPlayMedia(MediaDevice device) { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "addDeviceToPlayMedia() package name is null or empty!"); -            return false; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {              Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : " @@ -306,7 +292,6 @@ public abstract class InfoMediaManager extends MediaManager {      boolean preferRouteListingOrdering() {          return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -                && !TextUtils.isEmpty(mPackageName)                  && Api34Impl.preferRouteListingOrdering(getRouteListingPreference());      } @@ -326,11 +311,6 @@ public abstract class InfoMediaManager extends MediaManager {       * @return If device stop successful return {@code true}, otherwise return {@code false}       */      boolean removeDeviceFromPlayMedia(MediaDevice device) { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "removeDeviceFromMedia() package name is null or empty!"); -            return false; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {              Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : " @@ -346,11 +326,6 @@ public abstract class InfoMediaManager extends MediaManager {       * Release session to stop playing media on MediaDevice.       */      boolean releaseSession() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "releaseSession() package name is null or empty!"); -            return false; -        } -          final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();          if (sessionInfo == null) {              Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName); @@ -367,11 +342,6 @@ public abstract class InfoMediaManager extends MediaManager {       */      @NonNull      List<MediaDevice> getSelectableMediaDevices() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "getSelectableMediaDevices() package name is null or empty!"); -            return Collections.emptyList(); -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : " @@ -394,11 +364,6 @@ public abstract class InfoMediaManager extends MediaManager {       */      @NonNull      List<MediaDevice> getDeselectableMediaDevices() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.d(TAG, "getDeselectableMediaDevices() package name is null or empty!"); -            return Collections.emptyList(); -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : " @@ -462,11 +427,6 @@ public abstract class InfoMediaManager extends MediaManager {       * @param volume the value of volume       */      void adjustSessionVolume(int volume) { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "adjustSessionVolume() package name is null or empty!"); -            return; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : " @@ -484,11 +444,6 @@ public abstract class InfoMediaManager extends MediaManager {       * @return  maximum volume of the session, and return -1 if not found.       */      public int getSessionVolumeMax() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "getSessionVolumeMax() package name is null or empty!"); -            return -1; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : " @@ -505,11 +460,6 @@ public abstract class InfoMediaManager extends MediaManager {       * @return current volume of the session, and return -1 if not found.       */      public int getSessionVolume() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "getSessionVolume() package name is null or empty!"); -            return -1; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : " @@ -521,11 +471,6 @@ public abstract class InfoMediaManager extends MediaManager {      }      CharSequence getSessionName() { -        if (TextUtils.isEmpty(mPackageName)) { -            Log.w(TAG, "Unable to get session name. The package name is null or empty!"); -            return null; -        } -          final RoutingSessionInfo info = getRoutingSessionInfo();          if (info == null) {              Log.w(TAG, "Unable to get session name for package: " + mPackageName); @@ -548,20 +493,6 @@ public abstract class InfoMediaManager extends MediaManager {      // MediaRoute2Info.getType was made public on API 34, but exists since API 30.      @SuppressWarnings("NewApi") -    private void buildAllRoutes() { -        for (MediaRoute2Info route : getAllRoutes()) { -            if (DEBUG) { -                Log.d(TAG, "buildAllRoutes() route : " + route.getName() + ", volume : " -                        + route.getVolume() + ", type : " + route.getType()); -            } -            if (route.isSystemRoute()) { -                addMediaDevice(route); -            } -        } -    } - -    // MediaRoute2Info.getType was made public on API 34, but exists since API 30. -    @SuppressWarnings("NewApi")      private synchronized void buildAvailableRoutes() {          for (MediaRoute2Info route : getAvailableRoutes()) {              if (DEBUG) { @@ -594,8 +525,7 @@ public abstract class InfoMediaManager extends MediaManager {                  infos.add(transferableRoute);              }          } -        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -                && !TextUtils.isEmpty(mPackageName)) { +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {              RouteListingPreference routeListingPreference = getRouteListingPreference();              if (routeListingPreference != null) {                  final List<RouteListingPreference.Item> preferenceRouteListing = @@ -679,7 +609,7 @@ public abstract class InfoMediaManager extends MediaManager {                  break;          } -        if (mediaDevice != null && !TextUtils.isEmpty(mPackageName) +        if (mediaDevice != null                  && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {              mediaDevice.setState(STATE_SELECTED);              if (mCurrentConnectedDevice == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java index 97bbf12fd055..cf8906d282d0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java @@ -51,9 +51,9 @@ public class ManagerInfoMediaManager extends InfoMediaManager {      private final Executor mExecutor = Executors.newSingleThreadExecutor(); -    public ManagerInfoMediaManager( +    /* package */ ManagerInfoMediaManager(              Context context, -            String packageName, +            @NonNull String packageName,              Notification notification,              LocalBluetoothManager localBluetoothManager) {          super(context, packageName, notification, localBluetoothManager); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java index 9d578bcc3b16..5b1c8efd8360 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java @@ -41,7 +41,7 @@ import java.util.List;      NoOpInfoMediaManager(              Context context, -            String packageName, +            @NonNull String packageName,              Notification notification,              LocalBluetoothManager localBluetoothManager) {          super(context, packageName, notification, localBluetoothManager); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java index aef09ac236f3..c8c8b672d976 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java @@ -65,9 +65,9 @@ public final class RouterInfoMediaManager extends InfoMediaManager {              };      // TODO (b/321969740): Plumb target UserHandle between UMO and RouterInfoMediaManager. -    public RouterInfoMediaManager( +    /* package */ RouterInfoMediaManager(              Context context, -            String packageName, +            @NonNull String packageName,              Notification notification,              LocalBluetoothManager localBluetoothManager)              throws PackageNotAvailableException { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java index 213a66e546ab..1ad7d4930ecc 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java @@ -22,21 +22,30 @@ import static org.mockito.Mockito.mock;  import static org.mockito.Mockito.when;  import android.content.pm.ApplicationInfo; +import android.os.Flags;  import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.core.app.ApplicationProvider;  import org.junit.Before; +import org.junit.Rule;  import org.junit.Test;  import org.junit.runner.RunWith;  import org.junit.runners.JUnit4;  @RunWith(JUnit4.class)  public class ApplicationsStateTest { +    private static final int APP_ENTRY_ID = 1;      private ApplicationsState.AppEntry mEntry; +    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();      @Before      public void setUp() { -        mEntry = mock(ApplicationsState.AppEntry.class); -        mEntry.info = mock(ApplicationInfo.class); +        mEntry = new ApplicationsState.AppEntry( +                ApplicationProvider.getApplicationContext(), +                mock(ApplicationInfo.class), +                APP_ENTRY_ID);      }      @Test @@ -310,6 +319,8 @@ public class ApplicationsStateTest {      @Test      public void testPrivateProfileFilterDisplaysCorrectApps() { +        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); +          mEntry.showInPersonalTab = true;          mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;          assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue(); @@ -320,4 +331,14 @@ public class ApplicationsStateTest {          assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();          assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();      } + +    @Test +    public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() { +        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE); + +        mEntry.showInPersonalTab = false; +        mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE; +        assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse(); +        assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse(); +    }  } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index f0330c46315c..290e63cace51 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -212,28 +212,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() { -        final MediaRoute2Info info = mock(MediaRoute2Info.class); -        when(info.getId()).thenReturn(TEST_ID); -        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); -        when(info.isSystemRoute()).thenReturn(true); - -        final List<MediaRoute2Info> routes = new ArrayList<>(); -        routes.add(info); -        mShadowRouter2Manager.setAllRoutes(routes); - -        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); -        assertThat(mediaDevice).isNull(); - -        mInfoMediaManager.mPackageName = ""; -        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); - -        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); -        assertThat(infoDevice.getId()).isEqualTo(TEST_ID); -        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); -    } - -    @Test      public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); @@ -436,29 +414,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() { -        final MediaRoute2Info info = mock(MediaRoute2Info.class); -        when(info.getId()).thenReturn(TEST_ID); -        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); -        when(info.isSystemRoute()).thenReturn(true); -        when(info.getDeduplicationIds()).thenReturn(Set.of()); - -        final List<MediaRoute2Info> routes = new ArrayList<>(); -        routes.add(info); -        mShadowRouter2Manager.setAllRoutes(routes); - -        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); -        assertThat(mediaDevice).isNull(); - -        mInfoMediaManager.mPackageName = ""; -        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); - -        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); -        assertThat(infoDevice.getId()).isEqualTo(TEST_ID); -        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); -    } - -    @Test      public void hasPreferenceRouteListing_oldSdkVersion_returnsFalse() {          assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();      } @@ -587,36 +542,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() { -        final MediaRoute2Info info = mock(MediaRoute2Info.class); -        when(info.getId()).thenReturn(TEST_ID); -        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); -        when(info.isSystemRoute()).thenReturn(true); - -        final List<MediaRoute2Info> routes = new ArrayList<>(); -        routes.add(info); -        when(mRouterManager.getAllRoutes()).thenReturn(routes); - -        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); -        assertThat(mediaDevice).isNull(); - -        mInfoMediaManager.mPackageName = ""; -        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); - -        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); -        assertThat(infoDevice.getId()).isEqualTo(TEST_ID); -        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); -    } - -    @Test -    public void addDeviceToPlayMedia_packageNameIsNull_returnFalse() { -        mInfoMediaManager.mPackageName = null; -        final MediaDevice device = mock(MediaDevice.class); - -        assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse(); -    } - -    @Test      public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -660,14 +585,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void removeDeviceFromMedia_packageNameIsNull_returnFalse() { -        mInfoMediaManager.mPackageName = null; -        final MediaDevice device = mock(MediaDevice.class); - -        assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse(); -    } - -    @Test      public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -711,13 +628,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void getSelectableMediaDevice_packageNameIsNull_returnFalse() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.getSelectableMediaDevices()).isEmpty(); -    } - -    @Test      public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -730,13 +640,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void getDeselectableMediaDevice_packageNameIsNull_returnFalse() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.getDeselectableMediaDevices()).isEmpty(); -    } - -    @Test      public void getDeselectableMediaDevice_checkList() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -761,20 +664,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void adjustSessionVolume_packageNameIsNull_noCrash() { -        mInfoMediaManager.mPackageName = null; - -        mInfoMediaManager.adjustSessionVolume(10); -    } - -    @Test -    public void getSessionVolumeMax_packageNameIsNull_returnNotFound() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1); -    } - -    @Test      public void getSessionVolumeMax_containPackageName_returnMaxVolume() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -800,13 +689,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void getSessionVolume_packageNameIsNull_returnNotFound() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1); -    } - -    @Test      public void getSessionVolume_containPackageName_returnMaxVolume() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -841,13 +723,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void releaseSession_packageNameIsNull_returnFalse() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.releaseSession()).isFalse(); -    } - -    @Test      public void releaseSession_removeSuccessfully_returnTrue() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = mock(RoutingSessionInfo.class); @@ -860,13 +735,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void getSessionName_packageNameIsNull_returnNull() { -        mInfoMediaManager.mPackageName = null; - -        assertThat(mInfoMediaManager.getSessionName()).isNull(); -    } - -    @Test      public void getSessionName_routeSessionInfoIsNull_returnNull() {          final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();          final RoutingSessionInfo info = null; @@ -942,32 +810,6 @@ public class InfoMediaManagerTest {      }      @Test -    public void onTransferred_buildAllRoutes_shouldAddMediaDevice() { -        final MediaRoute2Info info = mock(MediaRoute2Info.class); -        final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); -        mInfoMediaManager.registerCallback(mCallback); - -        when(info.getId()).thenReturn(TEST_ID); -        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); -        when(info.isSystemRoute()).thenReturn(true); - -        final List<MediaRoute2Info> routes = new ArrayList<>(); -        routes.add(info); -        mShadowRouter2Manager.setAllRoutes(routes); - -        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); -        assertThat(mediaDevice).isNull(); - -        mInfoMediaManager.mPackageName = ""; -        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo); - -        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); -        assertThat(infoDevice.getId()).isEqualTo(TEST_ID); -        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); -        verify(mCallback).onConnectedDeviceChanged(null); -    } - -    @Test      public void onSessionUpdated_shouldDispatchDeviceListAdded() {          final MediaRoute2Info info = mock(MediaRoute2Info.class);          when(info.getId()).thenReturn(TEST_ID); @@ -978,7 +820,6 @@ public class InfoMediaManagerTest {          routes.add(info);          mShadowRouter2Manager.setAllRoutes(routes); -        mInfoMediaManager.mPackageName = "";          mInfoMediaManager.registerCallback(mCallback);          mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class)); diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java index fde378fc3e5e..3adb20409bd9 100644 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java +++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java @@ -27,12 +27,13 @@ import org.robolectric.annotation.Implementation;  import org.robolectric.annotation.Implements;  import org.robolectric.shadow.api.Shadow; +import java.util.ArrayList;  import java.util.List;  @Implements(MediaRouter2Manager.class)  public class ShadowRouter2Manager { -    private List<MediaRoute2Info> mAvailableRoutes; +    private List<MediaRoute2Info> mAvailableRoutes = new ArrayList<>();      private List<MediaRoute2Info> mAllRoutes;      private List<MediaRoute2Info> mDeselectableRoutes;      private List<RoutingSessionInfo> mRemoteSessions; diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 7f229fb65d4c..4ed1965d1b33 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -317,7 +317,7 @@ flag {     description: "Refactors shade header and keyguard status bar to read status bar dimens from a"          " central place, instead of reading resources directly. This is to take into account display"          " cutouts and other special cases. " -   bug: "317199366" +   bug: "317016114"     metadata {          purpose: PURPOSE_BUGFIX     } @@ -451,6 +451,16 @@ flag {  }  flag { +    name: "slice_manager_binder_call_background" +    namespace: "systemui" +    description: "Move the ISliceManager#getPinnedSpecs binder call to the background thread." +    bug: "322745650" +    metadata { +        purpose: PURPOSE_BUGFIX +    } +} + +flag {     name: "register_new_wallet_card_in_background"     namespace: "systemui"     description: "Decide whether the call to registerNewWalletCards method should be issued on background thread." @@ -469,3 +479,13 @@ flag {          purpose: PURPOSE_BUGFIX      }  } + +flag { +    name: "register_zen_mode_content_observer_background" +    namespace: "systemui" +    description: "Decide whether to register zen mode content observers in the background thread." +    bug: "324515627" +    metadata { +        purpose: PURPOSE_BUGFIX +    } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt index e4dc9beb51f8..5d5f12e8e567 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt @@ -123,7 +123,7 @@ constructor(              val views = LinkedList<View>().apply { add(view) }              while (views.isNotEmpty()) { -                val v = views.removeFirst() +                val v = views.removeAt(0)                  if (v.background != null) {                      return v.background                  } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 828e34da378d..2e781e69bb4a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -29,13 +29,17 @@ import androidx.compose.ui.geometry.lerp  import androidx.compose.ui.graphics.CompositingStrategy  import androidx.compose.ui.graphics.drawscope.ContentDrawScope  import androidx.compose.ui.graphics.drawscope.scale -import androidx.compose.ui.layout.IntermediateMeasureScope +import androidx.compose.ui.layout.ApproachLayoutModifierNode +import androidx.compose.ui.layout.ApproachMeasureScope +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.LookaheadScope  import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult  import androidx.compose.ui.layout.Placeable -import androidx.compose.ui.layout.intermediateLayout  import androidx.compose.ui.node.DrawModifierNode  import androidx.compose.ui.node.ModifierNodeElement  import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.testTag  import androidx.compose.ui.unit.Constraints  import androidx.compose.ui.unit.IntSize  import androidx.compose.ui.unit.round @@ -91,23 +95,7 @@ internal fun Modifier.element(      layoutImpl: SceneTransitionLayoutImpl,      scene: Scene,      key: ElementKey, -): Modifier { -    return this.then(ElementModifier(layoutImpl, scene, key)) -        // TODO(b/311132415): Move this into ElementNode once we can create a delegate -        // IntermediateLayoutModifierNode. -        .intermediateLayout { measurable, constraints -> -            // TODO(b/311132415): No need to fetch the element and sceneState from the map anymore -            // once this is merged into ElementNode. -            val element = layoutImpl.elements.getValue(key) -            val sceneState = element.sceneStates.getValue(scene.key) - -            val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints) -            layout(placeable.width, placeable.height) { -                place(layoutImpl, scene, element, sceneState, placeable, placementScope = this) -            } -        } -        .testTag(key.testTag) -} +): Modifier = this.then(ElementModifier(layoutImpl, scene, key)).testTag(key.testTag)  /**   * An element associated to [ElementNode]. Note that this element does not support updates as its @@ -129,7 +117,7 @@ internal class ElementNode(      private var layoutImpl: SceneTransitionLayoutImpl,      private var scene: Scene,      private var key: ElementKey, -) : Modifier.Node(), DrawModifierNode { +) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {      private var _element: Element? = null      private val element: Element          get() = _element!! @@ -197,6 +185,31 @@ internal class ElementNode(          maybePruneMaps(layoutImpl, prevElement, prevSceneState)      } +    override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean { +        // TODO(b/324191441): Investigate whether making this check more complex (checking if this +        // element is shared or transformed) would lead to better performance. +        return layoutImpl.state.currentTransition == null +    } + +    override fun Placeable.PlacementScope.isPlacementApproachComplete( +        lookaheadCoordinates: LayoutCoordinates +    ): Boolean { +        // TODO(b/324191441): Investigate whether making this check more complex (checking if this +        // element is shared or transformed) would lead to better performance. +        return layoutImpl.state.currentTransition == null +    } + +    @ExperimentalComposeUiApi +    override fun ApproachMeasureScope.approachMeasure( +        measurable: Measurable, +        constraints: Constraints, +    ): MeasureResult { +        val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints) +        return layout(placeable.width, placeable.height) { +            place(layoutImpl, scene, element, sceneState, placeable, placementScope = this) +        } +    } +      override fun ContentDrawScope.draw() {          val drawScale = getDrawScale(layoutImpl, element, scene)          if (drawScale == Scale.Default) { @@ -368,7 +381,7 @@ private fun elementAlpha(  }  @OptIn(ExperimentalComposeUiApi::class) -private fun IntermediateMeasureScope.measure( +private fun ApproachMeasureScope.measure(      layoutImpl: SceneTransitionLayoutImpl,      scene: Scene,      element: Element, @@ -431,7 +444,7 @@ private fun getDrawScale(  }  @OptIn(ExperimentalComposeUiApi::class) -private fun IntermediateMeasureScope.place( +private fun ApproachMeasureScope.place(      layoutImpl: SceneTransitionLayoutImpl,      scene: Scene,      element: Element, @@ -439,6 +452,8 @@ private fun IntermediateMeasureScope.place(      placeable: Placeable,      placementScope: Placeable.PlacementScope,  ) { +    this as LookaheadScope +      with(placementScope) {          // Update the offset (relative to the SceneTransitionLayout) this element has in this scene          // when idle. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index c8fbad4f4eef..76e7c95f274a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -348,6 +348,8 @@ internal class SceneGestureHandler(              // Compute the destination scene (and therefore offset) to settle in.              val offset = swipeTransition.dragOffset              val distance = swipeTransition.distance +            var targetScene: Scene +            var targetOffset: Float              if (                  shouldCommitSwipe(                      offset, @@ -356,12 +358,24 @@ internal class SceneGestureHandler(                      wasCommitted = swipeTransition._currentScene == toScene,                  )              ) { -                // Animate to the next scene -                animateTo(targetScene = toScene, targetOffset = distance) +                targetScene = toScene +                targetOffset = distance              } else { -                // Animate to the initial scene -                animateTo(targetScene = fromScene, targetOffset = 0f) +                targetScene = fromScene +                targetOffset = 0f +            } + +            if ( +                targetScene != swipeTransition._currentScene && +                    !layoutState.canChangeScene(targetScene.key) +            ) { +                // We wanted to change to a new scene but we are not allowed to, so we animate back +                // to the current scene. +                targetScene = swipeTransition._currentScene +                targetOffset = if (targetScene == fromScene) 0f else distance              } + +            animateTo(targetScene = targetScene, targetOffset = targetOffset)          } else {              // We are doing an overscroll animation between scenes. In this case, we can also start              // from the idle position. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 8c5a4720e7fb..08399ff03f63 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -232,7 +232,12 @@ internal class SceneTransitionLayoutImpl(                  scene(state.transitionState.currentScene).userActions[Back]?.let { result ->                      // TODO(b/290184746): Handle predictive back and use result.distance if                      // specified. -                    BackHandler { with(state) { coroutineScope.onChangeScene(result.toScene) } } +                    BackHandler { +                        val targetScene = result.toScene +                        if (state.canChangeScene(targetScene)) { +                            with(state) { coroutineScope.onChangeScene(targetScene) } +                        } +                    }                  }                  Box { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index a8da55101548..662f33f3e88b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -101,13 +101,30 @@ sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState      ): TransitionState.Transition?  } -/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */ +/** + * Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. + * + * @param initialScene the initial scene to which this state is initialized. + * @param transitions the [SceneTransitions] used when this state is transitioning between scenes. + * @param canChangeScene whether we can transition to the given scene. This is called when the user + *   commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns + *   `true`, then the gesture will be committed and we will animate to the other scene. Otherwise, + *   the gesture will be cancelled and we will animate back to the current scene. + * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other + *   [SceneTransitionLayoutState]s. + */  fun MutableSceneTransitionLayoutState(      initialScene: SceneKey,      transitions: SceneTransitions = SceneTransitions.Empty, +    canChangeScene: (SceneKey) -> Boolean = { true },      stateLinks: List<StateLink> = emptyList(),  ): MutableSceneTransitionLayoutState { -    return MutableSceneTransitionLayoutStateImpl(initialScene, transitions, stateLinks) +    return MutableSceneTransitionLayoutStateImpl( +        initialScene, +        transitions, +        canChangeScene, +        stateLinks, +    )  }  /** @@ -120,18 +137,32 @@ fun MutableSceneTransitionLayoutState(   *   This is called when the user commits a transition to a new scene because of a [UserAction], for   *   instance by triggering back navigation or by swiping to a new scene.   * @param transitions the definition of the transitions used to animate a change of scene. + * @param canChangeScene whether we can transition to the given scene. This is called when the user + *   commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns + *   `true`, then [onChangeScene] will be called right afterwards with the same [SceneKey]. If it + *   returns `false`, the user action will be cancelled and we will animate back to the current + *   scene. + * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other + *   [SceneTransitionLayoutState]s.   */  @Composable  fun updateSceneTransitionLayoutState(      currentScene: SceneKey,      onChangeScene: (SceneKey) -> Unit,      transitions: SceneTransitions = SceneTransitions.Empty, +    canChangeScene: (SceneKey) -> Boolean = { true },      stateLinks: List<StateLink> = emptyList(),  ): SceneTransitionLayoutState {      return remember { -            HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene, stateLinks) +            HoistedSceneTransitionLayoutState( +                currentScene, +                transitions, +                onChangeScene, +                canChangeScene, +                stateLinks, +            )          } -        .apply { update(currentScene, onChangeScene, transitions, stateLinks) } +        .apply { update(currentScene, onChangeScene, canChangeScene, transitions, stateLinks) }  }  @Stable @@ -208,6 +239,9 @@ internal abstract class BaseSceneTransitionLayoutState(      private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>() +    /** Whether we can transition to the given [scene]. */ +    internal abstract fun canChangeScene(scene: SceneKey): Boolean +      /**       * Called when the [current scene][TransitionState.currentScene] should be changed to [scene].       * @@ -330,25 +364,30 @@ internal abstract class BaseSceneTransitionLayoutState(   * A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes   * from outside).   */ -internal class HoistedSceneTransitionLayoutScene( +internal class HoistedSceneTransitionLayoutState(      initialScene: SceneKey,      override var transitions: SceneTransitions,      private var changeScene: (SceneKey) -> Unit, +    private var canChangeScene: (SceneKey) -> Boolean,      stateLinks: List<StateLink> = emptyList(),  ) : BaseSceneTransitionLayoutState(initialScene, stateLinks) {      private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED) -    override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene) +    override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene) + +    override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene.invoke(scene)      @Composable      fun update(          currentScene: SceneKey,          onChangeScene: (SceneKey) -> Unit, +        canChangeScene: (SceneKey) -> Boolean,          transitions: SceneTransitions,          stateLinks: List<StateLink>,      ) {          SideEffect {              this.changeScene = onChangeScene +            this.canChangeScene = canChangeScene              this.transitions = transitions              this.stateLinks = stateLinks @@ -361,7 +400,7 @@ internal class HoistedSceneTransitionLayoutScene(                  // late.                  val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey                  animateToScene( -                    layoutState = this@HoistedSceneTransitionLayoutScene, +                    layoutState = this@HoistedSceneTransitionLayoutState,                      target = newKey,                      transitionKey = null,                  ) @@ -374,6 +413,7 @@ internal class HoistedSceneTransitionLayoutScene(  internal class MutableSceneTransitionLayoutStateImpl(      initialScene: SceneKey,      override var transitions: SceneTransitions, +    private val canChangeScene: (SceneKey) -> Boolean = { true },      stateLinks: List<StateLink> = emptyList(),  ) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene, stateLinks) {      override fun setTargetScene( @@ -388,6 +428,8 @@ internal class MutableSceneTransitionLayoutStateImpl(          )      } +    override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene) +      override fun CoroutineScope.onChangeScene(scene: SceneKey) {          setTargetScene(scene, coroutineScope = this)      } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index c9b5b75d6c49..33be1dc83dc8 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -44,7 +44,6 @@ import kotlinx.coroutines.CoroutineScope  import kotlinx.coroutines.launch  import kotlinx.coroutines.test.runTest  import org.junit.Assert.assertThrows -import org.junit.Ignore  import org.junit.Rule  import org.junit.Test  import org.junit.runner.RunWith @@ -248,11 +247,9 @@ class ElementTest {      }      @Test -    @Ignore -    fun elementIsReusedInSameSceneAndBetweenScenes() { +    fun elementIsReusedBetweenScenes() {          var currentScene by mutableStateOf(TestScenes.SceneA)          var sceneCState by mutableStateOf(0) -        var sceneDState by mutableStateOf(0)          val key = TestElements.Foo          var nullableLayoutImpl: SceneTransitionLayoutImpl? = null @@ -270,19 +267,6 @@ class ElementTest {                  scene(TestScenes.SceneC) {                      when (sceneCState) {                          0 -> Row(Modifier.element(key)) {} -                        1 -> Column(Modifier.element(key)) {} -                        else -> { -                            /* Nothing */ -                        } -                    } -                } -                scene(TestScenes.SceneD) { -                    // We should be able to extract the modifier before assigning it to different -                    // nodes. -                    val childModifier = Modifier.element(key) -                    when (sceneDState) { -                        0 -> Row(childModifier) {} -                        1 -> Column(childModifier) {}                          else -> {                              /* Nothing */                          } @@ -315,35 +299,10 @@ class ElementTest {          assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)          assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC) -        // Scene C, state 1: the same element is reused. +        // Scene C, state 1: the element is removed from the map.          sceneCState = 1          rule.waitForIdle() -        assertThat(layoutImpl.elements.keys).containsExactly(key) -        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element) -        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC) - -        // Scene D, state 0: the same element is reused. -        currentScene = TestScenes.SceneD -        sceneDState = 0 -        rule.waitForIdle() - -        assertThat(layoutImpl.elements.keys).containsExactly(key) -        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element) -        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD) - -        // Scene D, state 1: the same element is reused. -        sceneDState = 1 -        rule.waitForIdle() - -        assertThat(layoutImpl.elements.keys).containsExactly(key) -        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element) -        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD) - -        // Scene D, state 2: the element is removed from the map. -        sceneDState = 2 -        rule.waitForIdle() -          assertThat(element.sceneStates).isEmpty()          assertThat(layoutImpl.elements).isEmpty()      } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index c91d29880ffb..fe53d5b45420 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -51,8 +51,13 @@ class SceneGestureHandlerTest {      private class TestGestureScope(          private val testScope: MonotonicClockTestScope,      ) { +        var canChangeScene: (SceneKey) -> Boolean = { true }          private val layoutState = -            MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions) +            MutableSceneTransitionLayoutStateImpl( +                SceneA, +                EmptyTestTransitions, +                canChangeScene = { canChangeScene(it) }, +            )          val mutableUserActionsA = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)          val mutableUserActionsB = mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA) @@ -890,4 +895,41 @@ class SceneGestureHandlerTest {          )          assertThat(transitionState).isNotSameInstanceAs(firstTransition)      } + +    @Test +    fun blockTransition() = runGestureTest { +        assertIdle(SceneA) + +        // Swipe up to scene B. +        onDragStarted(overSlop = up(0.1f)) +        assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB) + +        // Block the transition when the user release their finger. +        canChangeScene = { false } +        onDragStopped(velocity = -velocityThreshold) +        advanceUntilIdle() +        assertIdle(SceneA) +    } + +    @Test +    fun blockInterceptedTransition() = runGestureTest { +        assertIdle(SceneA) + +        // Swipe up to B. +        onDragStarted(overSlop = up(0.1f)) +        assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB) +        onDragStopped(velocity = -velocityThreshold) +        assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB) + +        // Intercept the transition and swipe down back to scene A. +        assertThat(sceneGestureHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue() +        onDragStartedImmediately() + +        // Block the transition when the user release their finger. +        canChangeScene = { false } +        onDragStopped(velocity = velocityThreshold) + +        advanceUntilIdle() +        assertIdle(SceneB) +    }  } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt index 837a9db6eea7..d33c10e7e663 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.viewmodel  import androidx.test.ext.junit.runners.AndroidJUnit4  import androidx.test.filters.SmallTest +import com.android.systemui.Flags as AConfigFlags  import com.android.systemui.SysuiTestCase  import com.android.systemui.coroutines.collectLastValue  import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -66,6 +67,25 @@ class AodAlphaViewModelTest : SysuiTestCase() {      }      @Test +    fun alpha_WhenNotGone_clockMigrationFlagIsOff_emitsKeyguardAlpha() = +        testScope.runTest { +            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) +            val alpha by collectLastValue(underTest.alpha) + +            keyguardTransitionRepository.sendTransitionSteps( +                from = KeyguardState.AOD, +                to = KeyguardState.LOCKSCREEN, +                testScope = testScope, +            ) + +            keyguardRepository.setKeyguardAlpha(0.5f) +            assertThat(alpha).isEqualTo(0.5f) + +            keyguardRepository.setKeyguardAlpha(0.8f) +            assertThat(alpha).isEqualTo(0.8f) +        } + +    @Test      fun alpha_WhenGoneToAod() =          testScope.runTest {              val alpha by collectLastValue(underTest.alpha) @@ -112,6 +132,7 @@ class AodAlphaViewModelTest : SysuiTestCase() {      @Test      fun alpha_whenGone_equalsZero() =          testScope.runTest { +            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)              val alpha by collectLastValue(underTest.alpha)              keyguardTransitionRepository.sendTransitionStep( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java deleted file mode 100644 index d0e05fa60114..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java +++ /dev/null @@ -1,614 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *      http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; -import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; -import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; -import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; -import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.app.KeyguardManager; -import android.app.Notification; -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.UserInfo; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.util.SparseArray; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlagsClassic; -import com.android.systemui.flags.Flags; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; -import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.settings.FakeSettings; - -import com.google.android.collect.Lists; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.Executor; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase { -    @Mock -    private NotificationPresenter mPresenter; -    @Mock -    private UserManager mUserManager; -    @Mock -    private UserTracker mUserTracker; - -    // Dependency mocks: -    @Mock -    private NotificationVisibilityProvider mVisibilityProvider; -    @Mock -    private CommonNotifCollection mNotifCollection; -    @Mock -    private DevicePolicyManager mDevicePolicyManager; -    @Mock -    private NotificationClickNotifier mClickNotifier; -    @Mock -    private OverviewProxyService mOverviewProxyService; -    @Mock -    private KeyguardManager mKeyguardManager; -    @Mock -    private DeviceProvisionedController mDeviceProvisionedController; -    @Mock -    private StatusBarStateController mStatusBarStateController; -    @Mock -    private BroadcastDispatcher mBroadcastDispatcher; -    @Mock -    private KeyguardStateController mKeyguardStateController; - -    private UserInfo mCurrentUser; -    private UserInfo mSecondaryUser; -    private UserInfo mWorkUser; -    private UserInfo mCommunalUser; -    private FakeSettings mSettings; -    private TestNotificationLockscreenUserManager mLockscreenUserManager; -    private NotificationEntry mCurrentUserNotif; -    private NotificationEntry mSecondaryUserNotif; -    private NotificationEntry mWorkProfileNotif; -    private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic(); -    private Executor mMainExecutor = Runnable::run; // Direct executor -    private Executor mBackgroundExecutor = Runnable::run; // Direct executor - -    @Before -    public void setUp() { -        MockitoAnnotations.initMocks(this); - -        mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false); - -        int currentUserId = ActivityManager.getCurrentUser(); -        when(mUserTracker.getUserId()).thenReturn(currentUserId); -        mSettings = new FakeSettings(); -        mSettings.setUserId(ActivityManager.getCurrentUser()); -        mCurrentUser = new UserInfo(currentUserId, "", 0); -        mSecondaryUser = new UserInfo(currentUserId + 1, "", 0); -        mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0, -                UserManager.USER_TYPE_PROFILE_MANAGED); -        mCommunalUser = new UserInfo(currentUserId + 3, "" /* name */, null /* iconPath */, 0, -                UserManager.USER_TYPE_PROFILE_COMMUNAL); - -        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true); -        when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList( -                mCurrentUser, mWorkUser)); -        when(mUserManager.getProfilesIncludingCommunal(currentUserId)).thenReturn( -                Lists.newArrayList(mCurrentUser, mWorkUser, mCommunalUser)); -        when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList( -                mSecondaryUser)); -        when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn( -                Lists.newArrayList(mSecondaryUser, mCommunalUser)); - -        Notification notifWithPrivateVisibility = new Notification(); -        notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE; -        mCurrentUserNotif = new NotificationEntryBuilder() -                .setNotification(notifWithPrivateVisibility) -                .setUser(new UserHandle(mCurrentUser.id)) -                .build(); -        mSecondaryUserNotif = new NotificationEntryBuilder() -                .setNotification(notifWithPrivateVisibility) -                .setUser(new UserHandle(mSecondaryUser.id)) -                .build(); -        mWorkProfileNotif = new NotificationEntryBuilder() -                .setNotification(notifWithPrivateVisibility) -                .setUser(new UserHandle(mWorkUser.id)) -                .build(); - -        mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext); -        mLockscreenUserManager.setUpWithPresenter(mPresenter); -    } - -    private void changeSetting(String setting) { -        final Collection<Uri> lockScreenUris = new ArrayList<>(); -        lockScreenUris.add(Settings.Secure.getUriFor(setting)); -        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false, -            lockScreenUris, 0); -    } - -    @Test -    public void testGetCurrentProfiles() { -        final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>(); -        expectedCurProfiles.put(mCurrentUser.id, mCurrentUser); -        expectedCurProfiles.put(mWorkUser.id, mWorkUser); -        if (android.multiuser.Flags.supportCommunalProfile()) { -            expectedCurProfiles.put(mCommunalUser.id, mCommunalUser); -        } -        assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedCurProfiles)); - -        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); - -        final SparseArray<UserInfo> expectedSecProfiles = new SparseArray<>(); -        expectedSecProfiles.put(mSecondaryUser.id, mSecondaryUser); -        if (android.multiuser.Flags.supportCommunalProfile()) { -            expectedSecProfiles.put(mCommunalUser.id, mCommunalUser); -        } -        assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedSecProfiles)); -    } - -    @Test -    public void testLockScreenShowNotificationsFalse() { -        mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); -        assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); -    } - -    @Test -    public void testLockScreenShowNotificationsTrue() { -        mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); -        assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications()); -    } - -    @Test -    public void testLockScreenAllowPrivateNotificationsTrue() { -        mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); -    } - -    @Test -    public void testLockScreenAllowPrivateNotificationsFalse() { -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mCurrentUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); -    } - -    @Test -    public void testLockScreenAllowsWorkPrivateNotificationsFalse() { -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mWorkUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); -    } - -    @Test -    public void testLockScreenAllowsWorkPrivateNotificationsTrue() { -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mWorkUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); -    } - -    @Test -    public void testCurrentUserPrivateNotificationsNotRedacted() { -        // GIVEN current user doesn't allow private notifications to show -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mCurrentUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN current user's notification is redacted -        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); -    } - -    @Test -    public void testCurrentUserPrivateNotificationsRedacted() { -        // GIVEN current user allows private notifications to show -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mCurrentUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN current user's notification isn't redacted -        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); -    } - -    @Test -    public void testWorkPrivateNotificationsRedacted() { -        // GIVEN work profile doesn't private notifications to show -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mWorkUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN work profile notification is redacted -        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); -        assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic()); -    } - -    @Test -    public void testWorkPrivateNotificationsNotRedacted() { -        // GIVEN work profile allows private notifications to show -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mWorkUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN work profile notification isn't redacted -        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); -        assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic()); -    } - -    @Test -    public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() { -        // GIVEN work profile allows private notifications to show but the other users don't -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mWorkUser.id); -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mCurrentUser.id); -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN the work profile notification doesn't need to be redacted -        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); - -        // THEN the current user and secondary user notifications do need to be redacted -        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); -        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); -    } - -    @Test -    public void testWorkProfileRedacted_otherUsersNotRedacted() { -        // GIVEN work profile doesn't allow private notifications to show but the other users do -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mWorkUser.id); -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mCurrentUser.id); -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN the work profile notification needs to be redacted -        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); - -        // THEN the current user and secondary user notifications don't need to be redacted -        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); -        assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); -    } - -    @Test -    public void testSecondaryUserNotRedacted_currentUserRedacted() { -        // GIVEN secondary profile allows private notifications to show but the current user -        // doesn't allow private notifications to show -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, -                mCurrentUser.id); -        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, -                mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); -        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); - -        // THEN the secondary profile notification still needs to be redacted because the current -        // user's setting takes precedence -        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); -    } - -    @Test -    public void testUserSwitchedCallsOnUserSwitching() { -        mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id, -                mContext); -        verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id); -    } - -    @Test -    public void testIsLockscreenPublicMode() { -        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id)); -        mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id); -        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id)); -    } - -    @Test -    public void testUpdateIsPublicMode() { -        when(mKeyguardStateController.isMethodSecure()).thenReturn(true); - -        NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class); -        mLockscreenUserManager.addNotificationStateChangedListener(listener); -        mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class)); - -        // first call explicitly sets user 0 to not public; notifies -        mLockscreenUserManager.updatePublicMode(); -        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); -        verify(listener).onNotificationStateChanged(); -        clearInvocations(listener); - -        // calling again has no changes; does not notify -        mLockscreenUserManager.updatePublicMode(); -        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0)); -        verify(listener, never()).onNotificationStateChanged(); - -        // Calling again with keyguard now showing makes user 0 public; notifies -        when(mKeyguardStateController.isShowing()).thenReturn(true); -        mLockscreenUserManager.updatePublicMode(); -        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); -        verify(listener).onNotificationStateChanged(); -        clearInvocations(listener); - -        // calling again has no changes; does not notify -        mLockscreenUserManager.updatePublicMode(); -        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0)); -        verify(listener, never()).onNotificationStateChanged(); -    } - -    @Test -    public void testDevicePolicyDoesNotAllowNotifications() { -        // User allows them -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        // DevicePolicy hides notifs on lockscreen -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) -                .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mCurrentUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); -    } - -    @Test -    public void testDevicePolicyDoesNotAllowNotifications_secondary() { -        Mockito.clearInvocations(mDevicePolicyManager); -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        // DevicePolicy hides notifications -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) -                .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); - -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mSecondaryUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); -    } - -    @Test -    public void testDevicePolicy_noPrivateNotifications() { -        Mockito.clearInvocations(mDevicePolicyManager); -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        // DevicePolicy hides sensitive content -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) -                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mCurrentUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); -    } - -    @Test -    public void testDevicePolicy_noPrivateNotifications_userAll() { -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        // DevicePolicy hides sensitive content -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) -                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mCurrentUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder() -                .setNotification(new Notification()) -                .setUser(UserHandle.ALL) -                .build())); -    } - -    @Test -    public void testDevicePolicyPrivateNotifications_secondary() { -        Mockito.clearInvocations(mDevicePolicyManager); -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        // DevicePolicy hides sensitive content -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id)) -                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); - -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mSecondaryUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); -        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); -    } - -    @Test -    public void testHideNotifications_primary() { -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); -    } - -    @Test -    public void testHideNotifications_secondary() { -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); -    } - -    @Test -    public void testHideNotifications_secondary_userSwitch() { -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); -    } - -    @Test -    public void testShowNotifications_secondary_userSwitch() { -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext); - -        assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id)); -    } - -    @Test -    public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() { -        // DevicePolicy allows notifications -        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id)) -                .thenReturn(0); -        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult( -                0, null, null, 0, true, false, null, mCurrentUser.id, 0); -        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr); -        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext, -                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)); - -        // KeyguardManager does not allow notifications -        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false); - -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); -        // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no -        // callback, so it's only updated when the setting is -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); -    } - -    @Test -    public void testUserAllowsNotificationsInPublic_settingsChange() { -        // User allows notifications -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); - -        // User disables -        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id); -        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS); - -        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id)); -    } - -    private class TestNotificationLockscreenUserManager -            extends NotificationLockscreenUserManagerImpl { -        public TestNotificationLockscreenUserManager(Context context) { -            super( -                    context, -                    mBroadcastDispatcher, -                    mDevicePolicyManager, -                    mUserManager, -                    mUserTracker, -                    (() -> mVisibilityProvider), -                    (() -> mNotifCollection), -                    mClickNotifier, -                    (() -> mOverviewProxyService), -                    NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager, -                    mStatusBarStateController, -                    mMainExecutor, -                    mBackgroundExecutor, -                    mDeviceProvisionedController, -                    mKeyguardStateController, -                    mSettings, -                    mock(DumpManager.class), -                    mock(LockPatternUtils.class), -                    mFakeFeatureFlags); -        } - -        public BroadcastReceiver getBaseBroadcastReceiverForTest() { -            return mBaseBroadcastReceiver; -        } - -        public UserTracker.Callback getUserTrackerCallbackForTest() { -            return mUserChangedCallback; -        } - -        public ContentObserver getLockscreenSettingsObserverForTest() { -            return mLockscreenSettingsObserver; -        } - -        public ContentObserver getSettingsObserverForTest() { -            return mSettingsObserver; -        } -    } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index bcc0710359cc..d505b27a9969 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -172,8 +172,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {      public void setUp() {          MockitoAnnotations.initMocks(this); -        mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); -          int currentUserId = ActivityManager.getCurrentUser();          when(mUserTracker.getUserId()).thenReturn(currentUserId);          mSettings = new FakeSettings(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index 4c824c0d130a..87d25ddcc75c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -46,12 +46,12 @@ import android.content.Intent;  import android.graphics.Region;  import android.os.UserHandle;  import android.service.notification.StatusBarNotification; -import android.testing.AndroidTestingRunner;  import android.testing.TestableLooper;  import androidx.annotation.NonNull;  import androidx.annotation.Nullable;  import androidx.test.filters.SmallTest; +import androidx.test.ext.junit.runners.AndroidJUnit4;  import com.android.internal.logging.UiEventLogger;  import com.android.internal.logging.testing.UiEventLoggerFake; @@ -75,8 +75,8 @@ import org.mockito.junit.MockitoJUnit;  import org.mockito.junit.MockitoRule;  @SmallTest -@RunWith(AndroidTestingRunner.class)  @TestableLooper.RunWithLooper +@RunWith(AndroidJUnit4.class)  public class BaseHeadsUpManagerTest extends SysuiTestCase {      @Rule      public MockitoRule rule = MockitoJUnit.rule(); diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags index f9546c46e915..162d8aebfc62 100644 --- a/packages/SystemUI/proguard_common.flags +++ b/packages/SystemUI/proguard_common.flags @@ -17,10 +17,6 @@    <1> *;  } --keepclasseswithmembers class * { -    public <init>(android.content.Context, android.util.AttributeSet); -} -  -keep class androidx.core.app.CoreComponentFactory  # Keep the wm shell lib diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java index a9928d80117b..63088aaf7ff4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java @@ -20,6 +20,7 @@ import static android.app.slice.Slice.HINT_LIST_ITEM;  import android.app.PendingIntent;  import android.net.Uri; +import android.os.Handler;  import android.os.Trace;  import android.provider.Settings;  import android.util.Log; @@ -39,6 +40,9 @@ import androidx.slice.widget.SliceLiveData;  import com.android.keyguard.dagger.KeyguardStatusViewScope;  import com.android.systemui.Dumpable; +import com.android.systemui.Flags; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main;  import com.android.systemui.dump.DumpManager;  import com.android.systemui.keyguard.KeyguardSliceProvider;  import com.android.systemui.plugins.ActivityStarter; @@ -60,6 +64,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie          Dumpable {      private static final String TAG = "KeyguardSliceViewCtrl"; +    private final Handler mHandler; +    private final Handler mBgHandler;      private final ActivityStarter mActivityStarter;      private final ConfigurationController mConfigurationController;      private final TunerService mTunerService; @@ -105,6 +111,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie      @Inject      public KeyguardSliceViewController( +            @Main Handler handler, +            @Background Handler bgHandler,              KeyguardSliceView keyguardSliceView,              ActivityStarter activityStarter,              ConfigurationController configurationController, @@ -112,6 +120,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie              DumpManager dumpManager,              DisplayTracker displayTracker) {          super(keyguardSliceView); +        mHandler = handler; +        mBgHandler = bgHandler;          mActivityStarter = activityStarter;          mConfigurationController = configurationController;          mTunerService = tunerService; @@ -182,24 +192,34 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie       * Update contents of the view.       */      public void refresh() { -        Slice slice; +          Trace.beginSection("KeyguardSliceViewController#refresh"); -        // We can optimize performance and avoid binder calls when we know that we're bound -        // to a Slice on the same process. -        if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { -            KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); -            if (instance != null) { -                slice = instance.onBindSlice(mKeyguardSliceUri); +        try { +            Slice slice; +            if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) { +                KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance(); +                if (instance != null) { +                    if (Flags.sliceManagerBinderCallBackground()) { +                        mBgHandler.post(() -> { +                            Slice _slice = instance.onBindSlice(mKeyguardSliceUri); +                            mHandler.post(() -> mObserver.onChanged(_slice)); +                        }); +                        return; +                    } +                    slice = instance.onBindSlice(mKeyguardSliceUri); +                } else { +                    Log.w(TAG, "Keyguard slice not bound yet?"); +                    slice = null; +                }              } else { -                Log.w(TAG, "Keyguard slice not bound yet?"); -                slice = null; +                // TODO: Make SliceViewManager injectable +                slice = SliceViewManager.getInstance(mView.getContext()).bindSlice( +                        mKeyguardSliceUri);              } -        } else { -            // TODO: Make SliceViewManager injectable -            slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri); +            mObserver.onChanged(slice); +        } finally { +            Trace.endSection();          } -        mObserver.onChanged(slice); -        Trace.endSection();      }      void showSlice(Slice slice) { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 6eff79284847..56162aeaab2a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -102,11 +102,6 @@ object Flags {              default = true          ) -    // TODO(b/301955929) -    @JvmField -    val NOTIF_LS_BACKGROUND_THREAD = -            releasedFlag("notification_lockscreen_mgr_bg_thread") -      // 200 - keyguard/lockscreen      // ** Flag retired **      // public static final BooleanFlag KEYGUARD_LAYOUT = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt index 72a81cbac9d5..0f1cc99bfef7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt @@ -20,8 +20,8 @@ package com.android.systemui.keyboard.stickykeys.shared.model  value class Locked(val locked: Boolean)  enum class ModifierKey(val displayedText: String) { -    ALT("ALT LEFT"), -    ALT_GR("ALT RIGHT"), +    ALT("ALT"), +    ALT_GR("ALT"),      CTRL("CTRL"),      META("ACTION"),      SHIFT("SHIFT"), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt index f208e85bde3e..8a3b57ba027f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt @@ -18,7 +18,9 @@  package com.android.systemui.keyguard.ui.viewmodel +import com.android.systemui.Flags.migrateClocksToBlueprint  import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor  import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor  import com.android.systemui.keyguard.shared.model.KeyguardState.AOD  import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING @@ -38,6 +40,7 @@ constructor(      keyguardTransitionInteractor: KeyguardTransitionInteractor,      goneToAodTransitionViewModel: GoneToAodTransitionViewModel,      goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, +    keyguardInteractor: KeyguardInteractor,  ) {      /** The alpha level for the entire lockscreen while in AOD. */ @@ -46,7 +49,8 @@ constructor(                  keyguardTransitionInteractor.transitions,                  goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },                  goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) }, -            ) { step, goneToAodAlpha, goneToDozingAlpha -> +                keyguardInteractor.keyguardAlpha.onStart { emit(1f) }, +            ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->                  if (step.to == GONE) {                      // When transitioning to GONE, only emit a value when complete as other                      // transitions may be controlling the alpha fade @@ -57,6 +61,8 @@ constructor(                      emit(goneToAodAlpha)                  } else if (step.from == GONE && step.to == DOZING) {                      emit(goneToDozingAlpha) +                } else if (!migrateClocksToBlueprint()) { +                    emit(keyguardAlpha)                  }              }              .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index ffb11dd3cf92..3908edec7da5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -177,6 +177,7 @@ public class CommandQueue extends IStatusBar.Stub implements      private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;      private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;      private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT; +    private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;      public static final int FLAG_EXCLUDE_NONE = 0;      public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;      public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1; @@ -520,6 +521,11 @@ public class CommandQueue extends IStatusBar.Stub implements           * @see IStatusBar#immersiveModeChanged           */          default void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {} + +        /** +         * @see IStatusBar#enterDesktop(int) +         */ +        default void enterDesktop(int displayId) {}      }      @VisibleForTesting @@ -609,14 +615,7 @@ public class CommandQueue extends IStatusBar.Stub implements              args.argi2 = state1;              args.argi3 = state2;              args.argi4 = animate ? 1 : 0; -            Message msg = mHandler.obtainMessage(MSG_DISABLE, args); -            if (Looper.myLooper() == mHandler.getLooper()) { -                // If its the right looper execute immediately so hides can be handled quickly. -                mHandler.handleMessage(msg); -                msg.recycle(); -            } else { -                msg.sendToTarget(); -            } +            mHandler.obtainMessage(MSG_DISABLE, args).sendToTarget();          }      } @@ -1420,6 +1419,13 @@ public class CommandQueue extends IStatusBar.Stub implements          mHandler.obtainMessage(MSG_GO_TO_FULLSCREEN_FROM_SPLIT).sendToTarget();      } +    @Override +    public void enterDesktop(int displayId) { +        SomeArgs args = SomeArgs.obtain(); +        args.arg1 = displayId; +        mHandler.obtainMessage(MSG_ENTER_DESKTOP, args).sendToTarget(); +    } +      private final class H extends Handler {          private H(Looper l) {              super(l); @@ -1914,6 +1920,13 @@ public class CommandQueue extends IStatusBar.Stub implements                          mCallbacks.get(i).immersiveModeChanged(rootDisplayAreaId, isImmersiveMode);                      }                      break; +                case MSG_ENTER_DESKTOP: +                    args = (SomeArgs) msg.obj; +                    int displayId = args.argi1; +                    for (int i = 0; i < mCallbacks.size(); i++) { +                        mCallbacks.get(i).enterDesktop(displayId); +                    } +                    break;              }          }      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 9916ef6ff9ee..1a06eec1cb4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -156,14 +156,12 @@ public class NotificationLockscreenUserManagerImpl implements              final String action = intent.getAction();              if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) { -                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -                    mKeyguardAllowingNotifications = -                            intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false); -                    if (mCurrentUserId == getSendingUserId()) { -                        boolean changed = updateLockscreenNotificationSetting(); -                        if (changed) { -                            notifyNotificationStateChanged(); -                        } +                mKeyguardAllowingNotifications = +                        intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false); +                if (mCurrentUserId == getSendingUserId()) { +                    boolean changed = updateLockscreenNotificationSetting(); +                    if (changed) { +                        notifyNotificationStateChanged();                      }                  }              } @@ -176,36 +174,26 @@ public class NotificationLockscreenUserManagerImpl implements              final String action = intent.getAction();              if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { -                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -                    boolean changed = false; -                    int sendingUserId = getSendingUserId(); -                    if (sendingUserId == USER_ALL) { -                        // When a Device Owner triggers changes it's sent as USER_ALL. Normalize -                        // the user before calling into DPM -                        sendingUserId = mCurrentUserId; -                        @SuppressLint("MissingPermission") -                        List<UserInfo> users = mUserManager.getUsers(); -                        for (int i = users.size() - 1; i >= 0; i--) { -                            changed |= updateDpcSettings(users.get(i).id); -                        } -                    } else { -                        changed |= updateDpcSettings(sendingUserId); -                    } - -                    if (mCurrentUserId == sendingUserId) { -                        changed |= updateLockscreenNotificationSetting(); -                    } -                    if (changed) { -                        notifyNotificationStateChanged(); +                boolean changed = false; +                int sendingUserId = getSendingUserId(); +                if (sendingUserId == USER_ALL) { +                    // When a Device Owner triggers changes it's sent as USER_ALL. Normalize +                    // the user before calling into DPM +                    sendingUserId = mCurrentUserId; +                    @SuppressLint("MissingPermission") +                    List<UserInfo> users = mUserManager.getUsers(); +                    for (int i = users.size() - 1; i >= 0; i--) { +                        changed |= updateDpcSettings(users.get(i).id);                      }                  } else { -                    if (isCurrentProfile(getSendingUserId())) { -                        mUsersAllowingPrivateNotifications.clear(); -                        updateLockscreenNotificationSetting(); -                        // TODO(b/231976036): Consolidate pipeline invalidations related to this -                        //  event -                        // notifyNotificationStateChanged(); -                    } +                    changed |= updateDpcSettings(sendingUserId); +                } + +                if (mCurrentUserId == sendingUserId) { +                    changed |= updateLockscreenNotificationSetting(); +                } +                if (changed) { +                    notifyNotificationStateChanged();                  }              }          } @@ -225,12 +213,10 @@ public class NotificationLockscreenUserManagerImpl implements                  updateCurrentProfilesCache();              } else if (Objects.equals(action, Intent.ACTION_USER_ADDED)){                  updateCurrentProfilesCache(); -                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); -                    mBackgroundExecutor.execute(() -> { -                        initValuesForUser(userId); -                    }); -                } +                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); +                mBackgroundExecutor.execute(() -> { +                    initValuesForUser(userId); +                });              } else if (profileAvailabilityActions(action)) {                  updateCurrentProfilesCache();              } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) { @@ -360,28 +346,16 @@ public class NotificationLockscreenUserManagerImpl implements      }      private void init() { -        mLockscreenSettingsObserver = new ExecutorContentObserver( -                mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) -                        ? mBackgroundExecutor -                        : mMainExecutor) { +        mLockscreenSettingsObserver = new ExecutorContentObserver(mBackgroundExecutor) {              @Override              public void onChange(boolean selfChange, Collection<Uri> uris, int flags) { -                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -                    @SuppressLint("MissingPermission") -                    List<UserInfo> users = mUserManager.getUsers(); -                    for (int i = users.size() - 1; i >= 0; i--) { -                        onChange(selfChange, uris, flags,users.get(i).getUserHandle()); -                    } -                } else { -                    // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or -                    // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ... -                    mUsersAllowingPrivateNotifications.clear(); -                    mUsersAllowingNotifications.clear(); -                    // ... and refresh all the notifications -                    updateLockscreenNotificationSetting(); -                    notifyNotificationStateChanged(); +                @SuppressLint("MissingPermission") +                List<UserInfo> users = mUserManager.getUsers(); +                for (int i = users.size() - 1; i >= 0; i--) { +                    onChange(selfChange, uris, flags,users.get(i).getUserHandle());                  } +              }              // Note: even though this is an override, this method is not called by the OS @@ -390,22 +364,20 @@ public class NotificationLockscreenUserManagerImpl implements              @Override              public void onChange(boolean selfChange, Collection<Uri> uris,                      int flags, UserHandle user) { -                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -                    boolean changed = false; -                    for (Uri uri: uris) { -                        if (SHOW_LOCKSCREEN.equals(uri)) { -                            changed |= updateUserShowSettings(user.getIdentifier()); -                        } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) { -                            changed |= updateUserShowPrivateSettings(user.getIdentifier()); -                        } +                boolean changed = false; +                for (Uri uri: uris) { +                    if (SHOW_LOCKSCREEN.equals(uri)) { +                        changed |= updateUserShowSettings(user.getIdentifier()); +                    } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) { +                        changed |= updateUserShowPrivateSettings(user.getIdentifier());                      } +                } -                    if (mCurrentUserId == user.getIdentifier()) { -                        changed |= updateLockscreenNotificationSetting(); -                    } -                    if (changed) { -                        notifyNotificationStateChanged(); -                    } +                if (mCurrentUserId == user.getIdentifier()) { +                    changed |= updateLockscreenNotificationSetting(); +                } +                if (changed) { +                    notifyNotificationStateChanged();                  }              }          }; @@ -432,16 +404,10 @@ public class NotificationLockscreenUserManagerImpl implements                  mLockscreenSettingsObserver,                  USER_ALL); -        if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            mContext.getContentResolver().registerContentObserver( -                    Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, -                    mSettingsObserver); -        }          mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,                  new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED), -                mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD) -                        ? mBackgroundExecutor : null, UserHandle.ALL); +                mBackgroundExecutor, UserHandle.ALL);          if (keyguardPrivateNotifications()) {              mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,                      new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED), @@ -471,17 +437,13 @@ public class NotificationLockscreenUserManagerImpl implements          mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late          updateCurrentProfilesCache(); -        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            // Set  up -            mBackgroundExecutor.execute(() -> { -                @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers(); -                for (int i = users.size() - 1; i >= 0; i--) { -                    initValuesForUser(users.get(i).id); -                } -            }); -        } else { -            mSettingsObserver.onChange(false);  // set up -        } +        // Set  up +        mBackgroundExecutor.execute(() -> { +            @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers(); +            for (int i = users.size() - 1; i >= 0; i--) { +                initValuesForUser(users.get(i).id); +            } +        });      }      private void initValuesForUser(@UserIdInt int userId) { @@ -519,26 +481,15 @@ public class NotificationLockscreenUserManagerImpl implements          boolean show;          boolean allowedByDpm; -        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            if (keyguardPrivateNotifications()) { -                show = mUsersUsersAllowingNotifications.get(mCurrentUserId); -            } else { -                show = mUsersUsersAllowingNotifications.get(mCurrentUserId) -                        && mKeyguardAllowingNotifications; -            } -            // If DPC never notified us about a user, that means they have no policy for the user, -            // and they allow the behavior -            allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true); +        if (keyguardPrivateNotifications()) { +            show = mUsersUsersAllowingNotifications.get(mCurrentUserId);          } else { -            show = mSecureSettings.getIntForUser( -                    LOCK_SCREEN_SHOW_NOTIFICATIONS, -                    1, -                    mCurrentUserId) != 0; -            final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures( -                    null /* admin */, mCurrentUserId); -            allowedByDpm = (dpmFlags -                    & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; +            show = mUsersUsersAllowingNotifications.get(mCurrentUserId) +                    && mKeyguardAllowingNotifications;          } +        // If DPC never notified us about a user, that means they have no policy for the user, +        // and they allow the behavior +        allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);          final boolean oldValue = mShowLockscreenNotifications;          setShowLockscreenNotifications(show && allowedByDpm); @@ -600,42 +551,24 @@ public class NotificationLockscreenUserManagerImpl implements       * when the lockscreen is in "public" (secure & locked) mode?       */      public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { -        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            if (userHandle == USER_ALL) { -                userHandle = mCurrentUserId; -            } -            if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { -                Log.i(TAG, "Asking for redact notifs setting too early", new Throwable()); -                return false; -            } -            if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { -                Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable()); -                return false; -            } -            if (keyguardPrivateNotifications()) { -                return mUsersUsersAllowingPrivateNotifications.get(userHandle) -                        && mUsersDpcAllowingPrivateNotifications.get(userHandle) -                        && mKeyguardAllowingNotifications; -            } else { -                return mUsersUsersAllowingPrivateNotifications.get(userHandle) -                        && mUsersDpcAllowingPrivateNotifications.get(userHandle); -            } +        if (userHandle == USER_ALL) { +            userHandle = mCurrentUserId; +        } +        if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { +            Log.i(TAG, "Asking for redact notifs setting too early", new Throwable()); +            return false; +        } +        if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { +            Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable()); +            return false; +        } +        if (keyguardPrivateNotifications()) { +            return mUsersUsersAllowingPrivateNotifications.get(userHandle) +                    && mUsersDpcAllowingPrivateNotifications.get(userHandle) +                    && mKeyguardAllowingNotifications;          } else { -            if (userHandle == USER_ALL) { -                return true; -            } - -            if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { -                final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( -                        LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); -                final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, -                        KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); -                final boolean allowed = allowedByUser && allowedByDpm; -                mUsersAllowingPrivateNotifications.append(userHandle, allowed); -                return allowed; -            } - -            return mUsersAllowingPrivateNotifications.get(userHandle); +            return mUsersUsersAllowingPrivateNotifications.get(userHandle) +                    && mUsersDpcAllowingPrivateNotifications.get(userHandle);          }      } @@ -688,48 +621,30 @@ public class NotificationLockscreenUserManagerImpl implements       * "public" (secure & locked) mode?       */      public boolean userAllowsNotificationsInPublic(int userHandle) { -        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            // Unlike 'show private', settings does not show a copy of this setting for each -            // profile, so it inherits from the parent user. -            if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) { -                userHandle = mCurrentUserId; -            } -            if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) { -                // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe -                // default value before moving to 'released' -                Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable()); -                updateUserShowSettings(userHandle); -            } -            if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) { -                // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe -                // default value before moving to 'released' -                Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable()); -                updateDpcSettings(userHandle); -            } -            if (keyguardPrivateNotifications()) { -                return mUsersUsersAllowingNotifications.get(userHandle) -                        && mUsersDpcAllowingNotifications.get(userHandle); -            } else { -                return mUsersUsersAllowingNotifications.get(userHandle) -                        && mUsersDpcAllowingNotifications.get(userHandle) -                        && mKeyguardAllowingNotifications; -            } +        // Unlike 'show private', settings does not show a copy of this setting for each +        // profile, so it inherits from the parent user. +        if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) { +            userHandle = mCurrentUserId; +        } +        if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) { +            // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe +            // default value before moving to 'released' +            Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable()); +            updateUserShowSettings(userHandle); +        } +        if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) { +            // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe +            // default value before moving to 'released' +            Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable()); +            updateDpcSettings(userHandle); +        } +        if (keyguardPrivateNotifications()) { +            return mUsersUsersAllowingNotifications.get(userHandle) +                    && mUsersDpcAllowingNotifications.get(userHandle);          } else { -            if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { -                return true; -            } - -            if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { -                final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( -                        LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); -                final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, -                        KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); -                final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed(); -                final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem; -                mUsersAllowingNotifications.append(userHandle, allowed); -                return allowed; -            } -            return mUsersAllowingNotifications.get(userHandle); +            return mUsersUsersAllowingNotifications.get(userHandle) +                    && mUsersDpcAllowingNotifications.get(userHandle) +                    && mKeyguardAllowingNotifications;          }      } @@ -766,13 +681,7 @@ public class NotificationLockscreenUserManagerImpl implements              return true;          }          NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); -        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            return entry != null && entry.isChannelVisibilityPrivate(); -        } else { -            return entry != null -                    && entry.getRanking().getLockscreenVisibilityOverride() -                    == Notification.VISIBILITY_PRIVATE; -        } +        return entry != null && entry.isChannelVisibilityPrivate();      }      @SuppressLint("MissingPermission") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt index 6429815bcb9f..11636bdf0f17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt @@ -17,6 +17,8 @@  package com.android.systemui.statusbar.data.repository  import android.graphics.Rect +import android.view.InsetsFlags +import android.view.ViewDebug  import android.view.WindowInsets  import android.view.WindowInsetsController  import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS @@ -305,8 +307,8 @@ constructor(          letterboxDetails.isNotEmpty()      override fun dump(pw: PrintWriter, args: Array<out String>) { -        pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}") -        pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}") +        pw.println("${_originalStatusBarAttributes.value}") +        pw.println("${modifiedStatusBarAttributes.value}")          pw.println("statusBarMode: ${statusBarMode.value}")      } @@ -320,7 +322,20 @@ constructor(          val navbarColorManagedByIme: Boolean,          @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,          val letterboxDetails: List<LetterboxDetails>, -    ) +    ) { +        override fun toString(): String { +            return """ +                StatusBarAttributes( +                    appearance=${appearance.toAppearanceString()}, +                    appearanceRegions=$appearanceRegions, +                    navbarColorManagedByIme=$navbarColorManagedByIme, +                    requestedVisibleTypes=${requestedVisibleTypes.toWindowInsetsString()}, +                    letterboxDetails=$letterboxDetails +                    ) +                    """ +                .trimIndent() +        } +    }      /**       * Internal class keeping track of how [StatusBarAttributes] were transformed into new @@ -331,9 +346,31 @@ constructor(          val appearanceRegions: List<AppearanceRegion>,          val navbarColorManagedByIme: Boolean,          val statusBarBounds: BoundsPair, -    ) +    ) { +        override fun toString(): String { +            return """ +                ModifiedStatusBarAttributes( +                    appearance=${appearance.toAppearanceString()}, +                    appearanceRegions=$appearanceRegions, +                    navbarColorManagedByIme=$navbarColorManagedByIme, +                    statusBarBounds=$statusBarBounds +                    ) +                    """ +                .trimIndent() +        } +    }  } +private fun @receiver:WindowInsets.Type.InsetsType Int.toWindowInsetsString() = +    "[${WindowInsets.Type.toString(this).replace(" ", ", ")}]" + +private fun @receiver:Appearance Int.toAppearanceString() = +    if (this == 0) { +        "NONE" +    } else { +        ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this) +    } +  @AssistedFactory  interface StatusBarModePerDisplayRepositoryFactory {      fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt index c8f996a0272f..c416d434a8fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt @@ -60,7 +60,7 @@ constructor(          val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true          if (!didAppend) {              frames.add(Frame(event)) -            if (frames.size > maxFrames) frames.removeFirst() +            if (frames.size > maxFrames) frames.removeAt(0)          }      } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt index 9fb453afb55e..65ab4fdeb77b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt @@ -136,7 +136,7 @@ object FooterViewBinder {          }          launch { -            viewModel.clearAllButton.accessibilityDescriptionId.collect { textId -> +            viewModel.manageOrHistoryButton.accessibilityDescriptionId.collect { textId ->                  footer.setManageOrHistoryButtonDescription(textId)              }          } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index d7fe36f75418..332ece428a6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -219,15 +219,11 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor(      }      private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean { -        return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { -            // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting -            // info, and NotificationLockscreenUserManagerImpl is already listening for updates -            // to those -            entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility == +        // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting +        // info, and NotificationLockscreenUserManagerImpl is already listening for updates +        // to those +        return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==                      VISIBILITY_SECRET -        } else { -            entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET -        }      }      override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index 1414150c5511..2c1780d3b304 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -158,7 +158,15 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {      @Override      public void showNotification(@NonNull NotificationEntry entry) {          mLogger.logShowNotification(entry); -        addEntry(entry); + +        // Add new entry and begin managing it +        HeadsUpEntry headsUpEntry = createHeadsUpEntry(); +        headsUpEntry.setEntry(entry); +        mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry); +        onEntryAdded(headsUpEntry); +        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); +        entry.setIsHeadsUpEntry(true); +          updateNotification(entry.getKey(), true /* shouldHeadsUpAgain */);          entry.setInterruption();      } @@ -319,19 +327,6 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {      }      /** -     * Add a new entry and begin managing it. -     * @param entry the entry to add -     */ -    protected final void addEntry(@NonNull NotificationEntry entry) { -        HeadsUpEntry headsUpEntry = createHeadsUpEntry(); -        headsUpEntry.setEntry(entry); -        mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry); -        onEntryAdded(headsUpEntry); -        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); -        entry.setIsHeadsUpEntry(true); -    } - -    /**       * Manager-specific logic that should occur when an entry is added.       * @param headsUpEntry entry added       */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index df210b073e77..600005b97610 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -16,6 +16,8 @@  package com.android.systemui.statusbar.policy; +import static com.android.systemui.Flags.registerZenModeContentObserverBackground; +  import android.app.AlarmManager;  import android.app.Flags;  import android.app.NotificationManager; @@ -45,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting;  import com.android.systemui.Dumpable;  import com.android.systemui.broadcast.BroadcastDispatcher;  import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background;  import com.android.systemui.dagger.qualifiers.Main;  import com.android.systemui.dump.DumpManager;  import com.android.systemui.settings.UserTracker; @@ -104,6 +107,7 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {      public ZenModeControllerImpl(              Context context,              @Main Handler handler, +            @Background Handler bgHandler,              BroadcastDispatcher broadcastDispatcher,              DumpManager dumpManager,              GlobalSettings globalSettings, @@ -134,9 +138,18 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {              }          };          mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); -        globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver); +        if (registerZenModeContentObserverBackground()) { +            bgHandler.post(() -> { +                globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver); +                globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, +                        configContentObserver); +            }); +        } else { +            globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver); +            globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, +                    configContentObserver); +        }          updateZenMode(getModeSettingValueFromProvider()); -        globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, configContentObserver);          updateZenModeConfig();          updateConsolidatedNotificationPolicy();          mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index e8325065c219..15e0965c16fe 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -356,6 +356,12 @@ public final class WMShell implements                          // TODO(b/278084491): update sysui state for changes on other displays                      }                  }, mSysUiMainExecutor); +        mCommandQueue.addCallback(new CommandQueue.Callbacks() { +            @Override +            public void enterDesktop(int displayId) { +                desktopMode.enterDesktop(displayId); +            } +        });      }      @VisibleForTesting diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java index 4d3243a2ccf6..edb910a3acc2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java @@ -22,8 +22,10 @@ import static org.mockito.Mockito.verify;  import static org.mockito.Mockito.when;  import android.content.pm.PackageManager; +import android.os.Handler;  import android.test.suitebuilder.annotation.SmallTest;  import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper;  import android.testing.TestableLooper.RunWithLooper;  import android.view.View; @@ -57,17 +59,22 @@ public class KeyguardSliceViewControllerTest extends SysuiTestCase {      private ActivityStarter mActivityStarter;      private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);      private DumpManager mDumpManager = new DumpManager(); - +    private Handler mHandler; +    private Handler mBgHandler;      private KeyguardSliceViewController mController;      @Before      public void setUp() throws Exception {          MockitoAnnotations.initMocks(this); +        TestableLooper testableLooper = TestableLooper.get(this); +        assert testableLooper != null; +        mHandler = new Handler(testableLooper.getLooper()); +        mBgHandler = new Handler(testableLooper.getLooper());          when(mView.isAttachedToWindow()).thenReturn(true);          when(mView.getContext()).thenReturn(mContext); -        mController = new KeyguardSliceViewController( -                mView, mActivityStarter, mConfigurationController, -                mTunerService, mDumpManager, mDisplayTracker); +        mController = new KeyguardSliceViewController(mHandler, mBgHandler, mView, +                mActivityStarter, mConfigurationController, mTunerService, mDumpManager, +                mDisplayTracker);          mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);      } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index 3811f04a365d..06410cd3530e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -431,28 +431,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      @Test      public void publicMode_settingsDisallow() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true); -        // GIVEN an 'unfiltered-keyguard-showing' state -        setupUnfilteredState(mEntry); - -        // WHEN the notification's user is in public mode and settings are configured to disallow -        // notifications in public mode -        when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true); -        when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID)) -                .thenReturn(false); - -        mEntry.setRanking(new RankingBuilder() -                .setChannel(new NotificationChannel("1", "1", 4)) -                .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) -                .setKey(mEntry.getKey()).build()); - -        // THEN filter out the entry -        assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); -    } - -    @Test -    public void publicMode_settingsDisallow_mainThread() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);          // GIVEN an 'unfiltered-keyguard-showing' state          setupUnfilteredState(mEntry); @@ -473,7 +451,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      @Test      public void publicMode_nullChannel_allowed() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);          // GIVEN an 'unfiltered-keyguard-showing' state          setupUnfilteredState(mEntry); @@ -490,7 +467,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      @Test      public void publicMode_notifDisallowed() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);          NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);          channel.setLockscreenVisibility(VISIBILITY_SECRET);          // GIVEN an 'unfiltered-keyguard-showing' state @@ -509,23 +485,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      }      @Test -    public void publicMode_notifDisallowed_mainThread() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false); -        // GIVEN an 'unfiltered-keyguard-showing' state -        setupUnfilteredState(mEntry); - -        // WHEN the notification's user is in public mode and settings are configured to disallow -        // notifications in public mode -        when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true); -        mEntry.setRanking(new RankingBuilder() -                .setKey(mEntry.getKey()) -                .setVisibilityOverride(VISIBILITY_SECRET).build()); - -        // THEN filter out the entry -        assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); -    } - -    @Test      public void doesNotExceedThresholdToShow() {          // GIVEN an 'unfiltered-keyguard-showing' state          setupUnfilteredState(mEntry); @@ -579,7 +538,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      @Test      public void notificationChannelVisibilityNoOverride() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);          // GIVEN a VISIBILITY_PRIVATE notification          NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()                  .setUser(new UserHandle(NOTIF_USER_ID)); @@ -602,7 +560,6 @@ public class KeyguardNotificationVisibilityProviderTest  extends SysuiTestCase {      @Test      public void notificationChannelVisibilitySecret() { -        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);          // GIVEN a VISIBILITY_PRIVATE notification          NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()                  .setUser(new UserHandle(NOTIF_USER_ID)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java index f1a2c281595d..ddd29c3f2803 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java @@ -79,6 +79,7 @@ public class ZenModeControllerImplTest extends SysuiTestCase {          mController = new ZenModeControllerImpl(                  mContext,                  Handler.createAsync(TestableLooper.get(this).getLooper()), +                Handler.createAsync(TestableLooper.get(this).getLooper()),                  mBroadcastDispatcher,                  mDumpManager,                  mGlobalSettings, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt index 9fb32841d201..f1784a8bc9f2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt @@ -18,6 +18,7 @@  package com.android.systemui.keyguard.ui.viewmodel +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor  import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor  import com.android.systemui.kosmos.Kosmos  import com.android.systemui.kosmos.Kosmos.Fixture @@ -28,5 +29,6 @@ val Kosmos.aodAlphaViewModel by Fixture {          keyguardTransitionInteractor = keyguardTransitionInteractor,          goneToAodTransitionViewModel = goneToAodTransitionViewModel,          goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, +        keyguardInteractor = keyguardInteractor,      )  } diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index 053ed779a27a..3ecdf3f101a5 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -36,9 +36,11 @@ import static com.android.window.flags.Flags.multiCrop;  import static com.google.common.truth.Truth.assertThat;  import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue;  import static org.mockito.ArgumentMatchers.any;  import static org.mockito.ArgumentMatchers.anyBoolean;  import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat;  import static org.mockito.ArgumentMatchers.eq;  import static org.mockito.Mockito.never;  import static org.mockito.Mockito.times; @@ -57,6 +59,7 @@ import android.content.Context;  import android.content.Intent;  import android.content.pm.PackageManager;  import android.content.pm.ResolveInfo; +import android.graphics.Rect;  import android.os.FileUtils;  import android.os.ParcelFileDescriptor;  import android.os.UserHandle; @@ -78,6 +81,7 @@ import org.junit.Rule;  import org.junit.Test;  import org.junit.rules.TemporaryFolder;  import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher;  import org.mockito.Mock;  import org.mockito.MockitoAnnotations; @@ -87,6 +91,7 @@ import java.io.FileOutputStream;  import java.io.IOException;  import java.util.ArrayList;  import java.util.List; +import java.util.Map;  import java.util.Optional;  @RunWith(AndroidJUnit4.class) @@ -815,6 +820,48 @@ public class WallpaperBackupAgentTest {                  WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);      } +    @Test +    public void testOnRestore_noCropHints() throws Exception { +        testParseCropHints(Map.of()); +    } + +    @Test +    public void testOnRestore_singleCropHint() throws Exception { +        Map<Integer, Rect> testMap = Map.of(WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4)); +        testParseCropHints(testMap); +    } + +    @Test +    public void testOnRestore_multipleCropHints() throws Exception { +        Map<Integer, Rect> testMap = Map.of( +                WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4), +                WallpaperManager.SQUARE_PORTRAIT, new Rect(5, 6, 7, 8), +                WallpaperManager.SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12)); +        testParseCropHints(testMap); +    } + +    private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception { +        assumeTrue(multiCrop()); +        mockRestoredStaticWallpaperFile(testMap); +        mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE); +        mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD, +                BackupAnnotations.OperationType.RESTORE); + +        mWallpaperBackupAgent.onRestoreFinished(); + +        ArgumentMatcher<SparseArray<Rect>> matcher = array -> { +            boolean result = testMap.entrySet().stream().allMatch(entry -> { +                int key = entry.getKey(); +                return (array.contains(key) && array.get(key).equals(testMap.get(key))); +            }); +            for (int i = 0; i < array.size(); i++) { +                if (!testMap.containsKey(array.keyAt(i))) result = false; +            } +            return result; +        }; +        verify(mWallpaperManager).setStreamWithCrops(any(), argThat(matcher), eq(true), anyInt()); +    } +      private void mockCurrentWallpaperIds(int systemWallpaperId, int lockWallpaperId) {          when(mWallpaperManager.getWallpaperId(eq(FLAG_SYSTEM))).thenReturn(systemWallpaperId);          when(mWallpaperManager.getWallpaperId(eq(FLAG_LOCK))).thenReturn(lockWallpaperId); @@ -880,6 +927,34 @@ public class WallpaperBackupAgentTest {          fstream.close();      } +    private void mockRestoredStaticWallpaperFile(Map<Integer, Rect> crops) throws Exception { +        File wallpaperFile = new File(mContext.getFilesDir(), WALLPAPER_INFO_STAGE); +        wallpaperFile.createNewFile(); +        FileOutputStream fstream = new FileOutputStream(wallpaperFile, false); +        TypedXmlSerializer out = Xml.resolveSerializer(fstream); +        out.startDocument(null, true); +        out.startTag(null, "wp"); +        for (Map.Entry<Integer, Rect> entry: crops.entrySet()) { +            String orientation = switch (entry.getKey()) { +                case WallpaperManager.PORTRAIT -> "Portrait"; +                case WallpaperManager.LANDSCAPE -> "Landscape"; +                case WallpaperManager.SQUARE_PORTRAIT -> "SquarePortrait"; +                case WallpaperManager.SQUARE_LANDSCAPE -> "SquareLandscape"; +                default -> throw new IllegalArgumentException("Invalid orientation"); +            }; +            Rect rect = entry.getValue(); +            out.attributeInt(null, "cropLeft" + orientation, rect.left); +            out.attributeInt(null, "cropTop" + orientation, rect.top); +            out.attributeInt(null, "cropRight" + orientation, rect.right); +            out.attributeInt(null, "cropBottom" + orientation, rect.bottom); +        } +        out.endTag(null, "wp"); +        out.endDocument(); +        fstream.flush(); +        FileUtils.sync(fstream); +        fstream.close(); +    } +      private WallpaperInfo getFakeWallpaperInfo() throws Exception {          Context context = InstrumentationRegistry.getTargetContext();          Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index d47245eb2272..5a3421799aae 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2770,6 +2770,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      + id + " destroyed");              return;          } +        if (sDebug) { +            Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId +                    + ", data=" + data); +        }          final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);          if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {              setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId); @@ -2823,12 +2827,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);          }          if (result instanceof FillResponse) { +            if (sDebug) { +                Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from" +                        + " authentication flow"); +            }              logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);              mPresentationStatsEventLogger.maybeSetAuthenticationResult(                  AUTHENTICATION_RESULT_SUCCESS);              replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);          } else if (result instanceof GetCredentialResponse) { -            Slog.d(TAG, "Received GetCredentialResponse from authentication flow"); +            if (sDebug) { +                Slog.d(TAG, "Received GetCredentialResponse from authentication flow"); +            }              boolean isCredmanCallbackInvoked = false;              if (Flags.autofillCredmanIntegration()) {                  GetCredentialResponse response = (GetCredentialResponse) result; @@ -2843,6 +2853,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  }              }          } else if (result instanceof Dataset) { +            if (sDebug) { +                Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from" +                        + " authentication flow"); +            }              if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {                  logAuthenticationStatusLocked(requestId,                          MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 31766f2ec4eb..ff076192b6ad 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -29,6 +29,7 @@ import android.app.compat.CompatChanges;  import android.companion.virtual.VirtualDeviceManager.ActivityListener;  import android.compat.annotation.ChangeId;  import android.compat.annotation.EnabledSince; +import android.content.AttributionSource;  import android.content.ComponentName;  import android.content.Intent;  import android.content.pm.ActivityInfo; @@ -44,6 +45,7 @@ import android.window.DisplayWindowPolicyController;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.app.BlockedAppStreamingActivity; +import com.android.modules.expresslog.Counter;  import java.util.Set; @@ -104,6 +106,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController      @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)      public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;      @NonNull +    private final AttributionSource mAttributionSource; +    @NonNull      private final ArraySet<UserHandle> mAllowedUsers;      @GuardedBy("mGenericWindowPolicyControllerLock")      private boolean mActivityLaunchAllowedByDefault; @@ -144,6 +148,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController       *       * @param windowFlags The window flags that this controller is interested in.       * @param systemWindowFlags The system window flags that this controller is interested in. +     * @param attributionSource The AttributionSource of the VirtualDevice owner application.       * @param allowedUsers The set of users that are allowed to stream in this display.       * @param activityLaunchAllowedByDefault Whether activities are default allowed to be launched       *   or blocked. @@ -169,6 +174,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController      public GenericWindowPolicyController(              int windowFlags,              int systemWindowFlags, +            AttributionSource attributionSource,              @NonNull ArraySet<UserHandle> allowedUsers,              boolean activityLaunchAllowedByDefault,              @NonNull Set<ComponentName> activityPolicyExemptions, @@ -184,6 +190,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController              boolean showTasksInHostDeviceRecents,              @Nullable ComponentName customHomeComponent) {          super(); +        mAttributionSource = attributionSource;          mAllowedUsers = allowedUsers;          mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;          mActivityPolicyExemptions = activityPolicyExemptions; @@ -436,6 +443,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController          if (!mIsMirrorDisplay && mActivityBlockedCallback != null) {              mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);          } +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            Counter.logIncrementWithUid( +                    "virtual_devices.value_activity_blocked_count", +                    mAttributionSource.getUid()); +        } +      }      private static boolean isAllowedByPolicy(boolean allowedByDefault, diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 1b49f18e60cf..9b72288955a7 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -21,6 +21,7 @@ import static android.text.TextUtils.formatSimple;  import android.annotation.IntDef;  import android.annotation.NonNull;  import android.annotation.StringDef; +import android.content.AttributionSource;  import android.graphics.PointF;  import android.hardware.display.DisplayManagerInternal;  import android.hardware.input.InputDeviceIdentifier; @@ -38,6 +39,7 @@ import android.os.IBinder;  import android.os.IInputConstants;  import android.os.RemoteException;  import android.util.ArrayMap; +import android.util.Log;  import android.util.Slog;  import android.view.Display;  import android.view.InputDevice; @@ -45,6 +47,7 @@ import android.view.WindowManager;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.expresslog.Counter;  import com.android.server.LocalServices;  import com.android.server.input.InputManagerInternal; @@ -98,11 +101,12 @@ class InputController {      private final DisplayManagerInternal mDisplayManagerInternal;      private final InputManagerInternal mInputManagerInternal;      private final WindowManager mWindowManager; +    private final AttributionSource mAttributionSource;      private final DeviceCreationThreadVerifier mThreadVerifier;      InputController(@NonNull Handler handler, -            @NonNull WindowManager windowManager) { -        this(new NativeWrapper(), handler, windowManager, +            @NonNull WindowManager windowManager, AttributionSource attributionSource) { +        this(new NativeWrapper(), handler, windowManager, attributionSource,                  // Verify that virtual devices are not created on the handler thread.                  () -> !handler.getLooper().isCurrentThread());      } @@ -110,12 +114,14 @@ class InputController {      @VisibleForTesting      InputController(@NonNull NativeWrapper nativeWrapper,              @NonNull Handler handler, @NonNull WindowManager windowManager, +            AttributionSource attributionSource,              @NonNull DeviceCreationThreadVerifier threadVerifier) {          mHandler = handler;          mNativeWrapper = nativeWrapper;          mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);          mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);          mWindowManager = windowManager; +        mAttributionSource = attributionSource;          mThreadVerifier = threadVerifier;      } @@ -838,6 +844,33 @@ class InputController {                      new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,                              deviceName, inputDeviceId));          } + +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            String metricId = getMetricIdForInputType(type); +            if (metricId != null) { +                Counter.logIncrementWithUid(metricId, mAttributionSource.getUid()); +            } +        } +    } + +    private static String getMetricIdForInputType(@InputDeviceDescriptor.Type int type) { +        switch (type) { +            case InputDeviceDescriptor.TYPE_KEYBOARD: +                return "virtual_devices.value_virtual_keyboard_created_count"; +            case InputDeviceDescriptor.TYPE_MOUSE: +                return "virtual_devices.value_virtual_mouse_created_count"; +            case InputDeviceDescriptor.TYPE_TOUCHSCREEN: +                return "virtual_devices.value_virtual_touchscreen_created_count"; +            case InputDeviceDescriptor.TYPE_DPAD: +                return "virtual_devices.value_virtual_dpad_created_count"; +            case InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD: +                return "virtual_devices.value_virtual_navigationtouchpad_created_count"; +            case InputDeviceDescriptor.TYPE_STYLUS: +                return "virtual_devices.value_virtual_stylus_created_count"; +            default: +                Log.e(TAG, "No metric known for input type: " + type); +                return null; +        }      }      @VisibleForTesting diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java index e9241dddcde9..cf4818060aa8 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -23,6 +23,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;  import android.companion.virtual.sensor.VirtualSensor;  import android.companion.virtual.sensor.VirtualSensorConfig;  import android.companion.virtual.sensor.VirtualSensorEvent; +import android.content.AttributionSource;  import android.hardware.SensorDirectChannel;  import android.os.Binder;  import android.os.IBinder; @@ -35,6 +36,7 @@ import android.util.SparseArray;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.expresslog.Counter;  import com.android.server.LocalServices;  import com.android.server.sensors.SensorManagerInternal; @@ -71,14 +73,18 @@ public class SensorController {      private List<VirtualSensor> mVirtualSensorList = null;      @NonNull +    private final AttributionSource mAttributionSource; +    @NonNull      private final SensorManagerInternal.RuntimeSensorCallback mRuntimeSensorCallback;      private final SensorManagerInternal mSensorManagerInternal;      private final VirtualDeviceManagerInternal mVdmInternal;      public SensorController(@NonNull IVirtualDevice virtualDevice, int virtualDeviceId, +            @NonNull AttributionSource attributionSource,              @Nullable IVirtualSensorCallback virtualSensorCallback,              @NonNull List<VirtualSensorConfig> sensors) {          mVirtualDeviceId = virtualDeviceId; +        mAttributionSource = attributionSource;          mRuntimeSensorCallback = new RuntimeSensorCallbackWrapper(virtualSensorCallback);          mSensorManagerInternal = LocalServices.getService(SensorManagerInternal.class);          mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class); @@ -139,6 +145,11 @@ public class SensorController {              mSensorDescriptors.put(sensorToken, sensorDescriptor);              mVirtualSensors.put(handle, sensor);          } +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            Counter.logIncrementWithUid( +                    "virtual_devices.value_virtual_sensors_created_count", +                    mAttributionSource.getUid()); +        }      }      boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index f13f49a5d378..6d731b21ac8a 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -104,6 +104,7 @@ import android.widget.Toast;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.app.BlockedAppStreamingActivity; +import com.android.modules.expresslog.Counter;  import com.android.server.LocalServices;  import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;  import com.android.server.companion.virtual.audio.VirtualAudioController; @@ -152,6 +153,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub      private final int mOwnerUid;      private final VirtualDeviceLog mVirtualDeviceLog;      private final String mOwnerPackageName; +    @NonNull +    private final AttributionSource mAttributionSource;      private final int mDeviceId;      @Nullable      private final String mPersistentDeviceId; @@ -288,6 +291,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub          super(PermissionEnforcer.fromContext(context));          mVirtualDeviceLog = virtualDeviceLog;          mOwnerPackageName = attributionSource.getPackageName(); +        mAttributionSource = attributionSource;          UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());          mContext = context.createContextAsUser(ownerUserHandle, 0);          mAssociationInfo = associationInfo; @@ -307,11 +311,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub          if (inputController == null) {              mInputController = new InputController(                      context.getMainThreadHandler(), -                    context.getSystemService(WindowManager.class)); +                    context.getSystemService(WindowManager.class), mAttributionSource);          } else {              mInputController = inputController;          } -        mSensorController = new SensorController(this, mDeviceId, +        mSensorController = new SensorController(this, mDeviceId, mAttributionSource,                  mParams.getVirtualSensorCallback(), mParams.getVirtualSensorConfigs());          mCameraAccessController = cameraAccessController;          if (mCameraAccessController != null) { @@ -620,7 +624,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub              }              if (mVirtualAudioController == null) { -                mVirtualAudioController = new VirtualAudioController(mContext); +                mVirtualAudioController = new VirtualAudioController(mContext, mAttributionSource);                  GenericWindowPolicyController gwpc = mVirtualDisplays.get(                          displayId).getWindowPolicyController();                  mVirtualAudioController.startListening(gwpc, routingCallback, @@ -1028,7 +1032,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub          if (mVirtualCameraController == null) {              throw new UnsupportedOperationException("Virtual camera controller is not available");          } -        mVirtualCameraController.registerCamera(cameraConfig); +        mVirtualCameraController.registerCamera(cameraConfig, mAttributionSource);      }      @Override // Binder call @@ -1110,6 +1114,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub          final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(                  FLAG_SECURE,                  SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, +                mAttributionSource,                  getAllowedUserHandles(),                  activityLaunchAllowedByDefault,                  mActivityPolicyExemptions, @@ -1179,6 +1184,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub              Binder.restoreCallingIdentity(token);          } +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            Counter.logIncrementWithUid( +                    "virtual_devices.value_virtual_display_created_count", +                    mAttributionSource.getUid()); +        }          return displayId;      } @@ -1220,6 +1230,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub          if ((display.getFlags() & FLAG_SECURE) == 0) {              showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,                      Toast.LENGTH_LONG, mContext.getMainLooper()); + +            if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +                Counter.logIncrementWithUid( +                        "virtual_devices.value_secure_window_blocked_count", +                        mAttributionSource.getUid()); +            }          }      } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 2168cb2043ed..9ad73cae08cd 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -482,6 +482,11 @@ public class VirtualDeviceManagerService extends SystemService {                      }                  });              } +            if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +                Counter.logIncrementWithUid( +                        "virtual_devices.value_virtual_devices_created_with_uid_count", +                        attributionSource.getUid()); +            }              return virtualDevice;          } diff --git a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java index c91877aad47e..4bffb76e1701 100644 --- a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java +++ b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java @@ -23,6 +23,7 @@ import android.annotation.Nullable;  import android.annotation.RequiresPermission;  import android.companion.virtual.audio.IAudioConfigChangedCallback;  import android.companion.virtual.audio.IAudioRoutingCallback; +import android.content.AttributionSource;  import android.content.Context;  import android.media.AudioManager;  import android.media.AudioPlaybackConfiguration; @@ -35,6 +36,7 @@ import android.util.Slog;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.expresslog.Counter;  import com.android.server.companion.virtual.GenericWindowPolicyController;  import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;  import com.android.server.companion.virtual.audio.AudioPlaybackDetector.AudioPlaybackCallback; @@ -70,10 +72,16 @@ public final class VirtualAudioController implements AudioPlaybackCallback,      @GuardedBy("mCallbackLock")      private IAudioConfigChangedCallback mConfigChangedCallback; -    public VirtualAudioController(Context context) { +    public VirtualAudioController(Context context, AttributionSource attributionSource) {          mContext = context;          mAudioPlaybackDetector = new AudioPlaybackDetector(context);          mAudioRecordingDetector = new AudioRecordingDetector(context); + +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            Counter.logIncrementWithUid( +                    "virtual_devices.value_virtual_audio_created_count", +                    attributionSource.getUid()); +        }      }      /** diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java index 2d82b5e7dd66..3bb1e33f83bb 100644 --- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java +++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java @@ -26,6 +26,7 @@ import android.companion.virtual.VirtualDeviceParams.DevicePolicy;  import android.companion.virtual.camera.VirtualCameraConfig;  import android.companion.virtualcamera.IVirtualCameraService;  import android.companion.virtualcamera.VirtualCameraConfiguration; +import android.content.AttributionSource;  import android.os.Binder;  import android.os.IBinder;  import android.os.RemoteException; @@ -35,6 +36,7 @@ import android.util.Slog;  import com.android.internal.annotations.GuardedBy;  import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.expresslog.Counter;  import java.io.PrintWriter;  import java.util.Map; @@ -76,7 +78,8 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {       *       * @param cameraConfig The {@link VirtualCameraConfig} sent by the client.       */ -    public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) { +    public void registerCamera(@NonNull VirtualCameraConfig cameraConfig, +            AttributionSource attributionSource) {          checkConfigByPolicy(cameraConfig);          connectVirtualCameraServiceIfNeeded(); @@ -97,6 +100,11 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {          } catch (RemoteException e) {              e.rethrowFromSystemServer();          } +        if (android.companion.virtualdevice.flags.Flags.metricsCollection()) { +            Counter.logIncrementWithUid( +                    "virtual_devices.value_virtual_camera_created_count", +                    attributionSource.getUid()); +        }      }      /** diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 05010f88efaf..f1776f4f9148 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -17,7 +17,7 @@  package com.android.server;  import static android.app.Flags.modesApi; -import static android.app.Flags.enableNightModeCache; +import static android.app.Flags.enableNightModeBinderCache;  import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;  import static android.app.UiModeManager.DEFAULT_PRIORITY;  import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF; @@ -148,7 +148,7 @@ final class UiModeManagerService extends SystemService {          @Override          public void set(int mode) {              mNightModeValue = mode; -            if (enableNightModeCache()) { +            if (enableNightModeBinderCache()) {                  UiModeManager.invalidateNightModeCache();              }          } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java index fab769e8bc4f..40e9198ea921 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java @@ -75,6 +75,6 @@ abstract class BrightnessClamper<T> {      protected enum Type {          THERMAL,          POWER, -        BEDTIME_MODE, +        WEAR_BEDTIME_MODE,      }  } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java index 2c02fc610a51..bc5fcb449c95 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -156,6 +156,8 @@ public class BrightnessClamperController {              return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;          } else if (mClamperType == Type.POWER) {              return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC; +        } else if (mClamperType == Type.WEAR_BEDTIME_MODE) { +            return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;          } else {              Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);              return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java index 7e853bfc4ad6..1902e35ed397 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java @@ -64,7 +64,7 @@ public class BrightnessWearBedtimeModeClamper extends      @NonNull      @Override      Type getType() { -        return Type.BEDTIME_MODE; +        return Type.WEAR_BEDTIME_MODE;      }      @Override diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java index 6a6e6ab23687..c2c82edee33d 100644 --- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java +++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java @@ -16,6 +16,7 @@  package com.android.server.grammaticalinflection; +import android.annotation.NonNull;  import android.annotation.Nullable;  import android.content.res.Configuration; @@ -55,11 +56,11 @@ public abstract class GrammaticalInflectionManagerInternal {       *       */      public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender( -            Configuration configuration); +            @NonNull Configuration configuration);      /**       * Whether the package can get the system grammatical gender or not.       */ -    public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName); +    public abstract boolean canGetSystemGrammaticalGender(int uid, @Nullable String packageName);  } diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java index 258e97d2dc71..0bcb26d7d0ab 100644 --- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java +++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java @@ -355,12 +355,11 @@ public class GrammaticalInflectionService extends SystemService {              final File file = getGrammaticalGenderFile(userId);              synchronized (mLock) {                  if (!file.exists()) { -                    Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file."); +                    Log.d(TAG, "User " + userId + " doesn't have the grammatical gender file.");                      return;                  }                  if (mGrammaticalGenderCache.indexOfKey(userId) < 0) { -                    try { -                        InputStream in = new FileInputStream(file); +                    try (FileInputStream in = new FileInputStream(file)) {                          final TypedXmlPullParser parser = Xml.resolvePullParser(in);                          mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));                      } catch (IOException | XmlPullParserException e) { diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java index 977dbff0b02e..84a59b4d28e4 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java @@ -117,6 +117,30 @@ final class IInputMethodClientInvoker {      }      @AnyThread +    void onStartInputResult(@NonNull InputBindResult res, int startInputSeq) { +        if (mIsProxy) { +            onStartInputResultInternal(res, startInputSeq); +        } else { +            mHandler.post(() -> onStartInputResultInternal(res, startInputSeq)); +        } +    } + +    @AnyThread +    private void onStartInputResultInternal(@NonNull InputBindResult res, int startInputSeq) { +        try { +            mTarget.onStartInputResult(res, startInputSeq); +        } catch (RemoteException e) { +            logRemoteException(e); +        } finally { +            // Dispose the channel if the input method is not local to this process +            // because the remote proxy will get its own copy when unparceled. +            if (res.channel != null && mIsProxy) { +                res.channel.dispose(); +            } +        } +    } + +    @AnyThread      void onBindAccessibilityService(@NonNull InputBindResult res, int id) {          if (mIsProxy) {              onBindAccessibilityServiceInternal(res, id); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index bc169ca40117..5574d1868925 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1539,7 +1539,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub          @Override          public void onStart() {              mService.publishLocalService(); -            publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/, +            IInputMethodManager.Stub service; +            if (Flags.useZeroJankProxy()) { +                service = new ZeroJankProxy(mService.mHandler::post, mService); +            } else { +                service = mService; +            } +            publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,                      DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);          } @@ -2216,6 +2222,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub          }      } +    @Nullable +    ClientState getClientState(IInputMethodClient client) { +        synchronized (ImfLock.class) { +            return mClientController.getClient(client.asBinder()); +        } +    } + +    // TODO(b/314150112): Move this to ClientController.      @GuardedBy("ImfLock.class")      void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {          if (mCurClient != null) { @@ -3741,6 +3755,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub          return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;      } +    //TODO(b/293640003): merge with startInputOrWindowGainedFocus once Flags.useZeroJankProxy() +    // is enabled. +    @Override +    public void startInputOrWindowGainedFocusAsync( +            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, +            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, +            int windowFlags, @Nullable EditorInfo editorInfo, +            IRemoteInputConnection inputConnection, +            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, +            int unverifiedTargetSdkVersion, @UserIdInt int userId, +            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) { +        // implemented by ZeroJankProxy +    } +      @NonNull      @Override      public InputBindResult startInputOrWindowGainedFocus( diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java new file mode 100644 index 000000000000..692fd7dcceae --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputmethod; + +import static com.android.server.inputmethod.InputMethodManagerService.TAG; + +import android.Manifest; +import android.annotation.BinderThread; +import android.annotation.EnforcePermission; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.util.ExceptionUtils; +import android.util.Slog; +import android.view.WindowManager; +import android.view.inputmethod.CursorAnchorInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ImeTracker; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.window.ImeOnBackInvokedDispatcher; + +import com.android.internal.inputmethod.DirectBootAwareness; +import com.android.internal.inputmethod.IConnectionlessHandwritingCallback; +import com.android.internal.inputmethod.IImeTracker; +import com.android.internal.inputmethod.IInputMethodClient; +import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; +import com.android.internal.inputmethod.IRemoteInputConnection; +import com.android.internal.inputmethod.InputBindResult; +import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.inputmethod.StartInputFlags; +import com.android.internal.inputmethod.StartInputReason; +import com.android.internal.util.FunctionalUtils.ThrowingRunnable; +import com.android.internal.view.IInputMethodManager; + +import java.io.FileDescriptor; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +/** + * A proxy that processes all {@link IInputMethodManager} calls asynchronously. + * @hide + */ +public class ZeroJankProxy extends IInputMethodManager.Stub { + +    private final IInputMethodManager mInner; +    private final Executor mExecutor; + +    ZeroJankProxy(Executor executor, IInputMethodManager inner) { +        mInner = inner; +        mExecutor = executor; +    } + +    private void offload(ThrowingRunnable r) { +        offloadInner(r); +    } + +    private void offload(Runnable r) { +        offloadInner(r); +    } + +    private void offloadInner(Runnable r) { +        boolean useThrowingRunnable = r instanceof ThrowingRunnable; +        final long identity = Binder.clearCallingIdentity(); +        try { +            mExecutor.execute(() -> { +                final long inner = Binder.clearCallingIdentity(); +                // Restoring calling identity, so we can still do permission checks on caller. +                Binder.restoreCallingIdentity(identity); +                try { +                    try { +                        if (useThrowingRunnable) { +                            ((ThrowingRunnable) r).runOrThrow(); +                        } else { +                            r.run(); +                        } +                    } catch (Exception e) { +                        Slog.e(TAG, "Error in async call", e); +                        throw ExceptionUtils.propagate(e); +                    } +                } finally { +                    Binder.restoreCallingIdentity(inner); +                } +            }); +        } finally { +            Binder.restoreCallingIdentity(identity); +        } +    } + +    @Override +    public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection, +            int selfReportedDisplayId) throws RemoteException { +        offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId)); +    } + +    @Override +    public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException { +        return mInner.getCurrentInputMethodInfoAsUser(userId); +    } + +    @Override +    public List<InputMethodInfo> getInputMethodList( +            int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException { +        return mInner.getInputMethodList(userId, directBootAwareness); +    } + +    @Override +    public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException { +        return mInner.getEnabledInputMethodList(userId); +    } + +    @Override +    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId, +            boolean allowsImplicitlyEnabledSubtypes, int userId) +            throws RemoteException { +        return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes, +                userId); +    } + +    @Override +    public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException { +        return mInner.getLastInputMethodSubtype(userId); +    } + +    @Override +    public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, +            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, +            int lastClickTooType, ResultReceiver resultReceiver, +            @SoftInputShowHideReason int reason) +            throws RemoteException { +        offload(() -> mInner.showSoftInput(client, windowToken, statsToken, flags, lastClickTooType, +                resultReceiver, reason)); +        return true; +    } + +    @Override +    public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, +            @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, +            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) +            throws RemoteException { +        offload(() -> mInner.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver, +                reason)); +        return true; +    } + +    @Override +    public void startInputOrWindowGainedFocusAsync( +            @StartInputReason int startInputReason, +            IInputMethodClient client, IBinder windowToken, +            @StartInputFlags int startInputFlags, +            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, +            int windowFlags, @Nullable EditorInfo editorInfo, +            IRemoteInputConnection inputConnection, +            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, +            int unverifiedTargetSdkVersion, @UserIdInt int userId, +            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) +            throws RemoteException { +        offload(() -> { +            InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client, +                    windowToken, startInputFlags, softInputMode, windowFlags, +                    editorInfo, +                    inputConnection, remoteAccessibilityInputConnection, +                    unverifiedTargetSdkVersion, +                    userId, imeDispatcher); +            sendOnStartInputResult(client, result, startInputSeq); +        }); +    } + +    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) +    @Override +    public InputBindResult startInputOrWindowGainedFocus( +            @StartInputReason int startInputReason, +            IInputMethodClient client, IBinder windowToken, +            @StartInputFlags int startInputFlags, +            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, +            int windowFlags, @Nullable EditorInfo editorInfo, +            IRemoteInputConnection inputConnection, +            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, +            int unverifiedTargetSdkVersion, @UserIdInt int userId, +            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) +            throws RemoteException { +        // Should never be called when flag is enabled i.e. when this proxy is used. +        return null; +    } + +    @Override +    public void showInputMethodPickerFromClient(IInputMethodClient client, +            int auxiliarySubtypeMode) +            throws RemoteException { +        offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode)); +    } + +    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS) +    @Override +    public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId) +            throws RemoteException { +        mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId); +    } + +    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD) +    @Override +    public boolean isInputMethodPickerShownForTest() throws RemoteException { +        super.isInputMethodPickerShownForTest_enforcePermission(); +        return mInner.isInputMethodPickerShownForTest(); +    } + +    @Override +    public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException { +        return mInner.getCurrentInputMethodSubtype(userId); +    } + +    @Override +    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes, +            @UserIdInt int userId) throws RemoteException { +        mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId); +    } + +    @Override +    public void setExplicitlyEnabledInputMethodSubtypes(String imeId, +            @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException { +        mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId); +    } + +    @Override +    public int getInputMethodWindowVisibleHeight(IInputMethodClient client) +            throws RemoteException { +        return mInner.getInputMethodWindowVisibleHeight(client); +    } + +    @Override +    public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) +            throws RemoteException { +        // Already async TODO(b/293640003): ordering issues? +        mInner.reportPerceptibleAsync(windowToken, perceptible); +    } + +    @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW) +    @Override +    public void removeImeSurface() throws RemoteException { +        mInner.removeImeSurface(); +    } + +    @Override +    public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException { +        mInner.removeImeSurfaceFromWindowAsync(windowToken); +    } + +    @Override +    public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException { +        mInner.startProtoDump(bytes, i, s); +    } + +    @Override +    public boolean isImeTraceEnabled() throws RemoteException { +        return mInner.isImeTraceEnabled(); +    } + +    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING) +    @Override +    public void startImeTrace() throws RemoteException { +        mInner.startImeTrace(); +    } + +    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING) +    @Override +    public void stopImeTrace() throws RemoteException { +        mInner.stopImeTrace(); +    } + +    @Override +    public void startStylusHandwriting(IInputMethodClient client) +            throws RemoteException { +        offload(() -> mInner.startStylusHandwriting(client)); +    } + +    @Override +    public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId, +            @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName, +            @Nullable String delegatorPackageName, +            @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException { +        offload(() -> mInner.startConnectionlessStylusHandwriting( +                client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName, +                callback)); +    } + +    @Override +    public boolean acceptStylusHandwritingDelegation( +            @NonNull IInputMethodClient client, +            @UserIdInt int userId, +            @NonNull String delegatePackageName, +            @NonNull String delegatorPackageName, +            @InputMethodManager.HandwritingDelegateFlags int flags) { +        try { +            return CompletableFuture.supplyAsync(() -> { +                try { +                    return mInner.acceptStylusHandwritingDelegation( +                            client, userId, delegatePackageName, delegatorPackageName, flags); +                } catch (RemoteException e) { +                    throw new RuntimeException(e); +                } +            }, this::offload).get(); +        } catch (InterruptedException e) { +            throw new RuntimeException(e); +        } catch (ExecutionException e) { +            throw new RuntimeException(e); +        } +    } + +    @Override +    public void prepareStylusHandwritingDelegation( +            @NonNull IInputMethodClient client, +            @UserIdInt int userId, +            @NonNull String delegatePackageName, +            @NonNull String delegatorPackageName) { +        offload(() -> mInner.prepareStylusHandwritingDelegation( +                client, userId, delegatePackageName, delegatorPackageName)); +    } + +    @Override +    public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless) +            throws RemoteException { +        return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless); +    } + +    @EnforcePermission("android.permission.TEST_INPUT_METHOD") +    @Override +    public void addVirtualStylusIdForTestSession(IInputMethodClient client) +            throws RemoteException { +        mInner.addVirtualStylusIdForTestSession(client); +    } + +    @EnforcePermission("android.permission.TEST_INPUT_METHOD") +    @Override +    public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout) +            throws RemoteException { +        mInner.setStylusWindowIdleTimeoutForTest(client, timeout); +    } + +    @Override +    public IImeTracker getImeTrackerService() throws RemoteException { +        return mInner.getImeTrackerService(); +    } + +    @BinderThread +    @Override +    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, +            @Nullable FileDescriptor err, +            @NonNull String[] args, @Nullable ShellCallback callback, +            @NonNull ResultReceiver resultReceiver) throws RemoteException { +        ((InputMethodManagerService) mInner).onShellCommand( +                in, out, err, args, callback, resultReceiver); +    } + +    private void sendOnStartInputResult( +            IInputMethodClient client, InputBindResult res, int startInputSeq) { +        InputMethodManagerService service = (InputMethodManagerService) mInner; +        final ClientState cs = service.getClientState(client); +        if (cs != null && cs.mClient != null) { +            cs.mClient.onStartInputResult(res, startInputSeq); +        } else { +            // client is unbound. +            Slog.i(TAG, "Client that requested startInputOrWindowGainedFocus is no longer" +                    + " bound. InputBindResult: " + res + " for startInputSeq: " + startInputSeq); +        } +    } +} + diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 3e8af27d2584..e7e8096a1965 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -312,14 +312,15 @@ public class MediaSessionService extends SystemService implements Monitor {                  }                  user.mPriorityStack.onSessionActiveStateChanged(record);              } -            boolean allowRunningInForeground = record.isActive() -                    && (playbackState == null || playbackState.isActive()); +            boolean isUserEngaged = +                    record.isActive() && (playbackState == null || playbackState.isActive());              Log.d(TAG, "onSessionActiveStateChanged: "                      + "record=" + record                      + "playbackState=" + playbackState -                    + "allowRunningInForeground=" + allowRunningInForeground); -            setForegroundServiceAllowance(record, allowRunningInForeground); +                    + "allowRunningInForeground=" + isUserEngaged); +            setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged); +            reportMediaInteractionEvent(record, isUserEngaged);              mHandler.postSessionsChanged(record);          }      } @@ -417,12 +418,14 @@ public class MediaSessionService extends SystemService implements Monitor {              }              user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);              if (playbackState != null) { -                boolean allowRunningInForeground = playbackState.isActive() && record.isActive(); +                boolean isUserEngaged = playbackState.isActive() && record.isActive();                  Log.d(TAG, "onSessionPlaybackStateChanged: "                          + "record=" + record                          + "playbackState=" + playbackState -                        + "allowRunningInForeground=" + allowRunningInForeground); -                setForegroundServiceAllowance(record, allowRunningInForeground); +                        + "allowRunningInForeground=" + isUserEngaged); +                setForegroundServiceAllowance( +                        record, /* allowRunningInForeground= */ isUserEngaged); +                reportMediaInteractionEvent(record, isUserEngaged);              }          }      } @@ -590,6 +593,7 @@ public class MediaSessionService extends SystemService implements Monitor {          Log.d(TAG, "destroySessionLocked: record=" + session);          setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false); +        reportMediaInteractionEvent(session, /* userEngaged= */ false);          mHandler.postSessionsChanged(session);      } @@ -608,11 +612,9 @@ public class MediaSessionService extends SystemService implements Monitor {          if (allowRunningInForeground) {              mActivityManagerInternal.startForegroundServiceDelegate(                      foregroundServiceDelegationOptions, /* connection= */ null); -            reportMediaInteractionEvent(record, /* userEngaged= */ true);          } else {              mActivityManagerInternal.stopForegroundServiceDelegate(                      foregroundServiceDelegationOptions); -            reportMediaInteractionEvent(record, /* userEngaged= */ false);          }      } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 53ae60b0cc76..7455fe0043f8 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5720,6 +5720,14 @@ public class NotificationManagerService extends SystemService {          }          @Override +        @Condition.State +        public int getAutomaticZenRuleState(@NonNull String id) { +            Objects.requireNonNull(id, "id is null"); +            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState"); +            return mZenModeHelper.getAutomaticZenRuleState(id); +        } + +        @Override          public void setAutomaticZenRuleState(String id, Condition condition) {              Objects.requireNonNull(id, "id is null");              Objects.requireNonNull(condition, "Condition is null"); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 54de1976edb6..efb8c84d788a 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -856,6 +856,20 @@ public class ZenModeHelper {          }      } +    @Condition.State +    int getAutomaticZenRuleState(String id) { +        synchronized (mConfigLock) { +            if (mConfig == null) { +                return Condition.STATE_UNKNOWN; +            } +            ZenRule rule = mConfig.automaticRules.get(id); +            if (rule == null || !canManageAutomaticZenRule(rule)) { +                return Condition.STATE_UNKNOWN; +            } +            return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE; +        } +    } +      void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,              int callingUid) {          requirePublicOrigin("setAutomaticZenRuleState", origin); diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java new file mode 100644 index 000000000000..a4c4347b7cef --- /dev/null +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.ondeviceintelligence; + +import android.Manifest; +import android.annotation.NonNull; +import android.app.AppGlobals; +import android.app.ondeviceintelligence.Content; +import android.app.ondeviceintelligence.DownloadCallback; +import android.app.ondeviceintelligence.Feature; +import android.app.ondeviceintelligence.IDownloadCallback; +import android.app.ondeviceintelligence.IFeatureCallback; +import android.app.ondeviceintelligence.IFeatureDetailsCallback; +import android.app.ondeviceintelligence.IListFeaturesCallback; +import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager; +import android.app.ondeviceintelligence.IProcessingSignal; +import android.app.ondeviceintelligence.IResponseCallback; +import android.app.ondeviceintelligence.IStreamingResponseCallback; +import android.app.ondeviceintelligence.ITokenCountCallback; +import android.app.ondeviceintelligence.OnDeviceIntelligenceManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.ICancellationSignal; +import android.os.ParcelFileDescriptor; +import android.os.PersistableBundle; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService; +import android.service.ondeviceintelligence.IRemoteStorageService; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.AndroidFuture; +import com.android.internal.infra.ServiceConnector; +import com.android.internal.os.BackgroundThread; +import com.android.server.SystemService; + +import java.util.Objects; +import java.util.Set; + +/** + * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This + * service holds connection references to the underlying remote services i.e. the isolated service + * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular + * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}. + * + * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of + * the Inference service for each user, due to possible high memory footprint. + * + * @hide + */ +public class OnDeviceIntelligenceManagerService extends SystemService { + +    private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName(); +    private static final String KEY_SERVICE_ENABLED = "service_enabled"; + +    /** Default value in absence of {@link DeviceConfig} override. */ +    private static final boolean DEFAULT_SERVICE_ENABLED = true; +    private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence"; + +    private final Context mContext; +    protected final Object mLock = new Object(); + + +    private RemoteOnDeviceTrustedInferenceService mRemoteInferenceService; +    private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService; +    volatile boolean mIsServiceEnabled; + +    public OnDeviceIntelligenceManagerService(Context context) { +        super(context); +        mContext = context; +    } + +    @Override +    public void onStart() { +        publishBinderService( +                Context.ON_DEVICE_INTELLIGENCE_SERVICE, new OnDeviceIntelligenceManagerInternal(), +                /* allowIsolated = */true); +    } + + +    @Override +    public void onBootPhase(int phase) { +        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { +            DeviceConfig.addOnPropertiesChangedListener( +                    NAMESPACE_ON_DEVICE_INTELLIGENCE, +                    BackgroundThread.getExecutor(), +                    (properties) -> onDeviceConfigChange(properties.getKeyset())); + +            mIsServiceEnabled = isServiceEnabled(); +        } +    } + +    private void onDeviceConfigChange(@NonNull Set<String> keys) { +        if (keys.contains(KEY_SERVICE_ENABLED)) { +            mIsServiceEnabled = isServiceEnabled(); +        } +    } + +    private boolean isServiceEnabled() { +        return DeviceConfig.getBoolean( +                NAMESPACE_ON_DEVICE_INTELLIGENCE, +                KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); +    } + +    private final class OnDeviceIntelligenceManagerInternal extends +            IOnDeviceIntelligenceManager.Stub { +        @Override +        public void getVersion(RemoteCallback remoteCallback) throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion"); +            Objects.requireNonNull(remoteCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                remoteCallback.sendResult(null); +                return; +            } +            ensureRemoteIntelligenceServiceInitialized(); +            mRemoteOnDeviceIntelligenceService.post( +                    service -> service.getVersion(remoteCallback)); +        } + +        @Override +        public void getFeature(int id, IFeatureCallback featureCallback) throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures"); +            Objects.requireNonNull(featureCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                featureCallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +                return; +            } +            ensureRemoteIntelligenceServiceInitialized(); +            mRemoteOnDeviceIntelligenceService.post( +                    service -> service.getFeature(id, featureCallback)); +        } + +        @Override +        public void listFeatures(IListFeaturesCallback listFeaturesCallback) +                throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures"); +            Objects.requireNonNull(listFeaturesCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                listFeaturesCallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +                return; +            } +            ensureRemoteIntelligenceServiceInitialized(); +            mRemoteOnDeviceIntelligenceService.post( +                    service -> service.listFeatures(listFeaturesCallback)); +        } + +        @Override +        public void getFeatureDetails(Feature feature, +                IFeatureDetailsCallback featureDetailsCallback) +                throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus"); +            Objects.requireNonNull(feature); +            Objects.requireNonNull(featureDetailsCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                featureDetailsCallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +                return; +            } +            ensureRemoteIntelligenceServiceInitialized(); +            mRemoteOnDeviceIntelligenceService.post( +                    service -> service.getFeatureDetails(feature, featureDetailsCallback)); +        } + +        @Override +        public void requestFeatureDownload(Feature feature, ICancellationSignal cancellationSignal, +                IDownloadCallback downloadCallback) throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload"); +            Objects.requireNonNull(feature); +            Objects.requireNonNull(downloadCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                downloadCallback.onDownloadFailed( +                        DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +            } +            ensureRemoteIntelligenceServiceInitialized(); +            mRemoteOnDeviceIntelligenceService.post( +                    service -> service.requestFeatureDownload(feature, cancellationSignal, +                            downloadCallback)); +        } + + +        @Override +        public void requestTokenCount(Feature feature, +                Content request, ICancellationSignal cancellationSignal, +                ITokenCountCallback tokenCountcallback) throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal prepareFeatureProcessing"); +            Objects.requireNonNull(feature); +            Objects.requireNonNull(request); +            Objects.requireNonNull(tokenCountcallback); + +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                tokenCountcallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +            } +            ensureRemoteTrustedInferenceServiceInitialized(); +            mRemoteInferenceService.post( +                    service -> service.requestTokenCount(feature, request, cancellationSignal, +                            tokenCountcallback)); +        } + +        @Override +        public void processRequest(Feature feature, +                Content request, +                int requestType, +                ICancellationSignal cancellationSignal, +                IProcessingSignal processingSignal, +                IResponseCallback responseCallback) +                throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequest"); +            Objects.requireNonNull(feature); +            Objects.requireNonNull(responseCallback); +            Objects.requireNonNull(request); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                responseCallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +            } +            ensureRemoteTrustedInferenceServiceInitialized(); +            mRemoteInferenceService.post( +                    service -> service.processRequest(feature, request, requestType, +                            cancellationSignal, processingSignal, +                            responseCallback)); +        } + +        @Override +        public void processRequestStreaming(Feature feature, +                Content request, +                int requestType, +                ICancellationSignal cancellationSignal, +                IProcessingSignal processingSignal, +                IStreamingResponseCallback streamingCallback) throws RemoteException { +            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequestStreaming"); +            Objects.requireNonNull(feature); +            Objects.requireNonNull(request); +            Objects.requireNonNull(streamingCallback); +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); +            if (!mIsServiceEnabled) { +                Slog.w(TAG, "Service not available"); +                streamingCallback.onFailure( +                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE, +                        "OnDeviceIntelligenceManagerService is unavailable", +                        new PersistableBundle()); +            } +            ensureRemoteTrustedInferenceServiceInitialized(); +            mRemoteInferenceService.post( +                    service -> service.processRequestStreaming(feature, request, requestType, +                            cancellationSignal, processingSignal, +                            streamingCallback)); +        } +    } + +    private void ensureRemoteIntelligenceServiceInitialized() throws RemoteException { +        synchronized (mLock) { +            if (mRemoteOnDeviceIntelligenceService == null) { +                String serviceName = mContext.getResources().getString( +                        R.string.config_defaultOnDeviceIntelligenceService); +                validateService(serviceName, false); +                mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext, +                        ComponentName.unflattenFromString(serviceName), +                        UserHandle.SYSTEM.getIdentifier()); +            } +        } +    } + +    private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException { +        synchronized (mLock) { +            if (mRemoteInferenceService == null) { +                String serviceName = mContext.getResources().getString( +                        R.string.config_defaultOnDeviceTrustedInferenceService); +                validateService(serviceName, true); +                mRemoteInferenceService = new RemoteOnDeviceTrustedInferenceService(mContext, +                        ComponentName.unflattenFromString(serviceName), +                        UserHandle.SYSTEM.getIdentifier()); +                mRemoteInferenceService.setServiceLifecycleCallbacks( +                        new ServiceConnector.ServiceLifecycleCallbacks<>() { +                            @Override +                            public void onConnected( +                                    @NonNull IOnDeviceTrustedInferenceService service) { +                                try { +                                    service.registerRemoteStorageService( +                                            getIRemoteStorageService()); +                                } catch (RemoteException ex) { +                                    Slog.w(TAG, "Failed to send connected event", ex); +                                } +                            } +                        }); +            } +        } +    } + +    @NonNull +    private IRemoteStorageService.Stub getIRemoteStorageService() { +        return new IRemoteStorageService.Stub() { +            @Override +            public void getReadOnlyFileDescriptor( +                    String filePath, +                    AndroidFuture<ParcelFileDescriptor> future) { +                mRemoteOnDeviceIntelligenceService.post( +                        service -> service.getReadOnlyFileDescriptor( +                                filePath, future)); +            } + +            @Override +            public void getReadOnlyFeatureFileDescriptorMap( +                    Feature feature, +                    RemoteCallback remoteCallback) +                    throws RemoteException { +                mRemoteOnDeviceIntelligenceService.post( +                        service -> service.getReadOnlyFeatureFileDescriptorMap( +                                feature, remoteCallback)); +            } +        }; +    } + +    @GuardedBy("mLock") +    private void validateService(String serviceName, boolean checkIsolated) +            throws RemoteException { +        if (TextUtils.isEmpty(serviceName)) { +            throw new RuntimeException(""); +        } +        ComponentName serviceComponent = ComponentName.unflattenFromString( +                serviceName); +        ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo( +                serviceComponent, +                PackageManager.MATCH_DIRECT_BOOT_AWARE +                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0); +        if (serviceInfo != null) { +            if (!checkIsolated) { +                checkServiceRequiresPermission(serviceInfo, +                        Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE); +                return; +            } + +            checkServiceRequiresPermission(serviceInfo, +                    Manifest.permission.BIND_ON_DEVICE_TRUSTED_SERVICE); +            if (!isIsolatedService(serviceInfo)) { +                throw new SecurityException( +                        "Call required an isolated service, but the configured service: " +                                + serviceName + ", is not isolated"); +            } +        } else { +            throw new RuntimeException( +                    "Could not find service info for serviceName: " + serviceName); +        } +    } + +    private static void checkServiceRequiresPermission(ServiceInfo serviceInfo, +            String requiredPermission) { +        final String permission = serviceInfo.permission; +        if (!requiredPermission.equals(permission)) { +            throw new SecurityException(String.format( +                    "Service %s requires %s permission. Found %s permission", +                    serviceInfo.getComponentName(), +                    requiredPermission, +                    serviceInfo.permission)); +        } +    } + +    @GuardedBy("mLock") +    private boolean isIsolatedService(@NonNull ServiceInfo serviceInfo) { +        return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 +                && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0; +    } +} diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java new file mode 100644 index 000000000000..48258d7bea72 --- /dev/null +++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.ondeviceintelligence; + +import static android.content.Context.BIND_FOREGROUND_SERVICE; +import static android.content.Context.BIND_INCLUDE_CAPABILITIES; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.service.ondeviceintelligence.IOnDeviceIntelligenceService; +import android.service.ondeviceintelligence.OnDeviceIntelligenceService; + +import com.android.internal.infra.ServiceConnector; + +/** + * Manages the connection to the remote on-device intelligence service. Also, handles unbinding + * logic set by the service implementation via a Secure Settings flag. + */ +public class RemoteOnDeviceIntelligenceService extends +        ServiceConnector.Impl<IOnDeviceIntelligenceService> { +    private static final String TAG = +            RemoteOnDeviceIntelligenceService.class.getSimpleName(); + +    RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName, +            int userId) { +        super(context, new Intent( +                        OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName), +                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId, +                IOnDeviceIntelligenceService.Stub::asInterface); + +        // Bind right away +        connect(); +    } + +    @Override +    protected long getAutoDisconnectTimeoutMs() { +        // Disable automatic unbinding. +        // TODO: add logic to fetch this flag via SecureSettings. +        return -1; +    } +} diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java new file mode 100644 index 000000000000..cc8e78804bb6 --- /dev/null +++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.ondeviceintelligence; + +import static android.content.Context.BIND_FOREGROUND_SERVICE; +import static android.content.Context.BIND_INCLUDE_CAPABILITIES; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService; +import android.service.ondeviceintelligence.OnDeviceTrustedInferenceService; + +import com.android.internal.infra.ServiceConnector; + + +/** + * Manages the connection to the remote on-device trusted inference service. Also, handles unbinding + * logic set by the service implementation via a SecureSettings flag. + */ +public class RemoteOnDeviceTrustedInferenceService extends +        ServiceConnector.Impl<IOnDeviceTrustedInferenceService> { +    /** +     * Creates an instance of {@link ServiceConnector} +     * +     * See {@code protected} methods for optional parameters you can override. +     * +     * @param context to be used for {@link Context#bindServiceAsUser binding} and +     *                {@link Context#unbindService unbinding} +     * @param userId  to be used for {@link Context#bindServiceAsUser binding} +     */ +    RemoteOnDeviceTrustedInferenceService(Context context, ComponentName serviceName, +            int userId) { +        super(context, new Intent( +                        OnDeviceTrustedInferenceService.SERVICE_INTERFACE).setComponent(serviceName), +                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId, +                IOnDeviceTrustedInferenceService.Stub::asInterface); + +        // Bind right away +        connect(); +    } + + +    @Override +    protected long getAutoDisconnectTimeoutMs() { +        // Disable automatic unbinding. +        // TODO: add logic to fetch this flag via SecureSettings. +        return -1; +    } +} diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 524bad58ce07..b6daed121057 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;  import android.content.pm.IBackgroundInstallControlService;  import android.content.pm.InstallSourceInfo;  import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller;  import android.content.pm.PackageManager;  import android.content.pm.PackageManagerInternal;  import android.content.pm.ParceledListSlice; @@ -46,6 +47,7 @@ import android.os.UserHandle;  import android.text.TextUtils;  import android.util.ArraySet;  import android.util.AtomicFile; +import android.util.Log;  import android.util.Slog;  import android.util.SparseArrayMap;  import android.util.SparseSetArray; @@ -63,8 +65,10 @@ import java.io.FileInputStream;  import java.io.FileOutputStream;  import java.io.IOException;  import java.util.ArrayList; +import java.util.Comparator;  import java.util.List;  import java.util.ListIterator; +import java.util.Optional;  import java.util.Set;  import java.util.TreeSet; @@ -103,6 +107,24 @@ public class BackgroundInstallControlService extends SystemService {      private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>              mInstallerForegroundTimeFrames = new SparseArrayMap<>(); +    @VisibleForTesting +    protected final PackageManagerInternal.PackageListObserver mPackageObserver = +            new PackageManagerInternal.PackageListObserver() { +                @Override +                public void onPackageAdded(String packageName, int uid) { +                    final int userId = UserHandle.getUserId(uid); +                    mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName) +                            .sendToTarget(); +                } + +                @Override +                public void onPackageRemoved(String packageName, int uid) { +                    final int userId = UserHandle.getUserId(uid); +                    mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName) +                            .sendToTarget(); +                } +            }; +      public BackgroundInstallControlService(@NonNull Context context) {          this(new InjectorImpl(context));      } @@ -258,6 +280,7 @@ public class BackgroundInstallControlService extends SystemService {          String installerPackageName;          String initiatingPackageName; +          try {              final InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(packageName);              installerPackageName = installInfo.getInstallingPackageName(); @@ -280,7 +303,8 @@ public class BackgroundInstallControlService extends SystemService {          // convert up-time to current time.          final long installTimestamp = -                System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp); +                System.currentTimeMillis() - (SystemClock.uptimeMillis() +                        - retrieveInstallStartTimestamp(packageName, userId, appInfo));          if (installedByAdb(initiatingPackageName)                  || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) { @@ -293,6 +317,35 @@ public class BackgroundInstallControlService extends SystemService {          writeBackgroundInstalledPackagesToDisk();      } +    private long retrieveInstallStartTimestamp(String packageName, +                                               int userId, ApplicationInfo appInfo) { +        long installStartTimestamp = appInfo.createTimestamp; + +        try { +            Optional<PackageInstaller.SessionInfo> latestInstallSession = +                    getLatestInstallSession(packageName, userId); +            if (latestInstallSession.isEmpty()) { +                Slog.w(TAG, "Package's historical install session not found, falling back " +                        + "to appInfo.createTimestamp: " + packageName); +            } else { +                installStartTimestamp = latestInstallSession.get().getCreatedMillis(); +            } +        } catch (Exception e) { +            Slog.w(TAG, "Retrieval of install time from historical session failed, falling " +                    + "back to appInfo.createTimestamp"); +            Slog.w(TAG, Log.getStackTraceString(e)); +        } +        return installStartTimestamp; +    } + +    private Optional<PackageInstaller.SessionInfo> getLatestInstallSession( +            String packageName, int userId) { +        List<PackageInstaller.SessionInfo> historicalSessions = +                mPackageManagerInternal.getHistoricalSessions(userId).getList(); +        return historicalSessions.stream().filter(s -> packageName.equals(s.getAppPackageName())) +                .max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis)); +    } +      // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be      // addressed with b/265203007      private boolean installedByAdb(String initiatingPackageName) { @@ -496,22 +549,7 @@ public class BackgroundInstallControlService extends SystemService {              publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);          } -        mPackageManagerInternal.getPackageList( -                new PackageManagerInternal.PackageListObserver() { -                    @Override -                    public void onPackageAdded(String packageName, int uid) { -                        final int userId = UserHandle.getUserId(uid); -                        mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName) -                                .sendToTarget(); -                    } - -                    @Override -                    public void onPackageRemoved(String packageName, int uid) { -                        final int userId = UserHandle.getUserId(uid); -                        mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName) -                                .sendToTarget(); -                    } -                }); +        mPackageManagerInternal.getPackageList(mPackageObserver);      }      // The foreground time frame (ForegroundTimeFrame) represents the period diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index 05e8f9aa24ec..df4e5a319ee6 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -354,21 +354,18 @@ public class PackageArchiver {                  ps.setArchiveState(/* archiveState= */ null, userId);              }          } -        mPm.mBackgroundHandler.post( -                () -> { -                    File iconsDir = getIconsDir(packageName, userId); -                    if (!iconsDir.exists()) { -                        return; -                    } -                    // TODO(b/319238030) Move this into installd. -                    if (!FileUtils.deleteContentsAndDir(iconsDir)) { -                        Slog.e(TAG, "Failed to clean up archive files for " + packageName); -                    } else { -                        if (DEBUG) { -                            Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath()); -                        } -                    } -                }); +        File iconsDir = getIconsDir(packageName, userId); +        if (!iconsDir.exists()) { +            return; +        } +        // TODO(b/319238030) Move this into installd. +        if (!FileUtils.deleteContentsAndDir(iconsDir)) { +            Slog.e(TAG, "Failed to clean up archive files for " + packageName); +        } else { +            if (DEBUG) { +                Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath()); +            } +        }      }      @Nullable diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java index 6e1e979b1bac..7b397755173d 100644 --- a/services/core/java/com/android/server/search/Searchables.java +++ b/services/core/java/com/android/server/search/Searchables.java @@ -147,6 +147,9 @@ public class Searchables {              Log.e(LOG_TAG, "Error getting activity info " + re);              return null;          } +        if (ai == null) { +            return null; +        }          String refActivityName = null;          // First look for activity-specific reference diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index a4c6959f8ad5..3c6baa873eca 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -255,4 +255,9 @@ public interface StatusBarManagerInternal {       * @param tile the ComponentName of the {@link android.service.quicksettings.TileService}       */      void removeQsTile(ComponentName tile); + +    /** +     * Called when requested to enter desktop from an app. +     */ +    void enterDesktop(int displayId);  } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index fd316eaf9b96..14c38bde6621 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -830,6 +830,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D          }          @Override +        public void enterDesktop(int displayId) { +            IStatusBar bar = mBar; +            if (bar != null) { +                try { +                    bar.enterDesktop(displayId); +                } catch (RemoteException ex) { } +            } +        } +        @Override          public void showMediaOutputSwitcher(String packageName) {              IStatusBar bar = mBar;              if (bar != null) { diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index fab0430cc40d..99ce3e2fb740 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -31,6 +31,7 @@ import static android.os.VibrationAttributes.USAGE_UNKNOWN;  import android.annotation.NonNull;  import android.annotation.Nullable;  import android.app.ActivityManager; +import android.app.SynchronousUserSwitchObserver;  import android.app.UidObserver;  import android.content.BroadcastReceiver;  import android.content.Context; @@ -145,8 +146,6 @@ final class VibrationSettings {                      PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,                      PowerManager.GO_TO_SLEEP_REASON_TIMEOUT)); -    private static final IntentFilter USER_SWITCHED_INTENT_FILTER = -            new IntentFilter(Intent.ACTION_USER_SWITCHED);      private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =              new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); @@ -162,9 +161,11 @@ final class VibrationSettings {      @VisibleForTesting      final SettingsContentObserver mSettingObserver;      @VisibleForTesting -    final MyUidObserver mUidObserver; -    @VisibleForTesting      final SettingsBroadcastReceiver mSettingChangeReceiver; +    @VisibleForTesting +    final VibrationUidObserver mUidObserver; +    @VisibleForTesting +    final VibrationUserSwitchObserver mUserSwitchObserver;      @GuardedBy("mLock")      private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); @@ -205,8 +206,9 @@ final class VibrationSettings {          mContext = context;          mVibrationConfig = config;          mSettingObserver = new SettingsContentObserver(handler); -        mUidObserver = new MyUidObserver();          mSettingChangeReceiver = new SettingsBroadcastReceiver(); +        mUidObserver = new VibrationUidObserver(); +        mUserSwitchObserver = new VibrationUserSwitchObserver();          mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)                  .getSystemUiServiceComponent().getPackageName(); @@ -245,7 +247,13 @@ final class VibrationSettings {          try {              ActivityManager.getService().registerUidObserver(mUidObserver,                      ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, -                    ActivityManager.PROCESS_STATE_UNKNOWN, null); +                    ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null); +        } catch (RemoteException e) { +            // ignored; both services live in system_server +        } + +        try { +            ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);          } catch (RemoteException e) {              // ignored; both services live in system_server          } @@ -270,7 +278,6 @@ final class VibrationSettings {                      }                  }); -        registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);          registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);          // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity. @@ -540,41 +547,44 @@ final class VibrationSettings {      /** Update all cached settings and triggers registered listeners. */      void update() { -        updateSettings(); +        updateSettings(UserHandle.USER_CURRENT);          updateRingerMode();          notifyListeners();      } -    private void updateSettings() { +    private void updateSettings(int userHandle) {          synchronized (mLock) { -            mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; -            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0; +            mVibrateInputDevices = +                    loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0; +            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;              mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, -                    mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0; +                    mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0, userHandle) > 0;              int alarmIntensity = toIntensity( -                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1), +                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),                      getDefaultIntensity(USAGE_ALARM));              int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);              int hapticFeedbackIntensity = toIntensity( -                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1), +                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1, userHandle),                      defaultHapticFeedbackIntensity);              int positiveHapticFeedbackIntensity = toPositiveIntensity(                      hapticFeedbackIntensity, defaultHapticFeedbackIntensity);              int hardwareFeedbackIntensity = toIntensity( -                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1), +                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1, +                            userHandle),                      positiveHapticFeedbackIntensity);              int mediaIntensity = toIntensity( -                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1), +                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1, userHandle),                      getDefaultIntensity(USAGE_MEDIA));              int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);              int notificationIntensity = toIntensity( -                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1), +                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1, +                            userHandle),                      defaultNotificationIntensity);              int positiveNotificationIntensity = toPositiveIntensity(                      notificationIntensity, defaultNotificationIntensity);              int ringIntensity = toIntensity( -                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1), +                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1, userHandle),                      getDefaultIntensity(USAGE_RINGTONE));              mCurrentVibrationIntensities.clear(); @@ -593,7 +603,7 @@ final class VibrationSettings {              mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);              mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity); -            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) { +            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, userHandle)) {                  // Make sure deprecated boolean setting still disables touch vibrations.                  mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);              } else { @@ -744,14 +754,13 @@ final class VibrationSettings {          return value;      } -    private boolean loadBooleanSetting(String settingKey) { -        return Settings.System.getIntForUser(mContext.getContentResolver(), -                settingKey, 0, UserHandle.USER_CURRENT) != 0; +    private boolean loadBooleanSetting(String settingKey, int userHandle) { +        return loadSystemSetting(settingKey, 0, userHandle) != 0;      } -    private int loadSystemSetting(String settingName, int defaultValue) { +    private int loadSystemSetting(String settingName, int defaultValue, int userHandle) {          return Settings.System.getIntForUser(mContext.getContentResolver(), -                settingName, defaultValue, UserHandle.USER_CURRENT); +                settingName, defaultValue, userHandle);      }      private void registerSettingsObserver(Uri settingUri) { @@ -828,24 +837,18 @@ final class VibrationSettings {          @Override          public void onChange(boolean selfChange) { -            updateSettings(); +            updateSettings(UserHandle.USER_CURRENT);              notifyListeners();          }      } -    /** -     * Implementation of {@link BroadcastReceiver} to update settings on current user or ringer -     * mode change. -     */ +    /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */      @VisibleForTesting      final class SettingsBroadcastReceiver extends BroadcastReceiver {          @Override          public void onReceive(Context context, Intent intent) {              String action = intent.getAction(); -            if (Intent.ACTION_USER_SWITCHED.equals(action)) { -                // Reload all settings, as they are user-based. -                update(); -            } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { +            if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {                  updateRingerMode();                  notifyListeners();              } @@ -854,7 +857,7 @@ final class VibrationSettings {      /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */      @VisibleForTesting -    final class MyUidObserver extends UidObserver { +    final class VibrationUidObserver extends UidObserver {          private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();          public boolean isUidForeground(int uid) { @@ -878,4 +881,23 @@ final class VibrationSettings {              }          }      } + +    /** Implementation of {@link SynchronousUserSwitchObserver} to update on user switch. */ +    @VisibleForTesting +    final class VibrationUserSwitchObserver extends SynchronousUserSwitchObserver { + +        @Override +        public void onUserSwitching(int newUserId) { +            // Reload settings early based on new user id. +            updateSettings(newUserId); +            notifyListeners(); +        } + +        @Override +        public void onUserSwitchComplete(int newUserId) { +            // Reload all settings including ones from AudioManager, +            // as they are based on UserHandle.USER_CURRENT. +            update(); +        } +    }  } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 759450b528b9..5d172a9feef3 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -2220,9 +2220,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {              // only cancel background vibrations.              IBinder deathBinder = commonOptions.background ? VibratorManagerService.this                      : mShellCallbacksToken; -            HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(), -                    Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs, -                    commonOptions.description, deathBinder); +            int uid = Binder.getCallingUid(); +            // Resolve the package name for the client based on the process UID, to cover cases like +            // rooted shell clients using ROOT_UID. +            String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME); +            HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT, +                    resolvedPackageName, combined, attrs, commonOptions.description, deathBinder);              maybeWaitOnVibration(vib, commonOptions);          } diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java index 00c3026b1194..d05482d75083 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java +++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java @@ -55,6 +55,7 @@ import com.android.server.pm.KnownPackages;  import com.android.server.utils.quota.MultiRateLimiter;  import java.io.FileDescriptor; +import java.time.Duration;  import java.util.HashSet;  import java.util.Objects;  import java.util.Set; @@ -106,7 +107,7 @@ public class WearableSensingManagerService extends      private final Context mContext;      private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1);      private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>(); -    private final MultiRateLimiter mDataRequestRateLimiter; +    @NonNull private volatile MultiRateLimiter mDataRequestRateLimiter;      volatile boolean mIsServiceEnabled;      public WearableSensingManagerService(Context context) { @@ -238,6 +239,57 @@ public class WearableSensingManagerService extends          }      } +    /** +     * Sets the window size used in data request rate limiting. +     * +     * <p>The new value will not be reflected in {@link +     * WearableSensingDataRequest#getRateLimitWindowSize()}. +     * +     * <p>{@code windowSize} will be automatically capped between +     * com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS and +     * com.android.server.utils.quota.QuotaTracker#MAX_WINDOW_SIZE_MS +     * +     * <p>The current rate limit will also be reset. +     * +     * <p>This method is only used for testing and must not be called in production code because +     * it effectively bypasses the rate limiting introduced to enhance privacy protection. +     */ +    @VisibleForTesting +    void setDataRequestRateLimitWindowSize(@NonNull Duration windowSize) { +        Slog.w( +                TAG, +                TextUtils.formatSimple( +                        "Setting the data request rate limit window size to %s. This also resets" +                            + " the current limit and should only be callable from a test.", +                        windowSize)); +        mDataRequestRateLimiter = +                new MultiRateLimiter.Builder(mContext) +                        .addRateLimit(WearableSensingDataRequest.getRateLimit(), windowSize) +                        .build(); +    } + +    /** +     * Resets the window size used in data request rate limiting back to the default value. +     * +     * <p>The current rate limit will also be reset. +     * +     * <p>This method is only used for testing and must not be called in production code because +     * it effectively bypasses the rate limiting introduced to enhance privacy protection. +     */ +    @VisibleForTesting +    void resetDataRequestRateLimitWindowSize() { +        Slog.w( +                TAG, +                "Resetting the data request rate limit window size back to the default value. This" +                    + " also resets the current limit and should only be callable from a test."); +        mDataRequestRateLimiter = +                new MultiRateLimiter.Builder(mContext) +                        .addRateLimit( +                                WearableSensingDataRequest.getRateLimit(), +                                WearableSensingDataRequest.getRateLimitWindowSize()) +                        .build(); +    } +      private DataRequestObserverContext getDataRequestObserverContext(              int dataType, int userId, PendingIntent dataRequestPendingIntent) {          synchronized (mDataRequestObserverContexts) { diff --git a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java index 842bccbe0847..0a9cf344e7b2 100644 --- a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java +++ b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java @@ -29,6 +29,7 @@ import android.util.Slog;  import java.io.IOException;  import java.io.OutputStream;  import java.io.PrintWriter; +import java.time.Duration;  final class WearableSensingShellCommand extends ShellCommand {      private static final String TAG = WearableSensingShellCommand.class.getSimpleName(); @@ -90,6 +91,8 @@ final class WearableSensingShellCommand extends ShellCommand {                  return getBoundPackageName();              case "set-temporary-service":                  return setTemporaryService(); +            case "set-data-request-rate-limit-window-size": +                return setDataRequestRateLimitWindowSize();              default:                  return handleDefaultCommands(cmd);          } @@ -114,6 +117,11 @@ final class WearableSensingShellCommand extends ShellCommand {          pw.println("  set-temporary-service USER_ID [PACKAGE_NAME] [COMPONENT_NAME DURATION]");          pw.println("    Temporarily (for DURATION ms) changes the service implementation.");          pw.println("    To reset, call with just the USER_ID argument."); +        pw.println("  set-data-request-rate-limit-window-size WINDOW_SIZE"); +        pw.println("    Set the window size used in data request rate limiting to WINDOW_SIZE" +                + " seconds."); +        pw.println("    positive WINDOW_SIZE smaller than 20 will be automatically set to 20."); +        pw.println("    To reset, call with 0 or a negative WINDOW_SIZE.");      }      private int createDataStream() { @@ -209,4 +217,20 @@ final class WearableSensingShellCommand extends ShellCommand {          resultPrinter.println(componentName == null ? "" : componentName.getPackageName());          return 0;      } + +    private int setDataRequestRateLimitWindowSize() { +        Slog.d(TAG, "setDataRequestRateLimitWindowSize"); +        int windowSizeSeconds = Integer.parseInt(getNextArgRequired()); +        if (windowSizeSeconds <= 0) { +            mService.resetDataRequestRateLimitWindowSize(); +        } else { +            // 20 is the minimum window size supported by the rate limiter. +            // It is defined by com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS +            if (windowSizeSeconds < 20) { +                windowSizeSeconds = 20; +            } +            mService.setDataRequestRateLimitWindowSize(Duration.ofSeconds(windowSizeSeconds)); +        } +        return 0; +    }  } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c36df8da77ae..2696fb6cba26 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1846,20 +1846,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());      } -    void layoutLetterbox(WindowState winHint) { -        mLetterboxUiController.layoutLetterbox(winHint); +    void layoutLetterboxIfNeeded(WindowState winHint) { +        mLetterboxUiController.layoutLetterboxIfNeeded(winHint);      }      boolean hasWallpaperBackgroundForLetterbox() {          return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();      } -    void updateLetterboxSurface(WindowState winHint, Transaction t) { -        mLetterboxUiController.updateLetterboxSurface(winHint, t); +    void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) { +        mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t);      } -    void updateLetterboxSurface(WindowState winHint) { -        mLetterboxUiController.updateLetterboxSurface(winHint); +    void updateLetterboxSurfaceIfNeeded(WindowState winHint) { +        mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint);      }      /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */ @@ -4546,7 +4546,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          }          super.removeChild(child);          checkKeyguardFlagsChanged(); -        updateLetterboxSurface(child); +        updateLetterboxSurfaceIfNeeded(child);      }      void setAppLayoutChanges(int changes, String reason) { @@ -6036,7 +6036,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          if (destroyedSomething) {              final DisplayContent dc = getDisplayContent();              dc.assignWindowLayers(true /*setLayoutNeeded*/); -            updateLetterboxSurface(null); +            updateLetterboxSurfaceIfNeeded(null);          }      } @@ -7688,7 +7688,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          }          if (mNeedsLetterboxedAnimation) { -            updateLetterboxSurface(findMainWindow(), t); +            updateLetterboxSurfaceIfNeeded(findMainWindow(), t);              mNeedsAnimationBoundsLayer = true;          } @@ -7856,7 +7856,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A          mNeedsAnimationBoundsLayer = false;          if (mNeedsLetterboxedAnimation) {              mNeedsLetterboxedAnimation = false; -            updateLetterboxSurface(findMainWindow(), t); +            updateLetterboxSurfaceIfNeeded(findMainWindow(), t);          }          if (mAnimatingActivityRegistry != null) { @@ -8943,6 +8943,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A              }          } +        // Fixed orientation bounds are the same as its parent container, so clear the fixed +        // orientation bounds. This can happen in close to square displays where the orientation +        // is not respected with insets, but the display still matches or is less than the +        // activity aspect ratio. +        if (resolvedBounds.equals(parentBounds)) { +            resolvedBounds.set(prevResolvedBounds); +            return; +        } +          // Calculate app bounds using fixed orientation bounds because they will be needed later          // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.          getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(), diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 2c492035140b..533529a3a3ff 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -639,6 +639,10 @@ public class ActivityStartController {          return mPendingRemoteAnimationRegistry;      } +    ActivityRecord getLastStartActivity() { +        return mLastStarter != null ? mLastStarter.mStartActivity : null; +    } +      void dumpLastHomeActivityStartResult(PrintWriter pw, String prefix) {          pw.print(prefix);          pw.print("mLastHomeActivityStartResult="); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3637ab129c36..adbe800634c4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5554,7 +5554,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {       * Saves the current activity manager state and includes the saved state in the next dump of       * activity manager.       */ -    void saveANRState(String reason) { +    void saveANRState(ActivityRecord activity, String reason) {          final StringWriter sw = new StringWriter();          final PrintWriter pw = new FastPrintWriter(sw, false, 1024);          pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date())); @@ -5562,14 +5562,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {              pw.println("  Reason: " + reason);          }          pw.println(); -        getActivityStartController().dump(pw, "  ", null); -        pw.println(); +        if (activity != null) { +            final Task rootTask = activity.getRootTask(); +            if (rootTask != null) { +                rootTask.forAllTaskFragments( +                        tf -> tf.dumpInner("  ", pw, true /* dumpAll */, null /* dumpPackage */)); +                pw.println(); +            } +            mActivityStartController.dump(pw, "  ", activity.packageName); +            if (mActivityStartController.getLastStartActivity() != activity) { +                activity.dump(pw, "  ", true /* dumpAll */); +            } +        } +        ActivityTaskSupervisor.printThisActivity(pw, mRootWindowContainer.getTopResumedActivity(), +                null /* dumpPackage */, INVALID_DISPLAY, true /* needSep */, +                "  ResumedActivity: ", /* header= */ null /* header */); +        mLockTaskController.dump(pw, "  "); +        mKeyguardController.dump(pw, "  ");          pw.println("-------------------------------------------------------------------"                  + "------------"); -        dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */, -                true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */, -                INVALID_DISPLAY, "" /* header */); -        pw.println();          pw.close();          mLastANRState = sw.toString(); diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java index b9f6e177e637..0013c5c63798 100644 --- a/services/core/java/com/android/server/wm/AnrController.java +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -367,7 +367,7 @@ class AnrController {                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");                  synchronized (mService.mGlobalLock) {                      mService.saveANRStateLocked(activity, windowState, reason); -                    mService.mAtmService.saveANRState(reason); +                    mService.mAtmService.saveANRState(activity, reason);                  }              } finally {                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index c2dfa21016fa..38ee4564396e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -629,7 +629,7 @@ class BackNavigationController {                  final ActivityRecord ar = openApps.valueAt(i);                  if (mAnimationHandler.isTarget(ar, true /* open */)) {                      openApps.removeAt(i); -                    mAnimationHandler.markStartingSurfaceMatch(); +                    mAnimationHandler.markStartingSurfaceMatch(null /* reparentTransaction */);                  }              }              for (int i = closeApps.size() - 1; i >= 0; --i) { @@ -773,10 +773,15 @@ class BackNavigationController {              for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {                  final WindowContainer wc = mTmpOpenApps.get(i);                  if (mAnimationHandler.isTarget(wc, true /* open */)) { -                    mAnimationHandler.markStartingSurfaceMatch(); +                    mAnimationHandler.markStartingSurfaceMatch(startTransaction);                      break;                  }              } +            // release animation leash +            if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) { +                startTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction); +                mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null; +            }              // Because the target will reparent to transition root, so it cannot be controlled by              // animation leash. Hide the close target when transition starts.              startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl()); @@ -993,7 +998,7 @@ class BackNavigationController {              }              final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2];              targets[0] = mCloseAdaptor.mAnimationTarget; -            targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget(); +            targets[1] = mOpenAnimAdaptor.mRemoteAnimationTarget;              return targets;          } @@ -1067,11 +1072,12 @@ class BackNavigationController {              }          } -        void markStartingSurfaceMatch() { -            mStartingSurfaceTargetMatch = true; -            for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { -                mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget(); +        void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) { +            if (mStartingSurfaceTargetMatch) { +                return;              } +            mStartingSurfaceTargetMatch = true; +            mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);          }          void clearBackAnimateTarget() { @@ -1140,14 +1146,23 @@ class BackNavigationController {          private static class BackWindowAnimationAdaptorWrapper {              final BackWindowAnimationAdaptor[] mAdaptors; +            // The highest remote animation target, which can be a wrapper if multiple adaptors, +            // or the single opening target. +            final RemoteAnimationTarget mRemoteAnimationTarget;              SurfaceControl.Transaction mCloseTransaction; +            // The starting surface task Id. Used to clear the starting surface if the animation has +            // requested one during animating. +            private int mRequestedStartingSurfaceId = INVALID_TASK_ID; +            private SurfaceControl mStartingSurface;              BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,                      @NonNull WindowContainer... targets) {                  mAdaptors = new BackWindowAnimationAdaptor[targets.length];                  for (int i = targets.length - 1; i >= 0; --i) {                      mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType);                  } +                mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget() +                        : mAdaptors[0].mAnimationTarget;              }              boolean isValid() { @@ -1160,76 +1175,152 @@ class BackNavigationController {              }              void cleanUp(boolean startingSurfaceMatch) { +                cleanUpWindowlessSurface(startingSurfaceMatch);                  for (int i = mAdaptors.length - 1; i >= 0; --i) { -                    mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch);                      mAdaptors[i].mTarget.cancelAnimation();                  } +                mRequestedStartingSurfaceId = INVALID_TASK_ID; +                mStartingSurface = null;                  if (mCloseTransaction != null) {                      mCloseTransaction.apply();                      mCloseTransaction = null;                  }              } -            void onAnimationFinish() { -                final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction(); -                if (mCloseTransaction != null) { -                    pt.merge(mCloseTransaction); -                    mCloseTransaction = null; -                } -                if (mAdaptors.length > 1) { -                    for (int i = mAdaptors.length - 1; i >= 0; --i) { -                        final WindowContainer wc = mAdaptors[i].mTarget; -                        final WindowContainer parent = wc.getParent(); -                        if (parent != null) { -                            pt.reparent(wc.getSurfaceControl(), -                                    parent.getSurfaceControl()); -                        } -                    } -                } -            } - -            @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() { +            private RemoteAnimationTarget createWrapTarget() {                  // Special handle for opening two activities together.                  // If we animate both activities separately, the animation area and rounded corner                  // would also being handled separately. To make them seem like "open" together, wrap                  // their leash with another animation leash. -                if (mAdaptors.length > 1 && mCloseTransaction == null) { -                    final Rect unionBounds = new Rect(); -                    for (int i = mAdaptors.length - 1; i >= 0; --i) { -                        unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds); +                final Rect unionBounds = new Rect(); +                for (int i = mAdaptors.length - 1; i >= 0; --i) { +                    unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds); +                } +                final WindowContainer wc = mAdaptors[0].mTarget; +                final Task task = wc.asActivityRecord() != null +                        ? wc.asActivityRecord().getTask() : wc.asTask(); +                final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget; +                final SurfaceControl leashSurface = new SurfaceControl.Builder() +                        .setName("cross-animation-leash") +                        .setContainerLayer() +                        .setHidden(false) +                        .setParent(task.getSurfaceControl()) +                        .build(); +                mCloseTransaction = new SurfaceControl.Transaction(); +                mCloseTransaction.reparent(leashSurface, null); +                final SurfaceControl.Transaction pt = wc.getPendingTransaction(); +                pt.setLayer(leashSurface, wc.getParent().getLastLayer()); +                for (int i = mAdaptors.length - 1; i >= 0; --i) { +                    BackWindowAnimationAdaptor adaptor = mAdaptors[i]; +                    pt.reparent(adaptor.mAnimationTarget.leash, leashSurface); +                    pt.setPosition(adaptor.mAnimationTarget.leash, +                            adaptor.mAnimationTarget.localBounds.left, +                            adaptor.mAnimationTarget.localBounds.top); +                    // For adjacent activity embedded, reparent Activity to TaskFragment when +                    // animation finish +                    final WindowContainer parent = adaptor.mTarget.getParent(); +                    if (parent != null) { +                        mCloseTransaction.reparent(adaptor.mTarget.getSurfaceControl(), +                                parent.getSurfaceControl());                      } -                    final WindowContainer wc = mAdaptors[0].mTarget; -                    final Task task = wc.asActivityRecord() != null -                            ? wc.asActivityRecord().getTask() : wc.asTask(); -                    final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget; -                    final SurfaceControl leashSurface = new SurfaceControl.Builder() -                            .setName("cross-animation-leash") -                            .setContainerLayer() -                            .setHidden(false) -                            .setParent(task.getSurfaceControl()) -                            .build(); -                    final SurfaceControl.Transaction pt = wc.getPendingTransaction(); -                    pt.setLayer(leashSurface, wc.getParent().getLastLayer()); -                    mCloseTransaction = new SurfaceControl.Transaction(); -                    mCloseTransaction.reparent(leashSurface, null); -                    for (int i = mAdaptors.length - 1; i >= 0; --i) { -                        BackWindowAnimationAdaptor adaptor = mAdaptors[i]; -                        pt.reparent(adaptor.mAnimationTarget.leash, leashSurface); -                        pt.setPosition(adaptor.mAnimationTarget.leash, -                                adaptor.mAnimationTarget.localBounds.left, -                                adaptor.mAnimationTarget.localBounds.top); +                } +                return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface, +                        represent.isTranslucent, represent.clipRect, represent.contentInsets, +                        represent.prefixOrderIndex, +                        new Point(unionBounds.left, unionBounds.top), +                        unionBounds, unionBounds, represent.windowConfiguration, +                        true /* isNotInRecents */, null, null, represent.taskInfo, +                        represent.allowEnterPip); +            } + +            void createStartingSurface(@NonNull WindowContainer closeWindow, +                    ActivityRecord[] visibleOpenActivities) { +                if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) { +                    return; +                } +                final WindowContainer mainOpen = mAdaptors[0].mTarget; +                final int switchType = mAdaptors[0].mSwitchType; +                final Task openTask = switchType == TASK_SWITCH +                        ? mainOpen.asTask() : switchType == ACTIVITY_SWITCH +                        ? mainOpen.asActivityRecord().getTask() : null; +                if (openTask == null) { +                    return; +                } +                final ActivityRecord mainActivity = switchType == ACTIVITY_SWITCH +                        ? mainOpen.asActivityRecord() +                        : openTask.getTopNonFinishingActivity(); +                if (mainActivity == null) { +                    return; +                } +                final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities); +                mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController +                        .addWindowlessStartingSurface(openTask, mainActivity, +                        // Choose configuration from closeWindow, because the configuration +                        // of opening target may not update before resume, so the starting +                        // surface should occlude it entirely. +                        mRemoteAnimationTarget.leash, snapshot, closeWindow.getConfiguration(), +                            new IWindowlessStartingSurfaceCallback.Stub() { +                            // Once the starting surface has been created in shell, it will call +                            // onSurfaceAdded to pass the created surface to core, so if a +                            // transition is triggered by the back gesture, there doesn't need to +                            // create another starting surface for the opening target, just reparent +                            // the starting surface to the opening target. +                            // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded +                            // called, there won't be able to reparent the starting surface on +                            // opening target. But if that happens and transition target is matched, +                            // the app window should already draw. +                                @Override +                                public void onSurfaceAdded(SurfaceControl sc) { +                                    synchronized (openTask.mWmService.mGlobalLock) { +                                        if (mRequestedStartingSurfaceId != INVALID_TASK_ID) { +                                            mStartingSurface = sc; +                                        } +                                    } +                                } +                            }); +            } + +            // When back gesture has triggered and transition target matches navigation target, +            // reparent the starting surface to the opening target as it's starting window. +            void reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction) { +                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { +                    return; +                } +                // If open target matches, reparent to open activity or task +                if (mStartingSurface != null && mStartingSurface.isValid()) { +                    SurfaceControl.Transaction transaction = reparentTransaction != null +                            ? reparentTransaction : mAdaptors[0].mTarget.getPendingTransaction(); +                    if (mAdaptors.length == 1) { +                        transaction.reparent(mStartingSurface, +                                        mAdaptors[0].mTarget.getSurfaceControl()); +                    } else { +                        // More than one opening window, reparent starting surface to leaf task. +                        final WindowContainer wc = mAdaptors[0].mTarget; +                        final Task task = wc.asActivityRecord() != null +                                ? wc.asActivityRecord().getTask() : wc.asTask(); +                        transaction.reparent(mStartingSurface, task != null +                                        ? task.getSurfaceControl() +                                        : mAdaptors[0].mTarget.getSurfaceControl());                      } -                    return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface, -                            represent.isTranslucent, represent.clipRect, represent.contentInsets, -                            represent.prefixOrderIndex, -                            new Point(unionBounds.left, unionBounds.top), -                            unionBounds, unionBounds, represent.windowConfiguration, -                            true /* isNotInRecents */, null, null, represent.taskInfo, -                            represent.allowEnterPip); -                } else { -                    return mAdaptors[0].mAnimationTarget; +                    // remove starting surface. +                    mStartingSurface = null;                  }              } + +            /** +             * Ask shell to clear the starting surface. +             * @param openTransitionMatch if true, shell will play the remove starting window +             *                            animation, otherwise remove it directly. +             */ +            void cleanUpWindowlessSurface(boolean openTransitionMatch) { +                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { +                    return; +                } +                mAdaptors[0].mTarget.mWmService.mAtmService.mTaskOrganizerController +                        .removeWindowlessStartingSurface(mRequestedStartingSurfaceId, +                                !openTransitionMatch); +                mRequestedStartingSurfaceId = INVALID_TASK_ID; +            }          }          private static class BackWindowAnimationAdaptor implements AnimationAdapter { @@ -1240,11 +1331,6 @@ class BackNavigationController {              private RemoteAnimationTarget mAnimationTarget;              private final int mSwitchType; -            // The starting surface task Id. Used to clear the starting surface if the animation has -            // requested one during animating. -            private int mRequestedStartingSurfaceId = INVALID_TASK_ID; -            private SurfaceControl mStartingSurface; -              BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen,                      int switchType) {                  mBounds.set(target.getBounds()); @@ -1276,8 +1362,6 @@ class BackNavigationController {              public void onAnimationCancelled(SurfaceControl animationLeash) {                  if (mCapturedLeash == animationLeash) {                      mCapturedLeash = null; -                    mRequestedStartingSurfaceId = INVALID_TASK_ID; -                    mStartingSurface = null;                  }              } @@ -1345,84 +1429,6 @@ class BackNavigationController {                          r.checkEnterPictureInPictureAppOpsState());                  return mAnimationTarget;              } - -            void createStartingSurface(@NonNull WindowContainer closeWindow, -                    @NonNull ActivityRecord[] visibleOpenActivities) { -                if (!mIsOpen) { -                    return; -                } -                if (mSwitchType == DIALOG_CLOSE) { -                    return; -                } -                final Task openTask = mSwitchType == TASK_SWITCH -                        ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH -                        ? mTarget.asActivityRecord().getTask() : null; -                if (openTask == null) { -                    return; -                } -                final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH -                        ? mTarget.asActivityRecord() -                        : openTask.getTopNonFinishingActivity(); -                if (mainActivity == null) { -                    return; -                } -                final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities); -                mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController -                        .addWindowlessStartingSurface(openTask, mainActivity, -                                // Choose configuration from closeWindow, because the configuration -                                // of opening target may not update before resume, so the starting -                                // surface should occlude it entirely. -                                mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(), -                                new IWindowlessStartingSurfaceCallback.Stub() { -                            // Once the starting surface has been created in shell, it will call -                            // onSurfaceAdded to pass the created surface to core, so if a -                            // transition is triggered by the back gesture, there doesn't need to -                            // create another starting surface for the opening target, just reparent -                            // the starting surface to the opening target. -                            // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded -                            // called, there won't be able to reparent the starting surface on -                            // opening target. But if that happens and transition target is matched, -                            // the app window should already draw. -                                    @Override -                                    public void onSurfaceAdded(SurfaceControl sc) { -                                        synchronized (mTarget.mWmService.mGlobalLock) { -                                            if (mRequestedStartingSurfaceId != INVALID_TASK_ID) { -                                                mStartingSurface = sc; -                                            } -                                        } -                                    } -                                }); -            } - -            // When back gesture has triggered and transition target matches navigation target, -            // reparent the starting surface to the opening target as it's starting window. -            void reparentWindowlessSurfaceToTarget() { -                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { -                    return; -                } -                // If open target matches, reparent to open activity or task -                if (mStartingSurface != null && mStartingSurface.isValid()) { -                    mTarget.getPendingTransaction() -                            .reparent(mStartingSurface, mTarget.getSurfaceControl()); -                    // remove starting surface. -                    mStartingSurface = null; -                } -            } - -            /** -             * Ask shell to clear the starting surface. -             * @param openTransitionMatch if true, shell will play the remove starting window -             *                            animation, otherwise remove it directly. -             */ -            void cleanUpWindowlessSurface(boolean openTransitionMatch) { -                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { -                    return; -                } -                mTarget.mWmService.mAtmService.mTaskOrganizerController -                        .removeWindowlessStartingSurface(mRequestedStartingSurfaceId, -                                !openTransitionMatch); -                mRequestedStartingSurfaceId = INVALID_TASK_ID; -            }          }          ScheduleAnimationBuilder prepareAnimation( @@ -1499,18 +1505,10 @@ class BackNavigationController {               * @param visibleOpenActivities  The visible activities in opening targets.               */              private void applyPreviewStrategy(@NonNull WindowContainer closeWindow, -                    @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor, +                    @NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor,                      @NonNull ActivityRecord[] visibleOpenActivities) { -                if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind -                        // TODO (b/274997067) Draw two snapshot in a single starting surface. -                        // We are using TaskId as the key of -                        // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create -                        // two activity snapshot with WindowlessStartingWindow. -                        // Try to draw two snapshot within a WindowlessStartingWindow, or find -                        // another key for StartingWindowRecordManager. -                        && openAnimationAdaptor.length == 1) { -                    openAnimationAdaptor[0].createStartingSurface(closeWindow, -                            visibleOpenActivities); +                if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { +                    openAnimationAdaptor.createStartingSurface(closeWindow, visibleOpenActivities);                  } else {                      for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {                          setLaunchBehind(visibleOpenActivities[i]); @@ -1541,7 +1539,7 @@ class BackNavigationController {                  }                  mCloseTarget.mTransitionController.mSnapshotController                          .mActivitySnapshotController.clearOnBackPressedActivities(); -                applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities); +                applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor, openingActivities);                  final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();                  final RemoteAnimationTarget[] targets = getAnimationTargets(); @@ -1565,7 +1563,6 @@ class BackNavigationController {                                  // animation was canceled                                  return;                              } -                            mOpenAnimAdaptor.onAnimationFinish();                              if (!triggerBack) {                                  clearBackAnimateTarget();                              } else { diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 5b4fb3e6971f..e48e4e84d60d 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -87,11 +87,7 @@ class ClientLifecycleManager {      void scheduleTransactionItemNow(@NonNull IApplicationThread client,              @NonNull ClientTransactionItem transactionItem) throws RemoteException {          final ClientTransaction clientTransaction = ClientTransaction.obtain(client); -        if (transactionItem.isActivityLifecycleItem()) { -            clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); -        } else { -            clientTransaction.addCallback(transactionItem); -        } +        clientTransaction.addTransactionItem(transactionItem);          scheduleTransaction(clientTransaction);      } @@ -115,11 +111,8 @@ class ClientLifecycleManager {          } else {              // TODO(b/260873529): cleanup after launch.              final ClientTransaction clientTransaction = ClientTransaction.obtain(client); -            if (transactionItem.isActivityLifecycleItem()) { -                clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); -            } else { -                clientTransaction.addCallback(transactionItem); -            } +            clientTransaction.addTransactionItem(transactionItem); +              scheduleTransaction(clientTransaction);          }      } @@ -160,8 +153,8 @@ class ClientLifecycleManager {          } else {              // TODO(b/260873529): cleanup after launch.              final ClientTransaction clientTransaction = ClientTransaction.obtain(client); -            clientTransaction.addCallback(transactionItem); -            clientTransaction.setLifecycleStateRequest(lifecycleItem); +            clientTransaction.addTransactionItem(transactionItem); +            clientTransaction.addTransactionItem(lifecycleItem);              scheduleTransaction(clientTransaction);          }      } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e7431723789d..d3acd716aed3 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1127,7 +1127,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp          final ActivityRecord activity = w.mActivityRecord;          if (activity != null && activity.isVisibleRequested()) { -            activity.updateLetterboxSurface(w); +            activity.updateLetterboxSurfaceIfNeeded(w);              final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);              if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {                  mTmpUpdateAllDrawn.add(activity); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index a7bbc25d0bb1..5cf9acdbc0d6 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -104,7 +104,6 @@ import android.os.SystemProperties;  import android.os.Trace;  import android.os.UserHandle;  import android.util.ArraySet; -import android.util.PrintWriterPrinter;  import android.util.Slog;  import android.util.SparseArray;  import android.view.DisplayInfo; @@ -2070,8 +2069,7 @@ public class DisplayPolicy {              }              return false;          } -        if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve() -                && !mDisplayContent.isSleeping()) { +        if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve() && mScreenOnFully) {              mCachedDecorInsets = null;          }          mDecorInsets.invalidate(); @@ -2136,16 +2134,6 @@ public class DisplayPolicy {          }          mCachedDecorInsets.mPreserveId =                  mDisplayContent.mTransitionController.getCollectingTransitionId(); -        // The validator will run after the transition is finished. So if the insets are changed -        // during the transition, it can update to the latest state. -        mDisplayContent.mTransitionController.mStateValidators.add(() -> { -            // The insets provider client may defer to change its window until screen is on. So -            // only validate when awake to avoid the cache being always dropped. -            if (!mDisplayContent.isSleeping() && updateDecorInsetsInfo()) { -                Slog.d(TAG, "Insets changed after display switch transition"); -                mDisplayContent.sendNewConfiguration(); -            } -        });      }      @NavigationBarPosition @@ -2891,9 +2879,6 @@ public class DisplayPolicy {          if (!CLIENT_TRANSIENT) {              mSystemGestures.dump(pw, prefix);          } - -        pw.print(prefix); pw.println("Looper state:"); -        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");      }      private boolean supportsPointerLocation() { diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index edf9da1e0bf5..5d613cf45643 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -52,7 +52,6 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN  import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;  import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;  import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT;  import static android.content.res.Configuration.ORIENTATION_UNDEFINED;  import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;  import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; @@ -924,21 +923,21 @@ final class LetterboxUiController {          return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);      } -    void updateLetterboxSurface(WindowState winHint) { -        updateLetterboxSurface(winHint, mActivityRecord.getSyncTransaction()); +    void updateLetterboxSurfaceIfNeeded(WindowState winHint) { +        updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction());      } -    void updateLetterboxSurface(WindowState winHint, Transaction t) { +    void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {          if (shouldNotLayoutLetterbox(winHint)) {              return;          } -        layoutLetterbox(winHint); +        layoutLetterboxIfNeeded(winHint);          if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {              mLetterbox.applySurfaceChanges(t);          }      } -    void layoutLetterbox(WindowState w) { +    void layoutLetterboxIfNeeded(WindowState w) {          if (shouldNotLayoutLetterbox(w)) {              return;          } @@ -1369,23 +1368,25 @@ final class LetterboxUiController {       *       * <p>Conditions that needs to be met:       * <ul> -     *   <li>Activity is portrait-only. -     *   <li>Fullscreen window in landscape device orientation. +     *   <li>Windowing mode is fullscreen.       *   <li>Horizontal Reachability is enabled. -     *   <li>Activity fills parent vertically. +     *   <li>First top opaque activity fills parent vertically, but not horizontally.       * </ul>       */      private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {          // Use screen resolved bounds which uses resolved bounds or size compat bounds          // as activity bounds can sometimes be empty +        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() +                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds() +                : mActivityRecord.getScreenResolvedBounds();          return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()                  && parentConfiguration.windowConfiguration.getWindowingMode()                          == WINDOWING_MODE_FULLSCREEN -                && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE -                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)                  // Check whether the activity fills the parent vertically.                  && parentConfiguration.windowConfiguration.getAppBounds().height() -                        <= mActivityRecord.getScreenResolvedBounds().height(); +                        <= opaqueActivityBounds.height() +                && parentConfiguration.windowConfiguration.getAppBounds().width() +                        > opaqueActivityBounds.width();      }      @VisibleForTesting @@ -1402,23 +1403,25 @@ final class LetterboxUiController {       *       * <p>Conditions that needs to be met:       * <ul> -     *   <li>Activity is landscape-only. -     *   <li>Fullscreen window in portrait device orientation. +     *   <li>Windowing mode is fullscreen.       *   <li>Vertical Reachability is enabled. -     *   <li>Activity fills parent horizontally. +     *   <li>First top opaque activity fills parent horizontally but not vertically.       * </ul>       */      private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {          // Use screen resolved bounds which uses resolved bounds or size compat bounds          // as activity bounds can sometimes be empty +        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() +                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds() +                : mActivityRecord.getScreenResolvedBounds();          return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()                  && parentConfiguration.windowConfiguration.getWindowingMode()                          == WINDOWING_MODE_FULLSCREEN -                && (parentConfiguration.orientation == ORIENTATION_PORTRAIT -                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)                  // Check whether the activity fills the parent horizontally. -                && parentConfiguration.windowConfiguration.getBounds().width() -                        == mActivityRecord.getScreenResolvedBounds().width(); +                && parentConfiguration.windowConfiguration.getAppBounds().width() +                        <= opaqueActivityBounds.width() +                && parentConfiguration.windowConfiguration.getAppBounds().height() +                        > opaqueActivityBounds.height();      }      @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 975208fb4b4c..908cbd340236 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -110,9 +110,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {      private final String mStringName;      SurfaceSession mSurfaceSession;      private final ArrayList<WindowState> mAddedWindows = new ArrayList<>(); -    // Set of visible application overlay window surfaces connected to this session. -    private final ArraySet<WindowSurfaceController> mAppOverlaySurfaces = new ArraySet<>(); -    // Set of visible alert window surfaces connected to this session. +    /** Set of visible alert/app-overlay window surfaces connected to this session. */      private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();      private final DragDropController mDragDropController;      final boolean mCanAddInternalSystemWindow; @@ -796,46 +794,45 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {          }          boolean changed; - -        if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) { -            // We want to track non-system apps adding alert windows so we can post an -            // on-going notification for the user to control their visibility. -            if (visible) { -                changed = mAlertWindowSurfaces.add(surfaceController); -                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, true); -            } else { -                changed = mAlertWindowSurfaces.remove(surfaceController); -                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, true); +        // Track non-system apps adding overlay/alert windows, so a notification can post for the +        // user to control their visibility. +        final boolean noSystemOverlayPermission = +                !mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay; +        if (visible) { +            changed = mAlertWindowSurfaces.add(surfaceController); +            if (type == TYPE_APPLICATION_OVERLAY) { +                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, +                        false /* set false to only log for TYPE_APPLICATION_OVERLAY */); +            } else if (noSystemOverlayPermission) { +                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, +                        true /* only log for non-TYPE_APPLICATION_OVERLAY */);              } - -            if (changed) { -                if (mAlertWindowSurfaces.isEmpty()) { -                    cancelAlertWindowNotification(); -                } else if (mAlertWindowNotification == null){ -                    mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName); -                    if (mShowingAlertWindowNotificationAllowed) { -                        mAlertWindowNotification.post(); -                    } -                } +        } else { +            changed = mAlertWindowSurfaces.remove(surfaceController); +            if (type == TYPE_APPLICATION_OVERLAY) { +                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, +                        false /* set false to only log for TYPE_APPLICATION_OVERLAY */); +            } else if (noSystemOverlayPermission) { +                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, +                        true /* only log for non-TYPE_APPLICATION_OVERLAY */);              }          } -        if (type != TYPE_APPLICATION_OVERLAY) { -            return; -        } - -        if (visible) { -            changed = mAppOverlaySurfaces.add(surfaceController); -            MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, false); -        } else { -            changed = mAppOverlaySurfaces.remove(surfaceController); -            MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, false); +        if (changed && noSystemOverlayPermission) { +            if (mAlertWindowSurfaces.isEmpty()) { +                cancelAlertWindowNotification(); +            } else if (mAlertWindowNotification == null) { +                mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName); +                if (mShowingAlertWindowNotificationAllowed) { +                    mAlertWindowNotification.post(); +                } +            }          } -        if (changed) { -            // Notify activity manager of changes to app overlay windows so it can adjust the -            // importance score for the process. -            setHasOverlayUi(!mAppOverlaySurfaces.isEmpty()); +        if (changed && mPid != WindowManagerService.MY_PID) { +            // Notify activity manager that the process contains overlay/alert windows, so it can +            // adjust the importance score for the process. +            setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());          }      } @@ -870,12 +867,12 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {          mSurfaceSession = null;          mAddedWindows.clear();          mAlertWindowSurfaces.clear(); -        mAppOverlaySurfaces.clear();          setHasOverlayUi(false);          cancelAlertWindowNotification();      } -    private void setHasOverlayUi(boolean hasOverlayUi) { +    @VisibleForTesting +    void setHasOverlayUi(boolean hasOverlayUi) {          mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();      } @@ -890,7 +887,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {      void dump(PrintWriter pw, String prefix) {          pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());                  pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow); -                pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);                  pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);                  pw.print(" mClientDead="); pw.print(mClientDead);                  pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 838ce86515cd..10cbc6633533 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1590,7 +1590,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {                              mAtmService.getLifecycleManager().scheduleTransactionItem(                                      appThread, activityResultItem);                          } else { -                            transaction.addCallback(activityResultItem); +                            transaction.addTransactionItem(activityResultItem);                          }                      }                  } @@ -1602,7 +1602,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {                          mAtmService.getLifecycleManager().scheduleTransactionItem(                                  appThread, newIntentItem);                      } else { -                        transaction.addCallback(newIntentItem); +                        transaction.addTransactionItem(newIntentItem);                      }                  } @@ -1624,7 +1624,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {                      mAtmService.getLifecycleManager().scheduleTransactionItem(                              appThread, resumeActivityItem);                  } else { -                    transaction.setLifecycleStateRequest(resumeActivityItem); +                    transaction.addTransactionItem(resumeActivityItem);                      mAtmService.getLifecycleManager().scheduleTransaction(transaction);                  } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e538f5d5cf41..61480d253364 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6750,11 +6750,6 @@ public class WindowManagerService extends IWindowManager.Stub      private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,              ArrayList<WindowState> windows) {          pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)"); -        dumpWindowsNoHeaderLocked(pw, dumpAll, windows); -    } - -    private void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll, -            ArrayList<WindowState> windows) {          mRoot.dumpWindowsNoHeader(pw, dumpAll, windows);          if (!mHidingNonSystemOverlayWindows.isEmpty()) { @@ -6989,9 +6984,15 @@ public class WindowManagerService extends IWindowManager.Stub          if (reason != null) {              pw.println("  Reason: " + reason);          } +        pw.println(); +        final ArrayList<WindowState> relatedWindows = new ArrayList<>();          for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {              final DisplayContent dc = mRoot.getChildAt(i);              final int displayId = dc.getDisplayId(); +            final WindowState currentFocus = dc.mCurrentFocus; +            final ActivityRecord focusedApp = dc.mFocusedApp; +            pw.println("  Display #" + displayId + " currentFocus=" + currentFocus +                    + " focusedApp=" + focusedApp);              if (!dc.mWinAddedSinceNullFocus.isEmpty()) {                  pw.println("  Windows added in display #" + displayId + " since null focus: "                          + dc.mWinAddedSinceNullFocus); @@ -7000,12 +7001,25 @@ public class WindowManagerService extends IWindowManager.Stub                  pw.println("  Windows removed in display #" + displayId + " since null focus: "                          + dc.mWinRemovedSinceNullFocus);              } +            pw.println("  Tasks in top down Z order:"); +            dc.forAllTaskDisplayAreas(tda -> { +                tda.dump(pw, "    ", false /* dumpAll */); +            }); +            dc.getInputMonitor().dump(pw, "  "); +            pw.println(); +            dc.forAllWindows(w -> { +                if ((currentFocus != null && Objects.equals(w.mAttrs.packageName, +                        currentFocus.mAttrs.packageName)) || (focusedApp != null +                        && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) { +                    relatedWindows.add(w); +                } +            }, true /* traverseTopToBottom */);          } +        if (windowState != null && !relatedWindows.contains(windowState)) { +            relatedWindows.add(windowState); +        } +        mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows);          pw.println(); -        dumpWindowsNoHeaderLocked(pw, true, null); -        pw.println(); -        pw.println("Last ANR continued"); -        mRoot.dumpDisplayContents(pw);          pw.close();          mLastANRState = sw.toString(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 6acf1f3f84af..ee16a37d6baf 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;  import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;  import static com.android.internal.util.Preconditions.checkArgument;  import static com.android.server.am.ProcessList.INVALID_ADJ; -import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;  import static com.android.server.wm.ActivityRecord.State.DESTROYED;  import static com.android.server.wm.ActivityRecord.State.DESTROYING;  import static com.android.server.wm.ActivityRecord.State.PAUSED; @@ -299,7 +298,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio       */      private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER; -    private boolean mCanUseSystemGrammaticalGender; +    private final boolean mCanUseSystemGrammaticalGender;      public WindowProcessController(@NonNull ActivityTaskManagerService atm,              @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6e993b340352..b8f1d156ec1d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1332,7 +1332,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP          updateSourceFrame(windowFrames.mFrame);          if (mActivityRecord != null && !mIsChildWindow) { -            mActivityRecord.layoutLetterbox(this); +            mActivityRecord.layoutLetterboxIfNeeded(this);          }          mSurfacePlacementNeeded = true;          mHaveFrame = true; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 17638fcaba68..dc8cec91001b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -21,8 +21,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;  import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;  import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST;  import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST; -import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled; -import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;  import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;  import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG; @@ -41,6 +39,7 @@ import android.app.admin.PackagePolicy;  import android.app.admin.PasswordPolicy;  import android.app.admin.PreferentialNetworkServiceConfig;  import android.app.admin.WifiSsidPolicy; +import android.app.admin.flags.Flags;  import android.graphics.Color;  import android.net.wifi.WifiSsid;  import android.os.Bundle; @@ -1297,7 +1296,7 @@ class ActiveAdmin {          pw.print("encryptionRequested=");          pw.println(encryptionRequested); -        if (!dumpsysPolicyEngineMigrationEnabled()) { +        if (!Flags.dumpsysPolicyEngineMigrationEnabled()) {              pw.print("disableCamera=");              pw.println(disableCamera); @@ -1316,7 +1315,8 @@ class ActiveAdmin {              UserRestrictionsUtils.dumpRestrictions(pw, "  ", userRestrictions);          } -        if (!policyEngineMigrationV2Enabled() || !dumpsysPolicyEngineMigrationEnabled()) { +        if (!Flags.policyEngineMigrationV2Enabled() +                || !Flags.dumpsysPolicyEngineMigrationEnabled()) {              pw.print("mUsbDataSignaling=");              pw.println(mUsbDataSignalingEnabled);          } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 3e066f23a520..12f44074a4ad 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -24,7 +24,6 @@ import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_HARDWARE_LIMIT  import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;  import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;  import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;  import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;  import android.Manifest; @@ -42,6 +41,7 @@ import android.app.admin.PolicyUpdateReceiver;  import android.app.admin.PolicyValue;  import android.app.admin.TargetUser;  import android.app.admin.UserRestrictionPolicyKey; +import android.app.admin.flags.Flags;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; @@ -225,7 +225,7 @@ final class DevicePolicyEngine {          synchronized (mLock) {              PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); -            if (devicePolicySizeTrackingEnabled() && false) { +            if (Flags.devicePolicySizeTrackingEnabled() && false) {                  if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,                          policyDefinition, userId)) {                      return; @@ -350,7 +350,7 @@ final class DevicePolicyEngine {              }              PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); -            if (devicePolicySizeTrackingEnabled() && false) { +            if (Flags.devicePolicySizeTrackingEnabled() && false) {                  decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);              } @@ -496,7 +496,7 @@ final class DevicePolicyEngine {          synchronized (mLock) {              PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); -            if (devicePolicySizeTrackingEnabled() && false) { +            if (Flags.devicePolicySizeTrackingEnabled() && false) {                  if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,                          policyDefinition, UserHandle.USER_ALL)) {                      return; @@ -568,7 +568,7 @@ final class DevicePolicyEngine {          synchronized (mLock) {              PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition); -            if (devicePolicySizeTrackingEnabled() && false) { +            if (Flags.devicePolicySizeTrackingEnabled() && false) {                  decreasePolicySizeForAdmin(policyState, enforcingAdmin);              } @@ -1892,7 +1892,7 @@ final class DevicePolicyEngine {          private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)                  throws IOException { -            if (devicePolicySizeTrackingEnabled() && false) { +            if (Flags.devicePolicySizeTrackingEnabled() && false) {                  if (mAdminPolicySize != null) {                      for (int i = 0; i < mAdminPolicySize.size(); i++) {                          int userId = mAdminPolicySize.keyAt(i); @@ -1916,7 +1916,7 @@ final class DevicePolicyEngine {          private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)                  throws IOException { -            if (!devicePolicySizeTrackingEnabled() || true) { +            if (!Flags.devicePolicySizeTrackingEnabled() || true) {                  return;              }              serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT); @@ -2081,7 +2081,7 @@ final class DevicePolicyEngine {          private void readMaxPolicySizeInner(TypedXmlPullParser parser)                  throws XmlPullParserException, IOException { -            if (!devicePolicySizeTrackingEnabled() || true) { +            if (!Flags.devicePolicySizeTrackingEnabled() || true) {                  return;              }              mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9d84d6fc40b4..d5d26beae596 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -239,14 +239,6 @@ import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_  import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;  import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;  import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED; -import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled; -import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled; -import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled; -import static android.app.admin.flags.Flags.headlessDeviceOwnerSingleUserEnabled; -import static android.app.admin.flags.Flags.permissionMigrationForZeroTrustImplEnabled; -import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled; -import static android.app.admin.flags.Flags.assistContentUserRestrictionEnabled; -import static android.app.admin.flags.Flags.securityLogV2Enabled;  import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;  import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;  import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -272,6 +264,7 @@ import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTAT  import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;  import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;  import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;  import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;  import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; @@ -360,6 +353,7 @@ import android.app.admin.SystemUpdatePolicy;  import android.app.admin.UnsafeStateException;  import android.app.admin.UserRestrictionPolicyKey;  import android.app.admin.WifiSsidPolicy; +import android.app.admin.flags.Flags;  import android.app.backup.IBackupManager;  import android.app.compat.CompatChanges;  import android.app.role.OnRoleHoldersChangedListener; @@ -513,7 +507,6 @@ import com.android.internal.widget.PasswordValidationError;  import com.android.modules.utils.TypedXmlPullParser;  import com.android.modules.utils.TypedXmlSerializer;  import com.android.net.module.util.ProxyUtils; -import com.android.net.thread.flags.Flags;  import com.android.server.AlarmManagerInternal;  import com.android.server.LocalManagerRegistry;  import com.android.server.LocalServices; @@ -2728,7 +2721,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              return;          } -        if (securityLogV2Enabled()) { +        if (Flags.securityLogV2Enabled()) {              boolean auditLoggingEnabled = Boolean.TRUE.equals(                      mDevicePolicyEngine.getResolvedPolicy(                              PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL)); @@ -3418,7 +3411,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      @GuardedBy("getLockObject()")      private void maybeMigrateSecurityLoggingPolicyLocked() { -        if (!securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) { +        if (!Flags.securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {              return;          } @@ -3522,7 +3515,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              }              revertTransferOwnershipIfNecessaryLocked(); -            if (!policyEngineMigrationV2Enabled()) { +            if (!Flags.policyEngineMigrationV2Enabled()) {                  updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());              }          } @@ -11151,7 +11144,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                  pw.println();                  mStatLogger.dump(pw);                  pw.println(); -                if (dumpsysPolicyEngineMigrationEnabled()) { +                if (Flags.dumpsysPolicyEngineMigrationEnabled()) {                      mDevicePolicyEngine.dump(pw);                      pw.println();                  } @@ -12068,7 +12061,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          if (packageList != null) { -            if (!devicePolicySizeTrackingEnabled()) { +            if (!Flags.devicePolicySizeTrackingEnabled()) {                  for (String pkg : packageList) {                      PolicySizeVerifier.enforceMaxPackageNameLength(pkg);                  } @@ -12313,7 +12306,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));          checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); -        if (headlessDeviceOwnerSingleUserEnabled()) { +        if (Flags.headlessDeviceOwnerSingleUserEnabled()) {              // Block this method if the device is in headless main user mode              Preconditions.checkCallAuthorization(                      getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER, @@ -13438,12 +13431,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                  UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS});          USER_RESTRICTION_PERMISSIONS.put(                  UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS}); -        if (Flags.threadUserRestrictionEnabled()) { +        if (com.android.net.thread.flags.Flags.threadUserRestrictionEnabled()) {              USER_RESTRICTION_PERMISSIONS.put(                      UserManager.DISALLOW_THREAD_NETWORK,                      new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});          } -        if (assistContentUserRestrictionEnabled()) { +        if (Flags.assistContentUserRestrictionEnabled()) {              USER_RESTRICTION_PERMISSIONS.put(                      UserManager.DISALLOW_ASSIST_CONTENT,                      new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT}); @@ -13777,7 +13770,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              return;          } -        if (!devicePolicySizeTrackingEnabled()) { +        if (!Flags.devicePolicySizeTrackingEnabled()) {              PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");          } @@ -14391,7 +14384,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)              throws SecurityException {          Objects.requireNonNull(packages, "packages is null"); -        if (!devicePolicySizeTrackingEnabled()) { +        if (!Flags.devicePolicySizeTrackingEnabled()) {              for (String pkg : packages) {                  PolicySizeVerifier.enforceMaxPackageNameLength(pkg);              } @@ -15798,7 +15791,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          @Override          public void enforceSecurityLoggingPolicy(boolean enabled) { -            if (!securityLogV2Enabled()) { +            if (!Flags.securityLogV2Enabled()) {                  return;              }              Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy( @@ -15808,7 +15801,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          @Override          public void enforceAuditLoggingPolicy(boolean enabled) { -            if (!securityLogV2Enabled()) { +            if (!Flags.securityLogV2Enabled()) {                  return;              }              Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy( @@ -16345,7 +16338,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                      mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));                  } -                if (permissionMigrationForZeroTrustImplEnabled()) { +                if (Flags.permissionMigrationForZeroTrustImplEnabled()) {                      final UserHandle user = UserHandle.of(userId);                      final String roleHolderPackage = getRoleHolderPackageNameOnUser(                              RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId); @@ -16359,7 +16352,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      @Override      public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) { -        if (permissionMigrationForZeroTrustImplEnabled()) { +        if (Flags.permissionMigrationForZeroTrustImplEnabled()) {              CallerIdentity caller = getCallerIdentity(admin, callerPackage);              enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,                      MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(), @@ -16816,7 +16809,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {                      return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;                  } -                if (headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) { +                if (Flags.headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {                      ensureSetUpUser = mUserManagerInternal.getMainUserId();                      if (ensureSetUpUser == UserHandle.USER_NULL) {                          return STATUS_HEADLESS_ONLY_SYSTEM_USER; @@ -17723,7 +17716,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          final CallerIdentity caller = getCallerIdentity(who, packageName); -        if (securityLogV2Enabled()) { +        if (Flags.securityLogV2Enabled()) {              EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(                      who,                      MANAGE_DEVICE_POLICY_SECURITY_LOGGING, @@ -17783,7 +17776,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              return mInjector.securityLogGetLoggingEnabledProperty();          } -        if (securityLogV2Enabled()) { +        if (Flags.securityLogV2Enabled()) {              final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(                      admin,                      MANAGE_DEVICE_POLICY_SECURITY_LOGGING, @@ -17881,7 +17874,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          final CallerIdentity caller = getCallerIdentity(admin, packageName); -        if (securityLogV2Enabled()) { +        if (Flags.securityLogV2Enabled()) {              EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(                      admin,                      MANAGE_DEVICE_POLICY_SECURITY_LOGGING, @@ -17936,7 +17929,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          final CallerIdentity caller = getCallerIdentity(callingPackage); -        if (!securityLogV2Enabled()) { +        if (!Flags.securityLogV2Enabled()) {              throw new UnsupportedOperationException("Audit log not enabled");          } @@ -17964,7 +17957,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              return false;          } -        if (!securityLogV2Enabled()) { +        if (!Flags.securityLogV2Enabled()) {              throw new UnsupportedOperationException("Audit log not enabled");          } @@ -18230,7 +18223,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          toggleBackupServiceActive(caller.getUserId(), enabled); -        if (backupServiceSecurityLogEventEnabled()) { +        if (Flags.backupServiceSecurityLogEventEnabled()) {              if (SecurityLog.isLoggingEnabled()) {                  SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,                          caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0); @@ -20951,7 +20944,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          final CallerIdentity caller = getCallerIdentity(callerPackage); -        if (permissionMigrationForZeroTrustImplEnabled()) { +        if (Flags.permissionMigrationForZeroTrustImplEnabled()) {              enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());          } else {              Preconditions.checkCallAuthorization( @@ -21555,7 +21548,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {              setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());              setLocale(provisioningParams.getLocale()); -            int deviceOwnerUserId = headlessDeviceOwnerSingleUserEnabled() +            int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled()                      && getHeadlessDeviceOwnerMode() == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER                      ? mUserManagerInternal.getMainUserId()                      : UserHandle.USER_SYSTEM; @@ -21932,7 +21925,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          Objects.requireNonNull(packageName, "Admin package name must be provided");          final CallerIdentity caller = getCallerIdentity(packageName); -        if (!policyEngineMigrationV2Enabled()) { +        if (!Flags.policyEngineMigrationV2Enabled()) {              Preconditions.checkCallAuthorization(                      isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),                      "USB data signaling can only be controlled by a device owner or " @@ -21942,7 +21935,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          }          synchronized (getLockObject()) { -            if (policyEngineMigrationV2Enabled()) { +            if (Flags.policyEngineMigrationV2Enabled()) {                  EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(                          /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,                          caller.getPackageName(), @@ -21982,7 +21975,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      @Override      public boolean isUsbDataSignalingEnabled(String packageName) {          final CallerIdentity caller = getCallerIdentity(packageName); -        if (policyEngineMigrationV2Enabled()) { +        if (Flags.policyEngineMigrationV2Enabled()) {              Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(                      PolicyDefinition.USB_DATA_SIGNALING,                      caller.getUserId()); @@ -22107,9 +22100,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {          enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),                  caller.getUserId()); -        //STOPSHIP: replace 1<<9 with -        // LockPatternUtils.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST once ag/26042068 lands -        return 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId()) & (1 << 9)); +        return mInjector.binderWithCleanCallingIdentity(() -> +                0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId()) +                        & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST));      }      @Override @@ -24235,7 +24228,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      @Override      public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) { -        if (!devicePolicySizeTrackingEnabled() || true) { +        if (!Flags.devicePolicySizeTrackingEnabled() || true) {              return;          }          CallerIdentity caller = getCallerIdentity(callerPackageName); @@ -24247,7 +24240,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {      @Override      public int getMaxPolicyStorageLimit(String callerPackageName) { -        if (!devicePolicySizeTrackingEnabled() || true) { +        if (!Flags.devicePolicySizeTrackingEnabled() || true) {              return -1;          }          CallerIdentity caller = getCallerIdentity(callerPackageName); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java index d9fef10ee41b..9d73ed0070c8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java @@ -16,11 +16,11 @@  package com.android.server.devicepolicy;  import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; -import static android.app.admin.flags.Flags.securityLogV2Enabled;  import android.annotation.Nullable;  import android.app.admin.SystemUpdateInfo;  import android.app.admin.SystemUpdatePolicy; +import android.app.admin.flags.Flags;  import android.content.ComponentName;  import android.os.UserHandle;  import android.util.ArrayMap; @@ -400,7 +400,7 @@ class OwnersData {              out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);              out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine); -            if (securityLogV2Enabled()) { +            if (Flags.securityLogV2Enabled()) {                  out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);              }              out.endTag(null, TAG_POLICY_ENGINE_MIGRATION); @@ -463,7 +463,7 @@ class OwnersData {                  case TAG_POLICY_ENGINE_MIGRATION:                      mMigratedToPolicyEngine = parser.getAttributeBoolean(                              null, ATTR_MIGRATED_TO_POLICY_ENGINE, false); -                    mSecurityLoggingMigrated = securityLogV2Enabled() +                    mSecurityLoggingMigrated = Flags.securityLogV2Enabled()                              && parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);                      break;                  default: diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java index e8c5658ca941..8cb511e8727c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java @@ -17,11 +17,11 @@  package com.android.server.devicepolicy;  import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; -import static android.app.admin.flags.Flags.defaultSmsPersonalAppSuspensionFixEnabled;  import android.accessibilityservice.AccessibilityServiceInfo;  import android.annotation.Nullable;  import android.annotation.UserIdInt; +import android.app.admin.flags.Flags;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; @@ -206,7 +206,7 @@ public final class PersonalAppsSuspensionHelper {      private String getDefaultSmsPackage() {          //TODO(b/319449037): Unflag the following change. -        if (defaultSmsPersonalAppSuspensionFixEnabled()) { +        if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {              return SmsApplication.getDefaultSmsApplicationAsUser(                              mContext, /*updateIfNeeded=*/ false, mContext.getUser())                      .getPackageName(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java index b6ab4c759166..c582a462db81 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java @@ -16,8 +16,6 @@  package com.android.server.devicepolicy; -import static android.app.admin.flags.Flags.securityLogV2Enabled; -  import static java.util.concurrent.TimeUnit.MILLISECONDS;  import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -25,6 +23,7 @@ import android.app.admin.DeviceAdminReceiver;  import android.app.admin.IAuditLogEventsCallback;  import android.app.admin.SecurityLog;  import android.app.admin.SecurityLog.SecurityEvent; +import android.app.admin.flags.Flags;  import android.os.Handler;  import android.os.IBinder;  import android.os.Process; @@ -468,11 +467,11 @@ class SecurityLogMonitor implements Runnable {              assignLogId(event);          } -        if (!securityLogV2Enabled() || mLegacyLogEnabled) { +        if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {              addToLegacyBufferLocked(dedupedLogs);          } -        if (securityLogV2Enabled() && mAuditLogEnabled) { +        if (Flags.securityLogV2Enabled() && mAuditLogEnabled) {              addAuditLogEventsLocked(dedupedLogs);          }      } @@ -549,7 +548,7 @@ class SecurityLogMonitor implements Runnable {                  saveLastEvents(newLogs);                  newLogs.clear(); -                if (!securityLogV2Enabled() || mLegacyLogEnabled) { +                if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {                      notifyDeviceOwnerOrProfileOwnerIfNeeded(force);                  }              } catch (IOException e) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java index 3458b08245a2..306b4f86024e 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java @@ -85,7 +85,7 @@ public class BrightnessWearBedtimeModeClamperTest {      @Test      public void testType() { -        assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType()); +        assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType());      }      @Test diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 4307ec5aa7e1..cea10ea9ade4 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -87,6 +87,7 @@ import android.os.HandlerThread;  import android.os.IRemoteCallback;  import android.os.Looper;  import android.os.Message; +import android.os.PowerManagerInternal;  import android.os.RemoteException;  import android.os.UserHandle;  import android.os.UserManager; @@ -1223,6 +1224,7 @@ public class UserControllerTest {          private final UserManagerInternal mUserManagerInternalMock;          private final WindowManagerService mWindowManagerMock;          private final ActivityTaskManagerInternal mActivityTaskManagerInternal; +        private final PowerManagerInternal mPowerManagerInternal;          private final KeyguardManager mKeyguardManagerMock;          private final LockPatternUtils mLockPatternUtilsMock; @@ -1244,6 +1246,7 @@ public class UserControllerTest {              mWindowManagerMock = mock(WindowManagerService.class);              mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);              mStorageManagerMock = mock(IStorageManager.class); +            mPowerManagerInternal = mock(PowerManagerInternal.class);              mKeyguardManagerMock = mock(KeyguardManager.class);              when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);              mLockPatternUtilsMock = mock(LockPatternUtils.class); @@ -1309,6 +1312,11 @@ public class UserControllerTest {          }          @Override +        PowerManagerInternal getPowerManagerInternal() { +            return mPowerManagerInternal; +        } + +        @Override          KeyguardManager getKeyguardManager() {              return mKeyguardManagerMock;          } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java index 132b6219977a..ec3e97b641b8 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;  import android.app.WindowConfiguration;  import android.companion.virtual.IVirtualDeviceIntentInterceptor;  import android.companion.virtual.VirtualDeviceManager; +import android.content.AttributionSource;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; @@ -712,6 +713,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -732,6 +734,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -753,6 +756,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -774,6 +778,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ Collections.singleton(blockedComponent), @@ -795,6 +800,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ false,                  /* activityPolicyExemptions= */ Collections.singleton(allowedComponent), @@ -816,6 +822,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -837,6 +844,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -858,6 +866,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), @@ -880,6 +889,7 @@ public class GenericWindowPolicyControllerTest {          return new GenericWindowPolicyController(                  0,                  0, +                AttributionSource.myAttributionSource(),                  /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),                  /* activityLaunchAllowedByDefault= */ true,                  /* activityPolicyExemptions= */ new ArraySet<>(), diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 07e6ab2d08fb..fd880dd23f25 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.startsWith;  import static org.mockito.Mockito.doAnswer;  import static org.mockito.Mockito.verify; +import android.content.AttributionSource;  import android.hardware.display.DisplayManagerInternal;  import android.hardware.input.IInputManager;  import android.hardware.input.InputManagerGlobal; @@ -95,6 +96,7 @@ public class InputControllerTest {          mInputController = new InputController(mNativeWrapperMock,                  new Handler(TestableLooper.get(this).getLooper()),                  InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class), +                AttributionSource.myAttributionSource(),                  threadVerifier);      } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java index faece4fbb325..67fc564fa778 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java @@ -32,6 +32,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;  import android.companion.virtual.sensor.VirtualSensor;  import android.companion.virtual.sensor.VirtualSensorConfig;  import android.companion.virtual.sensor.VirtualSensorEvent; +import android.content.AttributionSource;  import android.hardware.Sensor;  import android.os.Binder;  import android.os.IBinder; @@ -96,6 +97,7 @@ public class SensorControllerTest {          Throwable thrown = assertThrows(                  RuntimeException.class,                  () -> new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID, +                        AttributionSource.myAttributionSource(),                          mVirtualSensorCallback, List.of(mVirtualSensorConfig)));          assertThat(thrown.getCause().getMessage()) @@ -168,6 +170,7 @@ public class SensorControllerTest {          doReturn(VIRTUAL_DEVICE_ID).when(mVirtualDevice).getDeviceId();          SensorController sensorController = new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID, +                AttributionSource.myAttributionSource(),                  mVirtualSensorCallback, List.of(mVirtualSensorConfig));          List<VirtualSensor> sensors = sensorController.getSensorList(); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 157e8931edba..5e0806d58af3 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -389,7 +389,8 @@ public class VirtualDeviceManagerServiceTest {          final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;          mInputController = new InputController(mNativeWrapperMock,                  new Handler(TestableLooper.get(this).getLooper()), -                mContext.getSystemService(WindowManager.class), threadVerifier); +                mContext.getSystemService(WindowManager.class), +                AttributionSource.myAttributionSource(), threadVerifier);          mCameraAccessController =                  new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index ef5270e8f003..52f28b9bdc50 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;  import android.companion.virtual.audio.IAudioConfigChangedCallback;  import android.companion.virtual.audio.IAudioRoutingCallback; +import android.content.AttributionSource;  import android.content.Context;  import android.content.ContextWrapper;  import android.media.AudioPlaybackConfiguration; @@ -72,11 +73,13 @@ public class VirtualAudioControllerTest {      public void setUp() {          MockitoAnnotations.initMocks(this);          mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); -        mVirtualAudioController = new VirtualAudioController(mContext); +        mVirtualAudioController = new VirtualAudioController(mContext, +                AttributionSource.myAttributionSource());          mGenericWindowPolicyController =                  new GenericWindowPolicyController(                          FLAG_SECURE,                          SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, +                        AttributionSource.myAttributionSource(),                          /* allowedUsers= */ new ArraySet<>(),                          /* activityLaunchAllowedByDefault= */ true,                          /* activityPolicyExemptions= */ new ArraySet<>(), diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java index 81981e6b16ca..9ca1df09ffe9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java @@ -37,6 +37,7 @@ import android.companion.virtual.camera.VirtualCameraCallback;  import android.companion.virtual.camera.VirtualCameraConfig;  import android.companion.virtualcamera.IVirtualCameraService;  import android.companion.virtualcamera.VirtualCameraConfiguration; +import android.content.AttributionSource;  import android.os.Handler;  import android.os.HandlerExecutor;  import android.os.Looper; @@ -104,7 +105,7 @@ public class VirtualCameraControllerTest {      public void registerCamera_registersCamera(int lensFacing) throws Exception {          mVirtualCameraController.registerCamera(createVirtualCameraConfig(                  CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1, -                CAMERA_SENSOR_ORIENTATION_1, lensFacing)); +                CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());          ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =                  ArgumentCaptor.forClass(VirtualCameraConfiguration.class); @@ -121,7 +122,7 @@ public class VirtualCameraControllerTest {          VirtualCameraConfig config = createVirtualCameraConfig(                  CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,                  CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1); -        mVirtualCameraController.registerCamera(config); +        mVirtualCameraController.registerCamera(config, AttributionSource.myAttributionSource());          mVirtualCameraController.unregisterCamera(config); @@ -131,11 +132,15 @@ public class VirtualCameraControllerTest {      @Test      public void close_unregistersAllCameras() throws Exception {          mVirtualCameraController.registerCamera(createVirtualCameraConfig( -                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1, -                CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1)); +                        CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, +                        CAMERA_NAME_1, +                        CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1), +                AttributionSource.myAttributionSource());          mVirtualCameraController.registerCamera(createVirtualCameraConfig( -                CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, CAMERA_NAME_2, -                CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2)); +                        CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, +                        CAMERA_NAME_2, +                        CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2), +                AttributionSource.myAttributionSource());          mVirtualCameraController.close(); @@ -160,11 +165,12 @@ public class VirtualCameraControllerTest {              int lensFacing) {          mVirtualCameraController.registerCamera(createVirtualCameraConfig(                  CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1, -                CAMERA_SENSOR_ORIENTATION_1, lensFacing)); +                CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());          assertThrows(IllegalArgumentException.class,                  () -> mVirtualCameraController.registerCamera(createVirtualCameraConfig( -                        CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, -                        CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing))); +                                CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, +                                CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing), +                        AttributionSource.myAttributionSource()));      }      @Parameters(method = "getAllLensFacingDirections") @@ -176,8 +182,9 @@ public class VirtualCameraControllerTest {          assertThrows(IllegalArgumentException.class,                  () -> mVirtualCameraController.registerCamera(createVirtualCameraConfig( -                        CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, -                        CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing))); +                                CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, +                                CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing), +                        AttributionSource.myAttributionSource()));      }      private VirtualCameraConfig createVirtualCameraConfig( @@ -203,7 +210,7 @@ public class VirtualCameraControllerTest {      }      private static Integer[] getAllLensFacingDirections() { -        return new Integer[] { +        return new Integer[]{                  LENS_FACING_BACK,                  LENS_FACING_FRONT          }; diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index 1ae6e63c3ff1..0d826dfd2467 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -43,8 +43,10 @@ import android.content.Context;  import android.content.pm.ApplicationInfo;  import android.content.pm.InstallSourceInfo;  import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller;  import android.content.pm.PackageManager;  import android.content.pm.PackageManagerInternal; +import android.content.pm.ParceledListSlice;  import android.os.FileUtils;  import android.os.Looper;  import android.os.RemoteException; @@ -115,9 +117,6 @@ public final class BackgroundInstallControlServiceTest {      private BackgroundInstallControlCallbackHelper mCallbackHelper;      @Captor -    private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor; - -    @Captor      private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;      @Before @@ -137,8 +136,8 @@ public final class BackgroundInstallControlServiceTest {          mUsageEventListener = mUsageEventListenerCaptor.getValue();          mBackgroundInstallControlService.onStart(true); -        verify(mPackageManagerInternal).getPackageList(mPackageListObserverCaptor.capture()); -        mPackageListObserver = mPackageListObserverCaptor.getValue(); + +        mPackageListObserver = mBackgroundInstallControlService.mPackageObserver;      }      @After @@ -554,6 +553,7 @@ public final class BackgroundInstallControlServiceTest {          assertEquals(0, foregroundTimeFrames.size());      } +    //package installed, but no UI interaction found      @Test      public void testHandleUsageEvent_packageAddedNoUsageEvent()              throws NoSuchFieldException, PackageManager.NameNotFoundException { @@ -571,12 +571,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); @@ -590,6 +588,10 @@ public final class BackgroundInstallControlServiceTest {          assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));      } +    private long convertToTestAdjustTimestamp(long timestamp) { +        return timestamp - (System.currentTimeMillis() - SystemClock.uptimeMillis()); +    } +      @Test      public void testHandleUsageEvent_packageAddedInsideTimeFrame()              throws NoSuchFieldException, PackageManager.NameNotFoundException { @@ -607,12 +609,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); @@ -639,6 +639,122 @@ public final class BackgroundInstallControlServiceTest {      }      @Test +    public void testHandleUsageEvent_fallsBackToAppInfoTimeWhenHistoricalSessionsNotFound() +            throws NoSuchFieldException, PackageManager.NameNotFoundException { +        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); +        InstallSourceInfo installSourceInfo = +                new InstallSourceInfo( +                        /* initiatingPackageName= */ INSTALLER_NAME_1, +                        /* initiatingPackageSigningInfo= */ null, +                        /* originatingPackageName= */ null, +                        /* installingPackageName= */ INSTALLER_NAME_1); +        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); +        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); +        ApplicationInfo appInfo = mock(ApplicationInfo.class); + +        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) +                .thenReturn(appInfo); + +        FieldSetter.setField( +                appInfo, +                ApplicationInfo.class.getDeclaredField("createTimestamp"), +                // create timestamp is after generated foreground events (hence not considered +                // foreground install) +                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1)); + +        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE; +        assertEquals(USER_ID_1, UserHandle.getUserId(uid)); + +        createPackageManagerHistoricalSessions(List.of(), USER_ID_1); + +        // The 2 relevants usage events are before the timeframe, the app is not considered +        // foreground installed. +        doReturn(PERMISSION_GRANTED) +                .when(mPermissionManager) +                .checkPermission(anyString(), anyString(), anyString(), anyInt()); +        generateUsageEvent( +                UsageEvents.Event.ACTIVITY_RESUMED, +                USER_ID_1, +                INSTALLER_NAME_1, +                USAGE_EVENT_TIMESTAMP_1); +        generateUsageEvent( +                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + +        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); +        mTestLooper.dispatchAll(); + +        var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages(); +        assertNotNull(packages); +        assertEquals(1, packages.size()); +        assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1)); +    } + +    @Test +    public void testHandleUsageEvent_usesHistoricalSessionCreateTimeWhenHistoricalSessionsFound() +            throws NoSuchFieldException, PackageManager.NameNotFoundException { +        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); +        InstallSourceInfo installSourceInfo = +                new InstallSourceInfo( +                        /* initiatingPackageName= */ INSTALLER_NAME_1, +                        /* initiatingPackageSigningInfo= */ null, +                        /* originatingPackageName= */ null, +                        /* installingPackageName= */ INSTALLER_NAME_1); +        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1); +        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo); +        ApplicationInfo appInfo = mock(ApplicationInfo.class); + +        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt())) +                .thenReturn(appInfo); + +        FieldSetter.setField( +                appInfo, +                ApplicationInfo.class.getDeclaredField("createTimestamp"), +                //create timestamp is out of window of (after) the interact events +                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1)); + +        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE; +        assertEquals(USER_ID_1, UserHandle.getUserId(uid)); + +        PackageInstaller.SessionInfo installSession1 = mock(PackageInstaller.SessionInfo.class); +        PackageInstaller.SessionInfo installSession2 = mock(PackageInstaller.SessionInfo.class); +        doReturn(convertToTestAdjustTimestamp(0L)).when(installSession1).getCreatedMillis(); +        doReturn(convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1)).when(installSession2) +                .getCreatedMillis(); +        doReturn(PACKAGE_NAME_1).when(installSession1).getAppPackageName(); +        doReturn(PACKAGE_NAME_1).when(installSession2).getAppPackageName(); +        createPackageManagerHistoricalSessions(List.of(installSession1, installSession2), +                USER_ID_1); + +        // The following 2 generated usage events occur after historical session create times hence, +        // considered foreground install. The appInfo createTimestamp occurs after events, so the +        // app would be considered background install if it falls back to it as reference create +        // timestamp. +        doReturn(PERMISSION_GRANTED) +                .when(mPermissionManager) +                .checkPermission(anyString(), anyString(), anyString(), anyInt()); +        generateUsageEvent( +                UsageEvents.Event.ACTIVITY_RESUMED, +                USER_ID_1, +                INSTALLER_NAME_1, +                USAGE_EVENT_TIMESTAMP_1); +        generateUsageEvent( +                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); + +        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid); +        mTestLooper.dispatchAll(); + +        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); +    } + +    private void createPackageManagerHistoricalSessions( +            List<PackageInstaller.SessionInfo> sessions, int userId) { +        ParceledListSlice<PackageInstaller.SessionInfo> mockParcelList = +                mock(ParceledListSlice.class); +        when(mockParcelList.getList()).thenReturn(sessions); +        when(mPackageManagerInternal.getHistoricalSessions(userId)).thenReturn(mockParcelList); +    } + +    @Test      public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()              throws NoSuchFieldException, PackageManager.NameNotFoundException {          assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages()); @@ -655,12 +771,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); @@ -708,12 +822,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); @@ -765,12 +877,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); @@ -818,12 +928,10 @@ public final class BackgroundInstallControlServiceTest {          when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))                  .thenReturn(appInfo); -        long createTimestamp = -                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());          FieldSetter.setField(                  appInfo,                  ApplicationInfo.class.getDeclaredField("createTimestamp"), -                createTimestamp); +                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));          int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;          assertEquals(USER_ID_1, UserHandle.getUserId(uid)); diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java index 8bccce102887..f5c6795484fa 100644 --- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java @@ -16,41 +16,60 @@  package com.android.server.search; -import android.app.SearchManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +  import android.app.SearchableInfo;  import android.app.SearchableInfo.ActionKeyInfo;  import android.content.ComponentName;  import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.content.res.XmlResourceParser; -import android.os.RemoteException; -import com.android.server.search.Searchables; -import android.test.AndroidTestCase; +import android.content.pm.PackageManagerInternal;  import android.test.MoreAsserts; -import android.test.mock.MockContext; -import android.test.mock.MockPackageManager; -import android.test.suitebuilder.annotation.SmallTest;  import android.view.KeyEvent; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +  import java.util.ArrayList; -import java.util.List; -/** - * To launch this test from the command line: - *  - * adb shell am instrument -w \ - *   -e class com.android.unit_tests.SearchablesTest \ - *   com.android.unit_tests/android.test.InstrumentationTestRunner - */ +@RunWith(AndroidJUnit4.class)  @SmallTest -public class SearchablesTest extends AndroidTestCase { -     +public class SearchablesTest { +    @Mock protected PackageManagerInternal mPackageManagerInternal; + +    private Context mContext; + +    @Before +    public final void setUp() { +        MockitoAnnotations.initMocks(this); + +        LocalServices.removeServiceForTest(PackageManagerInternal.class); +        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + +        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); +    } + +    @After +    public final void tearDown() { +        Mockito.framework().clearInlineMocks(); +    } +      /*       * SearchableInfo tests       *  Mock the context so I can provide very specific input data @@ -69,15 +88,15 @@ public class SearchablesTest extends AndroidTestCase {       * Test that non-searchable activities return no searchable info (this would typically       * trigger the use of the default searchable e.g. contacts)       */ +    @Test      public void testNonSearchable() {          // test basic array & hashmap          Searchables searchables = new Searchables(mContext, 0);          searchables.updateSearchableList();          // confirm that we return null for non-searchy activities -        ComponentName nonActivity = new ComponentName( -                            "com.android.frameworks.coretests", -                            "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY"); +        ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests", +                "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY");          SearchableInfo si = searchables.getSearchableInfo(nonActivity);          assertNull(si);      } @@ -97,13 +116,11 @@ public class SearchablesTest extends AndroidTestCase {       *  getIcon works       */ +    @Test      public void testSearchablesListReal() { -        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); -        MyMockContext mockContext = new MyMockContext(mContext, mockPM); +        doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); -        // build item list with real-world source data -        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH); -        Searchables searchables = new Searchables(mockContext, 0); +        Searchables searchables = new Searchables(mContext, 0);          searchables.updateSearchableList();          // tests with "real" searchables (deprecate, this should be a unit test)          ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList(); @@ -117,12 +134,11 @@ public class SearchablesTest extends AndroidTestCase {      /**       * This round of tests confirms good operations with "zero" searchables found       */ +    @Test      public void testSearchablesListEmpty() { -        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager()); -        MyMockContext mockContext = new MyMockContext(mContext, mockPM); +        doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt()); -        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO); -        Searchables searchables = new Searchables(mockContext, 0); +        Searchables searchables = new Searchables(mContext, 0);          searchables.updateSearchableList();          ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();          assertNotNull(searchablesList); @@ -219,231 +235,6 @@ public class SearchablesTest extends AndroidTestCase {          if (s != null) {              MoreAsserts.assertNotEqual(s, "");          } -    }     -     -    /** -     * This is a mock for context.  Used to perform a true unit test on SearchableInfo. -     *  -     */ -    private class MyMockContext extends MockContext { -         -        protected Context mRealContext; -        protected PackageManager mPackageManager; -         -        /** -         * Constructor. -         *  -         * @param realContext Please pass in a real context for some pass-throughs to function. -         */ -        MyMockContext(Context realContext, PackageManager packageManager) { -            mRealContext = realContext; -            mPackageManager = packageManager; -        } -         -        /** -         * Resources.  Pass through for now. -         */ -        @Override -        public Resources getResources() { -            return mRealContext.getResources(); -        } - -        /** -         * Package manager.  Pass through for now. -         */ -        @Override -        public PackageManager getPackageManager() { -            return mPackageManager; -        } - -        /** -         * Package manager.  Pass through for now. -         */ -        @Override -        public Context createPackageContext(String packageName, int flags) -                throws PackageManager.NameNotFoundException { -            return mRealContext.createPackageContext(packageName, flags); -        } - -        /** -         * Message broadcast.  Pass through for now. -         */ -        @Override -        public void sendBroadcast(Intent intent) { -            mRealContext.sendBroadcast(intent); -        } -    } - -/** - * This is a mock for package manager.  Used to perform a true unit test on SearchableInfo. - *  - */ -    private class MyMockPackageManager extends MockPackageManager { -         -        public final static int SEARCHABLES_PASSTHROUGH = 0; -        public final static int SEARCHABLES_MOCK_ZERO = 1; -        public final static int SEARCHABLES_MOCK_ONEGOOD = 2; -        public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3; -         -        protected PackageManager mRealPackageManager; -        protected int mSearchablesMode; - -        public MyMockPackageManager(PackageManager realPM) { -            mRealPackageManager = realPM; -            mSearchablesMode = SEARCHABLES_PASSTHROUGH; -        } - -        /** -         * Set the mode for various tests. -         */ -        public void setSearchablesMode(int newMode) { -            switch (newMode) { -            case SEARCHABLES_PASSTHROUGH: -            case SEARCHABLES_MOCK_ZERO: -                mSearchablesMode = newMode; -                break; -                 -            default: -                throw new UnsupportedOperationException();        -            } -        } -         -        /** -         * Find activities that support a given intent. -         *  -         * Retrieve all activities that can be performed for the given intent. -         *  -         * @param intent The desired intent as per resolveActivity(). -         * @param flags Additional option flags.  The most important is -         *                    MATCH_DEFAULT_ONLY, to limit the resolution to only -         *                    those activities that support the CATEGORY_DEFAULT. -         *  -         * @return A List<ResolveInfo> containing one entry for each matching -         *         Activity. These are ordered from best to worst match -- that -         *         is, the first item in the list is what is returned by -         *         resolveActivity().  If there are no matching activities, an empty -         *         list is returned. -         */ -        @Override  -        public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { -            assertNotNull(intent); -            assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH) -                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH) -                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); -            switch (mSearchablesMode) { -            case SEARCHABLES_PASSTHROUGH: -                return mRealPackageManager.queryIntentActivities(intent, flags); -            case SEARCHABLES_MOCK_ZERO: -                return null; -            default: -                throw new UnsupportedOperationException(); -            } -        } -         -        @Override -        public ResolveInfo resolveActivity(Intent intent, int flags) { -            assertNotNull(intent); -            assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH) -                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH)); -            switch (mSearchablesMode) { -            case SEARCHABLES_PASSTHROUGH: -                return mRealPackageManager.resolveActivity(intent, flags); -            case SEARCHABLES_MOCK_ZERO: -                return null; -            default: -                throw new UnsupportedOperationException(); -            } -        } - -        /** -         * Retrieve an XML file from a package.  This is a low-level API used to -         * retrieve XML meta data. -         *  -         * @param packageName The name of the package that this xml is coming from. -         * Can not be null. -         * @param resid The resource identifier of the desired xml.  Can not be 0. -         * @param appInfo Overall information about <var>packageName</var>.  This -         * may be null, in which case the application information will be retrieved -         * for you if needed; if you already have this information around, it can -         * be much more efficient to supply it here. -         *  -         * @return Returns an TypedXmlPullParser allowing you to parse out the XML -         * data.  Returns null if the xml resource could not be found for any -         * reason. -         */ -        @Override  -        public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) { -            assertNotNull(packageName); -            MoreAsserts.assertNotEqual(packageName, ""); -            MoreAsserts.assertNotEqual(resid, 0); -            switch (mSearchablesMode) { -            case SEARCHABLES_PASSTHROUGH: -                return mRealPackageManager.getXml(packageName, resid, appInfo); -            case SEARCHABLES_MOCK_ZERO: -            default: -                throw new UnsupportedOperationException(); -            } -        } -         -        /** -         * Find a single content provider by its base path name. -         *  -         * @param name The name of the provider to find. -         * @param flags Additional option flags.  Currently should always be 0. -         *  -         * @return ContentProviderInfo Information about the provider, if found, -         *         else null. -         */ -        @Override  -        public ProviderInfo resolveContentProvider(String name, int flags) { -            assertNotNull(name); -            MoreAsserts.assertNotEqual(name, ""); -            assertEquals(flags, 0); -            switch (mSearchablesMode) { -            case SEARCHABLES_PASSTHROUGH: -                return mRealPackageManager.resolveContentProvider(name, flags); -            case SEARCHABLES_MOCK_ZERO: -            default: -                throw new UnsupportedOperationException(); -            } -        } - -        /** -         * Get the activity information for a particular activity. -         * -         * @param name The name of the activity to find. -         * @param flags Additional option flags. -         * -         * @return ActivityInfo Information about the activity, if found, else null. -         */ -        @Override -        public ActivityInfo getActivityInfo(ComponentName name, int flags) -                throws NameNotFoundException { -            assertNotNull(name); -            MoreAsserts.assertNotEqual(name, ""); -            switch (mSearchablesMode) { -            case SEARCHABLES_PASSTHROUGH: -                return mRealPackageManager.getActivityInfo(name, flags); -            case SEARCHABLES_MOCK_ZERO: -                throw new NameNotFoundException(); -            default: -                throw new UnsupportedOperationException(); -            } -        } - -        @Override -        public int checkPermission(String permName, String pkgName) { -            assertNotNull(permName); -            assertNotNull(pkgName); -            switch (mSearchablesMode) { -                case SEARCHABLES_PASSTHROUGH: -                    return mRealPackageManager.checkPermission(permName, pkgName); -                case SEARCHABLES_MOCK_ZERO: -                    return PackageManager.PERMISSION_DENIED; -                default: -                    throw new UnsupportedOperationException(); -                } -        }      }  } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 6e5c180de347..3f2ccafe6425 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -5384,6 +5384,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {          ZenRule rule = new ZenRule();          rule.pkg = pkg;          rule.creationTime = createdAt.toEpochMilli(); +        rule.enabled = true;          rule.deletionInstant = deletedAt;          // Plus stuff so that isValidAutomaticRule() passes          rule.name = "A rule from " + pkg + " created on " + createdAt; @@ -5392,6 +5393,47 @@ public class ZenModeHelperTest extends UiServiceTestCase {      }      @Test +    @EnableFlags(Flags.FLAG_MODES_API) +    public void getAutomaticZenRuleState_ownedRule_returnsRuleState() { +        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), +                new AutomaticZenRule.Builder("Rule", CONDITION_ID) +                        .setConfigurationActivity( +                                new ComponentName(mContext.getPackageName(), "Blah")) +                        .build(), +                UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID); + +        // Null condition -> STATE_FALSE +        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE); + +        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_TRUE, UPDATE_ORIGIN_APP, +                CUSTOM_PKG_UID); +        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_TRUE); + +        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_FALSE, UPDATE_ORIGIN_APP, +                CUSTOM_PKG_UID); +        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE); + +        mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID); +        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_UNKNOWN); +    } + +    @Test +    @EnableFlags(Flags.FLAG_MODES_API) +    public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() { +        // Assume existence of a system-owned rule that is currently ACTIVE. +        ZenRule systemRule = newZenRule("android", Instant.now(), null); +        systemRule.zenMode = ZEN_MODE_ALARMS; +        systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE); +        ZenModeConfig config = mZenModeHelper.mConfig.copy(); +        config.automaticRules.put("systemRule", systemRule); +        mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID); +        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS); + +        assertThat(mZenModeHelper.getAutomaticZenRuleState("systemRule")).isEqualTo( +                Condition.STATE_UNKNOWN); +    } + +    @Test      @EnableFlags(android.app.Flags.FLAG_MODES_API)      public void testCallbacks_policy() throws Exception {          setupZenConfig(); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java index f0803418376f..f54c7e57828b 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -100,6 +100,8 @@ public class VibrationSettingsTest {      @Rule      public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); +    private static final int OLD_USER_ID = 123; +    private static final int NEW_USER_ID = 456;      private static final int UID = 1;      private static final int VIRTUAL_DEVICE_ID = 1;      private static final String SYSUI_PACKAGE_NAME = "sysui"; @@ -211,10 +213,10 @@ public class VibrationSettingsTest {          mVibrationSettings.addListener(mListenerMock);          // Testing the broadcast flow manually. -        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy, -                new Intent(Intent.ACTION_USER_SWITCHED)); +        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID); +        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID); -        verify(mListenerMock).onChange(); +        verify(mListenerMock, times(2)).onChange();      }      @Test @@ -265,8 +267,7 @@ public class VibrationSettingsTest {          // Trigger multiple observers manually.          mVibrationSettings.mSettingObserver.onChange(false);          mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); -        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy, -                new Intent(Intent.ACTION_USER_SWITCHED)); +        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);          mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,                  new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); @@ -834,13 +835,17 @@ public class VibrationSettingsTest {          assertEquals(VIBRATION_INTENSITY_HIGH,                  mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE)); -        // Switching user is not working with FakeSettingsProvider. -        // Testing the broadcast flow manually. -        Settings.System.putIntForUser(mContextSpy.getContentResolver(), -                Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW, +        // Test early update of settings based on new user id. +        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW, +                NEW_USER_ID); +        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID); +        assertEquals(VIBRATION_INTENSITY_LOW, +                mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE)); + +        // Test later update of settings for UserHandle.USER_CURRENT. +        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,                  UserHandle.USER_CURRENT); -        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy, -                new Intent(Intent.ACTION_USER_SWITCHED)); +        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);          assertEquals(VIBRATION_INTENSITY_LOW,                  mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));      } @@ -956,12 +961,16 @@ public class VibrationSettingsTest {      }      private void setUserSetting(String settingName, int value) { -        Settings.System.putIntForUser( -                mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); +        putUserSetting(settingName, value, UserHandle.USER_CURRENT);          // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.          mVibrationSettings.mSettingObserver.onChange(false);      } +    private void putUserSetting(String settingName, int value, int userHandle) { +        Settings.System.putIntForUser( +                mContextSpy.getContentResolver(), settingName, value, userHandle); +    } +      private void setRingerMode(int ringerMode) {          when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);          // Mock AudioManager broadcast of internal ringer mode change. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 5d14334aaf69..5965fae74dcc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;  import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;  import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;  import static org.junit.Assert.assertEquals; @@ -388,6 +389,24 @@ public class DisplayPolicyTests extends WindowTestsBase {          // The current insets are restored from cache directly.          assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,                  info.logicalWidth, info.logicalHeight).mConfigFrame); + +        // If screen is not fully turned on, then the cache should be preserved. +        displayPolicy.screenTurnedOff(); +        final TransitionController transitionController = mDisplayContent.mTransitionController; +        spyOn(transitionController); +        doReturn(true).when(transitionController).isCollecting(); +        doReturn(Integer.MAX_VALUE).when(transitionController).getCollectingTransitionId(); +        // Make CachedDecorInsets.canPreserve return false. +        displayPolicy.physicalDisplayUpdated(); +        assertFalse(displayPolicy.shouldKeepCurrentDecorInsets()); +        displayPolicy.getDecorInsetsInfo(info.rotation, info.logicalWidth, info.logicalHeight) +                .mConfigFrame.offset(1, 1); +        // Even if CachedDecorInsets.canPreserve returns false, the cache won't be cleared. +        displayPolicy.updateDecorInsetsInfo(); +        // Successful to restore from cache. +        displayPolicy.updateCachedDecorInsets(); +        assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation, +                info.logicalWidth, info.logicalHeight).mConfigFrame);      }      @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index f42cdb88021e..b96f39d7a4e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;  import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;  import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;  import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;  import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;  import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;  import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; @@ -46,6 +47,7 @@ import static android.view.WindowInsets.Type.statusBars;  import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;  import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;  import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;  import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; @@ -100,6 +102,7 @@ import android.content.pm.ActivityInfo.ScreenOrientation;  import android.content.pm.IPackageManager;  import android.content.pm.PackageManager;  import android.content.res.Configuration; +import android.graphics.Insets;  import android.graphics.Rect;  import android.os.Binder;  import android.os.RemoteException; @@ -153,6 +156,8 @@ public class SizeCompatTests extends WindowTestsBase {      private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =              "never_constrain_display_apis_all_packages"; +    private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.005f; +      @Rule      public TestRule compatChangeRule = new PlatformCompatChangeRule(); @@ -211,90 +216,46 @@ public class SizeCompatTests extends WindowTestsBase {      @Test      public void testHorizontalReachabilityEnabledForTranslucentActivities() { -        setUpDisplaySizeWithApp(2500, 1000); -        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); -        final LetterboxConfiguration config = mWm.mLetterboxConfiguration; -        config.setTranslucentLetterboxingOverrideEnabled(true); -        config.setLetterboxHorizontalPositionMultiplier(0.5f); -        config.setIsHorizontalReachabilityEnabled(true); - -        // Opaque activity -        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); -        addWindowToActivity(mActivity); -        mActivity.mRootWindowContainer.performSurfacePlacement(); - -        // Translucent Activity -        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm) -                .setActivityTheme(android.R.style.Theme_Translucent) -                .setLaunchedFromUid(mActivity.getUid()) -                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) -                .build(); -        mTask.addChild(translucentActivity); - -        spyOn(translucentActivity.mLetterboxUiController); -        doReturn(true).when(translucentActivity.mLetterboxUiController) -                .shouldShowLetterboxUi(any()); - -        addWindowToActivity(translucentActivity); -        translucentActivity.mRootWindowContainer.performSurfacePlacement(); - -        final Function<ActivityRecord, Rect> innerBoundsOf = -                (ActivityRecord a) -> { -                    final Rect bounds = new Rect(); -                    a.mLetterboxUiController.getLetterboxInnerBounds(bounds); -                    return bounds; -                }; -        final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity), -                innerBoundsOf.apply(translucentActivity)); -        final Runnable checkIsLeft = () -> assertThat( -                innerBoundsOf.apply(translucentActivity).left).isEqualTo(0); -        final Runnable checkIsRight = () -> assertThat( -                innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500); -        final Runnable checkIsCentered = () -> assertThat( -                innerBoundsOf.apply(translucentActivity).left > 0 -                        && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue(); - -        final Consumer<Integer> doubleClick = -                (Integer x) -> { -                    mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x); -                    mActivity.mRootWindowContainer.performSurfacePlacement(); -                }; - -        // Initial state -        checkIsCentered.run(); - -        // Double-click left -        doubleClick.accept(/* x */ 10); -        checkLetterboxPositions.run(); -        checkIsLeft.run(); - -        // Double-click right -        doubleClick.accept(/* x */ 1990); -        checkLetterboxPositions.run(); -        checkIsCentered.run(); - -        // Double-click right -        doubleClick.accept(/* x */ 1990); -        checkLetterboxPositions.run(); -        checkIsRight.run(); +        testReachabilityEnabledForTranslucentActivity(/* dw */ 2500,  /* dh */1000, +                SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f, +                /* horizontalReachability */ true); +    } -        // Double-click left -        doubleClick.accept(/* x */ 10); -        checkLetterboxPositions.run(); -        checkIsCentered.run(); +    @Test +    public void testHorizontalReachabilityEnabled_TranslucentPortraitActivities_portraitDisplay() { +        testReachabilityEnabledForTranslucentActivity(/* dw */ 1400,  /* dh */1600, +                SCREEN_ORIENTATION_PORTRAIT, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, +                /* horizontalReachability */ true);      }      @Test      public void testVerticalReachabilityEnabledForTranslucentActivities() { -        setUpDisplaySizeWithApp(1000, 2500); +        testReachabilityEnabledForTranslucentActivity(/* dw */ 1000,  /* dh */2500, +                SCREEN_ORIENTATION_LANDSCAPE, /* minAspectRatio */ 0f, +                /* horizontalReachability */ false); +    } + +    @Test +    public void testVerticalReachabilityEnabled_TranslucentLandscapeActivities_landscapeDisplay() { +        testReachabilityEnabledForTranslucentActivity(/* dw */ 1600,  /* dh */1400, +                SCREEN_ORIENTATION_LANDSCAPE, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, +                /* horizontalReachability */ false); +    } + +    private void testReachabilityEnabledForTranslucentActivity(int displayWidth, int displayHeight, +            @ScreenOrientation int screenOrientation, float minAspectRatio, +            boolean horizontalReachability) { +        setUpDisplaySizeWithApp(displayWidth, displayHeight);          mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);          final LetterboxConfiguration config = mWm.mLetterboxConfiguration;          config.setTranslucentLetterboxingOverrideEnabled(true);          config.setLetterboxVerticalPositionMultiplier(0.5f);          config.setIsVerticalReachabilityEnabled(true); +        config.setLetterboxHorizontalPositionMultiplier(0.5f); +        config.setIsHorizontalReachabilityEnabled(true);          // Opaque activity -        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); +        prepareMinAspectRatio(mActivity, minAspectRatio, screenOrientation);          addWindowToActivity(mActivity);          mActivity.mRootWindowContainer.performSurfacePlacement(); @@ -302,7 +263,7 @@ public class SizeCompatTests extends WindowTestsBase {          final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)                  .setActivityTheme(android.R.style.Theme_Translucent)                  .setLaunchedFromUid(mActivity.getUid()) -                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) +                .setScreenOrientation(screenOrientation)                  .build();          mTask.addChild(translucentActivity); @@ -324,39 +285,78 @@ public class SizeCompatTests extends WindowTestsBase {          final Runnable checkIsTop = () -> assertThat(                  innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);          final Runnable checkIsBottom = () -> assertThat( -                innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500); -        final Runnable checkIsCentered = () -> assertThat( +                innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(displayHeight); +        final Runnable checkIsLeft = () -> assertThat( +                innerBoundsOf.apply(translucentActivity).left).isEqualTo(0); +        final Runnable checkIsRight = () -> assertThat( +                innerBoundsOf.apply(translucentActivity).right).isEqualTo(displayWidth); +        final Runnable checkIsHorizontallyCentered = () -> assertThat( +                innerBoundsOf.apply(translucentActivity).left > 0 +                        && innerBoundsOf.apply(translucentActivity).right < displayWidth).isTrue(); +        final Runnable checkIsVerticallyCentered = () -> assertThat(                  innerBoundsOf.apply(translucentActivity).top > 0 -                        && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue(); - -        final Consumer<Integer> doubleClick = -                (Integer y) -> { -                    mActivity.mLetterboxUiController.handleVerticalDoubleTap(y); -                    mActivity.mRootWindowContainer.performSurfacePlacement(); -                }; - -        // Initial state -        checkIsCentered.run(); - -        // Double-click top -        doubleClick.accept(/* y */ 10); -        checkLetterboxPositions.run(); -        checkIsTop.run(); - -        // Double-click bottom -        doubleClick.accept(/* y */ 1990); -        checkLetterboxPositions.run(); -        checkIsCentered.run(); - -        // Double-click bottom -        doubleClick.accept(/* y */ 1990); -        checkLetterboxPositions.run(); -        checkIsBottom.run(); - -        // Double-click top -        doubleClick.accept(/* y */ 10); -        checkLetterboxPositions.run(); -        checkIsCentered.run(); +                        && innerBoundsOf.apply(translucentActivity).bottom < displayHeight) +                .isTrue(); + +        if (horizontalReachability) { +            final Consumer<Integer> doubleClick = +                    (Integer x) -> { +                        mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x); +                        mActivity.mRootWindowContainer.performSurfacePlacement(); +                    }; + +            // Initial state +            checkIsHorizontallyCentered.run(); + +            // Double-click left +            doubleClick.accept(/* x */ 10); +            checkLetterboxPositions.run(); +            checkIsLeft.run(); + +            // Double-click right +            doubleClick.accept(/* x */ displayWidth - 100); +            checkLetterboxPositions.run(); +            checkIsHorizontallyCentered.run(); + +            // Double-click right +            doubleClick.accept(/* x */ displayWidth - 100); +            checkLetterboxPositions.run(); +            checkIsRight.run(); + +            // Double-click left +            doubleClick.accept(/* x */ 10); +            checkLetterboxPositions.run(); +            checkIsHorizontallyCentered.run(); +        } else { +            final Consumer<Integer> doubleClick = +                    (Integer y) -> { +                        mActivity.mLetterboxUiController.handleVerticalDoubleTap(y); +                        mActivity.mRootWindowContainer.performSurfacePlacement(); +                    }; + +            // Initial state +            checkIsVerticallyCentered.run(); + +            // Double-click top +            doubleClick.accept(/* y */ 10); +            checkLetterboxPositions.run(); +            checkIsTop.run(); + +            // Double-click bottom +            doubleClick.accept(/* y */ displayHeight - 100); +            checkLetterboxPositions.run(); +            checkIsVerticallyCentered.run(); + +            // Double-click bottom +            doubleClick.accept(/* y */ displayHeight - 100); +            checkLetterboxPositions.run(); +            checkIsBottom.run(); + +            // Double-click top +            doubleClick.accept(/* y */ 10); +            checkLetterboxPositions.run(); +            checkIsVerticallyCentered.run(); +        }      }      @Test @@ -2143,7 +2143,7 @@ public class SizeCompatTests extends WindowTestsBase {          final Rect afterBounds = mActivity.getBounds();          final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();          assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW, -                actualAspectRatio, 0.001f); +                actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);          assertTrue(mActivity.areBoundsLetterboxed());      } @@ -2179,7 +2179,7 @@ public class SizeCompatTests extends WindowTestsBase {          // default letterbox aspect ratio for multi-window.          final Rect afterBounds = mActivity.getBounds();          final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width(); -        assertEquals(minAspectRatio, actualAspectRatio, 0.001f); +        assertEquals(minAspectRatio, actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);          assertTrue(mActivity.areBoundsLetterboxed());      } @@ -2487,7 +2487,7 @@ public class SizeCompatTests extends WindowTestsBase {          final float afterAspectRatio =                  (float) Math.max(width, height) / (float) Math.min(width, height); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2512,7 +2512,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2537,7 +2537,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2563,7 +2563,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2589,7 +2589,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2629,7 +2629,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * screenHeight / getExpectedSplitSize(screenWidth);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);          assertFalse(activity.areBoundsLetterboxed());      } @@ -2670,7 +2670,7 @@ public class SizeCompatTests extends WindowTestsBase {          float expectedAspectRatio = 1f * screenWidth / getExpectedSplitSize(screenHeight);          final Rect afterBounds = activity.getBounds();          final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height(); -        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); +        assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);          assertFalse(activity.areBoundsLetterboxed());      } @@ -2847,9 +2847,8 @@ public class SizeCompatTests extends WindowTestsBase {          assertFitted();          // Check that the display aspect ratio is used by the app.          final float targetMinAspectRatio = 1f * displayHeight / displayWidth; -        final float delta = 0.01f;          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(mActivity.getBounds()), delta); +                .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2883,9 +2882,8 @@ public class SizeCompatTests extends WindowTestsBase {          assertFitted();          // Check that the display aspect ratio is used by the app.          final float targetMinAspectRatio = 1f * displayWidth / displayHeight; -        final float delta = 0.01f;          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(mActivity.getBounds()), delta); +                .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2910,9 +2908,8 @@ public class SizeCompatTests extends WindowTestsBase {          assertFitted();          // Check that the display aspect ratio is used by the app.          final float targetMinAspectRatio = 1f * displayHeight / displayWidth; -        final float delta = 0.01f;          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(mActivity.getBounds()), delta); +                .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -2937,9 +2934,8 @@ public class SizeCompatTests extends WindowTestsBase {          assertFitted();          // Check that the display aspect ratio is used by the app.          final float targetMinAspectRatio = 1f * displayWidth / displayHeight; -        final float delta = 0.01f;          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(mActivity.getBounds()), delta); +                .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -3609,6 +3605,32 @@ public class SizeCompatTests extends WindowTestsBase {      }      @Test +    public void testIsHorizontalReachabilityEnabled_portraitDisplayAndApp_true() { +        // Portrait display +        setUpDisplaySizeWithApp(1400, 1600); +        mActivity.mWmService.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true); + +        // 16:9f unresizable portrait app +        prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, +                SCREEN_ORIENTATION_PORTRAIT); + +        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled()); +    } + +    @Test +    public void testIsVerticalReachabilityEnabled_landscapeDisplayAndApp_true() { +        // Landscape display +        setUpDisplaySizeWithApp(1600, 1500); +        mActivity.mWmService.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true); + +        // 16:9f unresizable landscape app +        prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, +                SCREEN_ORIENTATION_LANDSCAPE); + +        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled()); +    } + +    @Test      public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {          setUpDisplaySizeWithApp(2800, 1000);          mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -4053,6 +4075,32 @@ public class SizeCompatTests extends WindowTestsBase {      }      @Test +    public void testPortraitCloseToSquareDisplayWithTaskbar_notLetterboxed() { +        // Set up portrait close to square display +        setUpDisplaySizeWithApp(2200, 2280); +        final DisplayContent display = mActivity.mDisplayContent; +        // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape +        final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, +                "navbar"); +        final Binder owner = new Binder(); +        navbar.mAttrs.providedInsets = new InsetsFrameProvider[] { +                new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars()) +                        .setInsetsSize(Insets.of(0, 0, 0, 150)) +        }; +        display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs); +        assertTrue(navbar.providesDisplayDecorInsets() +                && display.getDisplayPolicy().updateDecorInsetsInfo()); +        display.sendNewConfiguration(); + +        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + +        // Activity is fullscreen even though orientation is not respected with insets, because +        // the display still matches or is less than the activity aspect ratio +        assertEquals(display.getBounds(), mActivity.getBounds()); +        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); +    } + +    @Test      public void testApplyAspectRatio_activityAlignWithParentAppVertical() {          // The display's app bounds will be (0, 100, 1000, 2350)          final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500) @@ -4275,7 +4323,7 @@ public class SizeCompatTests extends WindowTestsBase {                  .getFixedOrientationLetterboxAspectRatio(parentConfig);          float expected = mActivity.mLetterboxUiController.getSplitScreenAspectRatio(); -        assertEquals(expected, actual, 0.01); +        assertEquals(expected, actual, DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -4671,13 +4719,12 @@ public class SizeCompatTests extends WindowTestsBase {                  .windowConfiguration.getAppBounds());          // Check that aspect ratio of app bounds is equal to the min aspect ratio. -        final float delta = 0.01f;          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(fixedOrientationAppBounds), delta); +                .computeAspectRatio(fixedOrientationAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(minAspectRatioAppBounds), delta); +                .computeAspectRatio(minAspectRatioAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);          assertEquals(targetMinAspectRatio, ActivityRecord -                .computeAspectRatio(sizeCompatAppBounds), delta); +                .computeAspectRatio(sizeCompatAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);      }      @Test @@ -4859,6 +4906,12 @@ public class SizeCompatTests extends WindowTestsBase {                  .build();      } +    static void prepareMinAspectRatio(ActivityRecord activity, float minAspect, +            int screenOrientation) { +        prepareLimitedBounds(activity, -1 /* maxAspect */, minAspect, screenOrientation, +                true /* isUnresizable */); +    } +      static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {          prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);      } @@ -4873,11 +4926,17 @@ public class SizeCompatTests extends WindowTestsBase {          prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable);      } +    static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, +            int screenOrientation, boolean isUnresizable) { +        prepareLimitedBounds(activity, maxAspect, -1 /* minAspect */, screenOrientation, +                isUnresizable); +    } +      /** -     * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed -     * orientation, and/or whether it is resizable. +     * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, minAspect, +     * fixed orientation, and/or whether it is resizable.       */ -    static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, +    static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, float minAspect,              int screenOrientation, boolean isUnresizable) {          activity.info.resizeMode = isUnresizable                  ? RESIZE_MODE_UNRESIZEABLE @@ -4892,6 +4951,9 @@ public class SizeCompatTests extends WindowTestsBase {          if (maxAspect >= 0) {              activity.info.setMaxAspectRatio(maxAspect);          } +        if (minAspect >= 0) { +            activity.info.setMinAspectRatio(minAspect); +        }          if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {              activity.info.screenOrientation = screenOrientation;              activity.setRequestedOrientation(screenOrientation); diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java index 6a15b0594428..f1d84cfc636d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java @@ -40,8 +40,10 @@ import android.view.WindowManager;  import androidx.test.ext.junit.rules.ActivityScenarioRule;  import androidx.test.platform.app.InstrumentationRegistry; +import com.android.server.wm.utils.CommonUtils;  import com.android.window.flags.Flags; +import org.junit.After;  import org.junit.Before;  import org.junit.Rule;  import org.junit.Test; @@ -77,6 +79,11 @@ public class TrustedOverlayTests {          });      } +    @After +    public void tearDown() { +        CommonUtils.waitUntilActivityRemoved(mActivity); +    } +      @RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)      @Test      public void setTrustedOverlayInputWindow() throws InterruptedException { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 4da519c212df..c972e518e46d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -240,6 +240,22 @@ public class WindowManagerServiceTests extends WindowTestsBase {      }      @Test +    public void testTrackOverlayWindow() { +        final WindowProcessController wpc = mSystemServicesTestRule.addProcess( +                "pkgName", "processName", 1000 /* pid */, Process.SYSTEM_UID); +        final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid); +        spyOn(session); +        assertTrue(session.mCanAddInternalSystemWindow); +        final WindowSurfaceController winSurface = mock(WindowSurfaceController.class); +        session.onWindowSurfaceVisibilityChanged(winSurface, true /* visible */, +                LayoutParams.TYPE_PHONE); +        verify(session).setHasOverlayUi(true); +        session.onWindowSurfaceVisibilityChanged(winSurface, false /* visible */, +                LayoutParams.TYPE_PHONE); +        verify(session).setHasOverlayUi(false); +    } + +    @Test      public void testRelayoutExitingWindow() {          final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");          final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d99abe882405..5d99acd87dd3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4803,12 +4803,51 @@ public class CarrierConfigManager {           */          public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string"; +        /** +         * Indicates the supported protocol version in the parameter entitlement_version. +         * The default value is 2. The possible value is 2 and 8. +         * +         * Reference: GSMA TS.43-v8 section 2.5 Protocol version control and +         * Table 3. GET Parameters for Entitlement Configuration in section 2.3 +         * HTTP GET method Parameters. +         * @hide +         */ +        public static final String KEY_ENTITLEMENT_VERSION_INT = +                KEY_PREFIX + "entitlement_version_int"; + +        /** +         * Controls the service entitlement status when receiving the VERS characteristic +         * with both version and validity set to -1 or -2. +         * If {@code true}, default service entitlement status is enabled. +         * If {@code false}, default service entitlement status is disabled. +         * +         * Reference: GSMA TS.14-v8 section 2.1, overview +         * @hide +         */ +        public static final String KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL = +                KEY_PREFIX + "default_service_entitlement_status_bool"; + +        /** +         * Indicates if UE can skip service entitlement check when the user turns on Wi-Fi Calling. +         * UE still shows Wi-Fi Calling emergency address update web view when the user clicks +         * "Update Emergency Address" on the WiFi calling setting. +         * +         * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING} +         * is set to this app. +         * @hide +         */ +        public static final String KEY_SKIP_WFC_ACTIVATION_BOOL = +                KEY_PREFIX + "skip_wfc_activation_bool"; +          private static PersistableBundle getDefaults() {              PersistableBundle defaults = new PersistableBundle();              defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");              defaults.putString(KEY_FCM_SENDER_ID_STRING, "");              defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false);              defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false); +            defaults.putBoolean(KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL, false); +            defaults.putBoolean(KEY_SKIP_WFC_ACTIVATION_BOOL, false); +            defaults.putInt(KEY_ENTITLEMENT_VERSION_INT, 2);              return defaults;          }      } diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml index 7e97fa3a8ff1..9b527dc159ee 100644 --- a/tests/BootImageProfileTest/AndroidTest.xml +++ b/tests/BootImageProfileTest/AndroidTest.xml @@ -14,6 +14,7 @@       limitations under the License.  -->  <configuration description="Config for BootImageProfileTest"> +    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />       <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.           furthermore the changes in /data/local.prop don't actually seem to get picked up.      --> diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png Binary files differindex 70e4a7101c7f..443de8edc2d3 100644 --- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png +++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png Binary files differindex 502c1b4499d4..cb69c0e44a03 100644 --- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png +++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png Binary files differindex 591b2fa9608e..1c6d1b3a097d 100644 --- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png +++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png Binary files differindex 0137a853e538..c51da052f4bf 100644 --- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png +++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png Binary files differindex 37a91e1fce53..ab23401bf629 100644 --- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png +++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png diff --git a/tests/Internal/src/com/android/internal/protolog/OWNERS b/tests/Internal/src/com/android/internal/protolog/OWNERS new file mode 100644 index 000000000000..18cf2be9f7df --- /dev/null +++ b/tests/Internal/src/com/android/internal/protolog/OWNERS @@ -0,0 +1,3 @@ +# ProtoLog owners +# Bug component: 1157642 +include platform/development:/tools/winscope/OWNERS  |