diff options
103 files changed, 3035 insertions, 598 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java index 217b8b6bae93..c956bf52ed55 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -17,9 +17,12 @@ package com.android.server.job; import android.annotation.Nullable; +import android.app.job.JobInfo; import android.app.job.JobParameters; import android.util.proto.ProtoOutputStream; +import java.util.List; + /** * JobScheduler local system service interface. * {@hide} Only for use within the system server. @@ -27,6 +30,11 @@ import android.util.proto.ProtoOutputStream; public interface JobSchedulerInternal { /** + * Returns a list of jobs scheduled by the system service for itself. + */ + List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace); + + /** * Cancel the jobs for a given uid (e.g. when app data is cleared) * * @param includeProxiedJobs Include jobs scheduled for this UID by other apps diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index fce75a2a0897..e786342e00da 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -3536,6 +3536,21 @@ public class JobSchedulerService extends com.android.server.SystemService final class LocalService implements JobSchedulerInternal { @Override + public List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace) { + synchronized (mLock) { + final List<JobInfo> ownJobs = new ArrayList<>(); + mJobs.forEachJob(Process.SYSTEM_UID, (job) -> { + if (job.getSourceUid() == Process.SYSTEM_UID + && Objects.equals(job.getNamespace(), namespace) + && "android".equals(job.getSourcePackageName())) { + ownJobs.add(job.getJob()); + } + }); + return ownJobs; + } + } + + @Override public void cancelJobsForUid(int uid, boolean includeProxiedJobs, @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { JobSchedulerService.this.cancelJobsForUid(uid, diff --git a/boot/preloaded-classes b/boot/preloaded-classes index 39b7385b3f1c..b43421def72e 100644 --- a/boot/preloaded-classes +++ b/boot/preloaded-classes @@ -5525,6 +5525,8 @@ android.nfc.NfcAdapter$CreateNdefMessageCallback android.nfc.NfcAdapter android.nfc.NfcControllerAlwaysOnListener android.nfc.NfcManager +android.nfc.NfcServiceManager$ServiceRegisterer +android.nfc.NfcServiceManager android.nfc.Tag$1 android.nfc.Tag android.nfc.TechListParcel$1 diff --git a/core/api/current.txt b/core/api/current.txt index ed81918a63f4..75f57271dacc 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13465,7 +13465,7 @@ package android.credentials { ctor public CreateCredentialException(@NonNull String); method @NonNull public String getType(); field @NonNull public static final String TYPE_INTERRUPTED = "android.credentials.CreateCredentialException.TYPE_INTERRUPTED"; - field @NonNull public static final String TYPE_NO_CREDENTIAL = "android.credentials.CreateCredentialException.TYPE_NO_CREDENTIAL"; + field @NonNull public static final String TYPE_NO_CREATE_OPTIONS = "android.credentials.CreateCredentialException.TYPE_NO_CREATE_OPTIONS"; field @NonNull public static final String TYPE_UNKNOWN = "android.credentials.CreateCredentialException.TYPE_UNKNOWN"; field @NonNull public static final String TYPE_USER_CANCELED = "android.credentials.CreateCredentialException.TYPE_USER_CANCELED"; } @@ -39475,6 +39475,7 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset build(); method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender); method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field); + method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull String, @NonNull android.service.autofill.Field); method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation); method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); @@ -39583,6 +39584,7 @@ package android.service.autofill { method @Nullable public android.content.IntentSender getDelayedFillIntentSender(); method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts(); method public int getFlags(); + method @NonNull public java.util.List<java.lang.String> getHints(); method public int getId(); method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -39611,6 +39613,7 @@ package android.service.autofill { method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.service.autofill.Presentations); method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle); + method @NonNull public android.service.autofill.FillResponse.Builder setDetectedFieldClassifications(@NonNull java.util.Set<android.service.assist.classification.FieldClassification>); method @NonNull public android.service.autofill.FillResponse.Builder setDialogHeader(@NonNull android.widget.RemoteViews); method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...); method @NonNull public android.service.autofill.FillResponse.Builder setFillDialogTriggerIds(@NonNull android.view.autofill.AutofillId...); @@ -40242,6 +40245,7 @@ package android.service.credentials { } public final class BeginCreateCredentialResponse implements android.os.Parcelable { + ctor public BeginCreateCredentialResponse(); method public int describeContents(); method @NonNull public java.util.List<android.service.credentials.CreateEntry> getCreateEntries(); method @Nullable public android.service.credentials.CreateEntry getRemoteCreateEntry(); @@ -40283,6 +40287,7 @@ package android.service.credentials { } public final class BeginGetCredentialResponse implements android.os.Parcelable { + ctor public BeginGetCredentialResponse(); method public int describeContents(); method @NonNull public java.util.List<android.service.credentials.Action> getActions(); method @NonNull public java.util.List<android.service.credentials.Action> getAuthenticationActions(); @@ -40976,7 +40981,7 @@ package android.service.voice { method public void onRequestCompleteVoice(android.service.voice.VoiceInteractionSession.CompleteVoiceRequest); method public void onRequestConfirmation(android.service.voice.VoiceInteractionSession.ConfirmationRequest); method public void onRequestPickOption(android.service.voice.VoiceInteractionSession.PickOptionRequest); - method public void onShow(android.os.Bundle, int); + method public void onShow(@Nullable android.os.Bundle, int); method public void onTaskFinished(android.content.Intent, int); method public void onTaskStarted(android.content.Intent, int); method public void onTrimMemory(int); @@ -44008,6 +44013,7 @@ package android.telephony { field public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241; // 0x2031 field public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245; // 0x2035 field public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242; // 0x2032 + field public static final int IWLAN_TUNNEL_NOT_FOUND = 16390; // 0x4006 field public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055; // 0x2b2f field public static final int IWLAN_USER_UNKNOWN = 9001; // 0x2329 field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index af35d96be2d6..517102741117 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -316,6 +316,30 @@ package android.net.wifi { } +package android.nfc { + + public class NfcFrameworkInitializer { + method public static void registerServiceWrappers(); + method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager); + } + + public class NfcServiceManager { + method @NonNull public android.nfc.NfcServiceManager.ServiceRegisterer getNfcManagerServiceRegisterer(); + } + + public static class NfcServiceManager.ServiceNotFoundException extends java.lang.Exception { + ctor public NfcServiceManager.ServiceNotFoundException(@NonNull String); + } + + public static final class NfcServiceManager.ServiceRegisterer { + method @Nullable public android.os.IBinder get(); + method @NonNull public android.os.IBinder getOrThrow() throws android.nfc.NfcServiceManager.ServiceNotFoundException; + method public void register(@NonNull android.os.IBinder); + method @Nullable public android.os.IBinder tryGet(); + } + +} + package android.os { public class ArtModuleServiceManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 85861c3db466..51e90d28abdc 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2529,6 +2529,7 @@ package android.service.autofill { public final class Dataset implements android.os.Parcelable { method @Nullable public android.content.IntentSender getAuthentication(); + method @Nullable public java.util.ArrayList<java.lang.String> getAutofillDatatypes(); method @Nullable public android.content.ClipData getFieldContent(); method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds(); method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues(); @@ -2549,6 +2550,7 @@ package android.service.autofill { } public final class FillResponse implements android.os.Parcelable { + method @NonNull public java.util.Set<android.service.assist.classification.FieldClassification> getDetectedFieldClassifications(); method public int getFlags(); } @@ -3278,6 +3280,7 @@ package android.view.autofill { field public static final String DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_IGNORE_VIEWS = "autofill_credential_manager_ignore_views"; field public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED = "autofill_dialog_enabled"; field public static final String DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED = "pcc_classification_enabled"; + field public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS = "pcc_classification_hints"; field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; field public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS = "non_autofillable_ime_action_ids"; field public static final String DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW = "package_deny_list_for_unimportant_view"; diff --git a/core/java/android/accessibilityservice/MagnificationConfig.java b/core/java/android/accessibilityservice/MagnificationConfig.java index 486dc500d012..5019aee41254 100644 --- a/core/java/android/accessibilityservice/MagnificationConfig.java +++ b/core/java/android/accessibilityservice/MagnificationConfig.java @@ -107,7 +107,7 @@ public final class MagnificationConfig implements Parcelable { * Returns the activated state of the controlling magnifier. The controlling magnifier can be * activated even if the scale returned by {@link MagnificationConfig#getScale()} equals to 1.0. * - * @return {@code true} if the magnifier is showing on screen, + * @return {@code true} if the magnifier is activated and showing on screen, * {@code false} otherwise. */ public boolean isActivated() { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8395ec65d4b9..54961917a06e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -109,6 +109,8 @@ import android.net.ConnectivityManager; import android.net.Proxy; import android.net.TrafficStats; import android.net.Uri; +import android.nfc.NfcFrameworkInitializer; +import android.nfc.NfcServiceManager; import android.os.AsyncTask; import android.os.Binder; import android.os.BluetoothServiceManager; @@ -8135,6 +8137,7 @@ public final class ActivityThread extends ClientTransactionHandler BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager()); BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> { BinderCallsStats.startForBluetooth(context); }); + NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager()); } private void purgePendingResources() { diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index 5ef29e4e0b5b..a7824a850e59 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -107,9 +107,8 @@ public abstract class ForegroundServiceTypePolicy { * * @hide */ - // TODO (b/254661666): Change to @EnabledAfter(T) @ChangeId - @Disabled + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) @Overridable public static final long FGS_TYPE_NONE_DISABLED_CHANGE_ID = 255038118L; @@ -142,9 +141,8 @@ public abstract class ForegroundServiceTypePolicy { * * @hide */ - // TODO (b/254661666): Change to @EnabledAfter(T) @ChangeId - @Disabled + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) @Overridable public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L; @@ -286,10 +284,7 @@ public abstract class ForegroundServiceTypePolicy { new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION) }, true), - new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { - new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT), - new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA) - }, false) + null ); /** @@ -1058,7 +1053,7 @@ public abstract class ForegroundServiceTypePolicy { if (policy.isTypeDisabled(callerUid)) { return FGS_TYPE_POLICY_CHECK_DISABLED; } - int permissionResult = PERMISSION_DENIED; + int permissionResult = PERMISSION_GRANTED; // Do we have the permission to start FGS with this type. if (policy.mAllOfPermissions != null) { permissionResult = policy.mAllOfPermissions.checkPermissions(context, diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 9e310110d935..f74be22569f0 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -940,7 +940,7 @@ public class StatusBarManager { * @param tileLabel label of the tile to show to the user. * @param icon icon to use in the tile shown to the user. * @param resultExecutor an executor to run the callback on - * @param resultCallback callback to indicate the {@link RequestResult}. + * @param resultCallback callback to indicate the result of the request. * * @see android.service.quicksettings.TileService */ diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 0365f8c0548c..64538ec132de 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -157,7 +157,7 @@ import android.net.vcn.VcnManager; import android.net.wifi.WifiFrameworkInitializer; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; -import android.nfc.NfcManager; +import android.nfc.NfcFrameworkInitializer; import android.ondevicepersonalization.OnDevicePersonalizationFrameworkInitializer; import android.os.BatteryManager; import android.os.BatteryStats; @@ -484,13 +484,6 @@ public final class SystemServiceRegistry { return new BatteryManager(ctx, stats, registrar); }}); - registerService(Context.NFC_SERVICE, NfcManager.class, - new CachedServiceFetcher<NfcManager>() { - @Override - public NfcManager createService(ContextImpl ctx) { - return new NfcManager(ctx); - }}); - registerService(Context.DROPBOX_SERVICE, DropBoxManager.class, new CachedServiceFetcher<DropBoxManager>() { @Override @@ -1589,6 +1582,7 @@ public final class SystemServiceRegistry { JobSchedulerFrameworkInitializer.registerServiceWrappers(); BlobStoreManagerFrameworkInitializer.initialize(); BluetoothFrameworkInitializer.registerServiceWrappers(); + NfcFrameworkInitializer.registerServiceWrappers(); TelephonyFrameworkInitializer.registerServiceWrappers(); AppSearchManagerFrameworkInitializer.initialize(); HealthServicesInitializer.registerServiceWrappers(); diff --git a/core/java/android/credentials/CreateCredentialException.java b/core/java/android/credentials/CreateCredentialException.java index 84cc9a84b23c..c3440043a4c0 100644 --- a/core/java/android/credentials/CreateCredentialException.java +++ b/core/java/android/credentials/CreateCredentialException.java @@ -40,13 +40,13 @@ public class CreateCredentialException extends Exception { "android.credentials.CreateCredentialException.TYPE_UNKNOWN"; /** - * The error type value for when no credential is available for the given {@link - * CredentialManager#createCredential(CreateCredentialRequest, Activity, + * The error type value for when no create options are available from any provider(s), + * for the given {@link CredentialManager#createCredential(CreateCredentialRequest, Activity, * CancellationSignal, Executor, OutcomeReceiver)} request. */ @NonNull - public static final String TYPE_NO_CREDENTIAL = - "android.credentials.CreateCredentialException.TYPE_NO_CREDENTIAL"; + public static final String TYPE_NO_CREATE_OPTIONS = + "android.credentials.CreateCredentialException.TYPE_NO_CREATE_OPTIONS"; /** * The error type value for when the user intentionally cancelled the request. * diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 6dc80cf4c374..1bb44af81cec 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -43,7 +43,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.Log; import java.io.IOException; @@ -426,6 +425,7 @@ public final class NfcAdapter { // recovery @UnsupportedAppUsage static INfcAdapter sService; + static NfcServiceManager.ServiceRegisterer sServiceRegisterer; static INfcTag sTagService; static INfcCardEmulation sCardEmulationService; static INfcFCardEmulation sNfcFCardEmulationService; @@ -624,6 +624,12 @@ public final class NfcAdapter { Log.v(TAG, "this device does not have NFC support"); throw new UnsupportedOperationException(); } + NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager(); + if (manager == null) { + Log.e(TAG, "NfcServiceManager is null"); + throw new UnsupportedOperationException(); + } + sServiceRegisterer = manager.getNfcManagerServiceRegisterer(); sService = getServiceInterface(); if (sService == null) { Log.e(TAG, "could not retrieve NFC service"); @@ -665,7 +671,7 @@ public final class NfcAdapter { /** get handle to NFC service interface */ private static INfcAdapter getServiceInterface() { /* get a handle to NFC service */ - IBinder b = ServiceManager.getService("nfc"); + IBinder b = sServiceRegisterer.get(); if (b == null) { return null; } @@ -695,12 +701,13 @@ public final class NfcAdapter { "context not associated with any application (using a mock context?)"); } - if (getServiceInterface() == null) { - // NFC is not available - return null; + if (sIsInitialized && sServiceRegisterer.tryGet() == null) { + synchronized (NfcAdapter.class) { + /* Stale sService pointer */ + if (sIsInitialized) sIsInitialized = false; + } } - - /* use getSystemService() for consistency */ + /* Try to initialize the service */ NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); if (manager == null) { // NFC not available diff --git a/core/java/android/nfc/NfcFrameworkInitializer.java b/core/java/android/nfc/NfcFrameworkInitializer.java new file mode 100644 index 000000000000..1ab8a1ebd72c --- /dev/null +++ b/core/java/android/nfc/NfcFrameworkInitializer.java @@ -0,0 +1,71 @@ +/* + * 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.nfc; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.SystemServiceRegistry; +import android.content.Context; + +/** + * Class for performing registration for Nfc service. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class NfcFrameworkInitializer { + private NfcFrameworkInitializer() {} + + private static volatile NfcServiceManager sNfcServiceManager; + + /** + * Sets an instance of {@link NfcServiceManager} that allows + * the nfc mainline module to register/obtain nfc binder services. This is called + * by the platform during the system initialization. + * + * @param nfcServiceManager instance of {@link NfcServiceManager} that allows + * the nfc mainline module to register/obtain nfcd binder services. + */ + public static void setNfcServiceManager( + @NonNull NfcServiceManager nfcServiceManager) { + if (sNfcServiceManager != null) { + throw new IllegalStateException("setNfcServiceManager called twice!"); + } + + if (nfcServiceManager == null) { + throw new IllegalArgumentException("nfcServiceManager must not be null"); + } + + sNfcServiceManager = nfcServiceManager; + } + + /** @hide */ + public static NfcServiceManager getNfcServiceManager() { + return sNfcServiceManager; + } + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers NFC service + * to {@link Context}, so that {@link Context#getSystemService} can return them. + * + * @throws IllegalStateException if this is called from anywhere besides + * {@link SystemServiceRegistry} + */ + public static void registerServiceWrappers() { + SystemServiceRegistry.registerContextAwareService(Context.NFC_SERVICE, + NfcManager.class, context -> new NfcManager(context)); + } +} diff --git a/core/java/android/nfc/NfcServiceManager.java b/core/java/android/nfc/NfcServiceManager.java new file mode 100644 index 000000000000..5582f1154cad --- /dev/null +++ b/core/java/android/nfc/NfcServiceManager.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + + +/********************************************************************** + * This file is not a part of the NFC mainline modure * + * *******************************************************************/ + +package android.nfc; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.SystemApi.Client; +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Provides a way to register and obtain the system service binder objects managed by the nfc + * service. + * + * @hide + */ +@SystemApi(client = Client.MODULE_LIBRARIES) +public class NfcServiceManager { + + /** + * @hide + */ + public NfcServiceManager() { + } + + /** + * A class that exposes the methods to register and obtain each system service. + */ + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Register a system server binding object for a service. + */ + public void register(@NonNull IBinder service) { + ServiceManager.addService(mServiceName, service); + } + + /** + * Get the system server binding object for a service. + * + * <p>This blocks until the service instance is ready, + * or a timeout happens, in which case it returns null. + */ + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + + /** + * Get the system server binding object for a service. + * + * <p>This blocks until the service instance is ready, + * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. + */ + @NonNull + public IBinder getOrThrow() throws ServiceNotFoundException { + try { + return ServiceManager.getServiceOrThrow(mServiceName); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new ServiceNotFoundException(mServiceName); + } + } + + /** + * Get the system server binding object for a service. If the specified service is + * not available, it returns null. + */ + @Nullable + public IBinder tryGet() { + return ServiceManager.checkService(mServiceName); + } + } + + /** + * See {@link ServiceRegisterer#getOrThrow}. + * + */ + public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { + /** + * Constructor. + * + * @param name the name of the binder service that cannot be found. + * + */ + public ServiceNotFoundException(@NonNull String name) { + super(name); + } + } + + /** + * Returns {@link ServiceRegisterer} for the "nfc" service. + */ + @NonNull + public ServiceRegisterer getNfcManagerServiceRegisterer() { + return new ServiceRegisterer(Context.NFC_SERVICE); + } +} diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 7ad1735210c5..810bd636de07 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -34,7 +34,6 @@ import static android.system.OsConstants.S_IWOTH; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.TestApi; -import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ContentProvider; @@ -46,7 +45,6 @@ import android.system.Os; import android.system.OsConstants; import android.system.StructStat; import android.util.Log; -import android.util.Slog; import dalvik.system.CloseGuard; import dalvik.system.VMRuntime; @@ -331,17 +329,6 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { - if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0 - && (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0) - && file.exists()) { - String packageName = ActivityThread.currentApplication().getApplicationContext() - .getPackageName(); - Slog.wtfQuiet(TAG, "ParcelFileDescriptor.open is called with w without t or a or r, " - + "which will have a different behavior beginning in Android Q." - + "\nPackage Name: " + packageName + "\nMode: " + mode - + "\nFilename: " + file.getPath()); - } - final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC); int realMode = S_IRWXU | S_IRWXG; diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e9f099accac1..0fa5e3e3b323 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -120,6 +120,8 @@ public final class Dataset implements Parcelable { private final ArrayList<InlinePresentation> mFieldInlinePresentations; private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations; private final ArrayList<DatasetFieldFilter> mFieldFilters; + private final ArrayList<String> mAutofillDatatypes; + @Nullable private final ClipData mFieldContent; private final RemoteViews mPresentation; private final RemoteViews mDialogPresentation; @@ -143,6 +145,14 @@ public final class Dataset implements Parcelable { mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mAuthentication = builder.mAuthentication; mId = builder.mId; + mAutofillDatatypes = builder.mAutofillDatatypes; + } + + /** @hide */ + @TestApi + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + public @Nullable ArrayList<String> getAutofillDatatypes() { + return mAutofillDatatypes; } /** @hide */ @@ -293,6 +303,7 @@ public final class Dataset implements Parcelable { private ArrayList<InlinePresentation> mFieldInlinePresentations; private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations; private ArrayList<DatasetFieldFilter> mFieldFilters; + private ArrayList<String> mAutofillDatatypes; @Nullable private ClipData mFieldContent; private RemoteViews mPresentation; private RemoteViews mDialogPresentation; @@ -922,6 +933,55 @@ public final class Dataset implements Parcelable { } /** + * Adds a field to this Dataset with a specific type and no + * AutofillId. This is used to send back Field information + * when Autofilling with platform detections is on. + * Platform detections are on when receiving a populated list from + * FillRequest#getHints(). + * + * Populate every field/type known for this user for this app. + * + * For example, if getHints() contains "username" and "password", + * a new Dataset should be created that calls this method twice, + * one for the username, then another for the password (assuming + * the only one credential pair is found for the user). If a user + * has two credential pairs, then two Datasets should be created, + * and so on. + * + * Using this will remove any data populated with + * setField(@NonNull AutofillId id, @Nullable Field field). + * + * @param hint An autofill hint returned from {@link + * FillRequest#getHints()}. + * + * @param field the fill information about the field. + * + * @throws IllegalStateException if {@link #build()} was already called + * or this builder also contains AutofillId information + * + * @return this builder. + */ + public @NonNull Dataset.Builder setField( + @NonNull String hint, @NonNull Field field) { + throwIfDestroyed(); + + final DatasetFieldFilter filter = field.getDatasetFieldFilter(); + final Presentations presentations = field.getPresentations(); + if (presentations == null) { + setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null, + filter, null); + } else { + setLifeTheUniverseAndEverything(hint, field.getValue(), + presentations.getMenuPresentation(), + presentations.getInlinePresentation(), + presentations.getInlineTooltipPresentation(), filter, + presentations.getDialogPresentation()); + } + + return this; + } + + /** * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an * {@link InlinePresentation} to visualize it as an inline suggestion. * @@ -958,6 +1018,32 @@ public final class Dataset implements Parcelable { return this; } + private void setLifeTheUniverseAndEverything(String datatype, + @Nullable AutofillValue value, + @Nullable RemoteViews presentation, + @Nullable InlinePresentation inlinePresentation, + @Nullable InlinePresentation tooltip, + @Nullable DatasetFieldFilter filter, + @Nullable RemoteViews dialogPresentation) { + if (mAutofillDatatypes == null) { + mFieldValues = new ArrayList<>(); + mFieldPresentations = new ArrayList<>(); + mFieldDialogPresentations = new ArrayList<>(); + mFieldInlinePresentations = new ArrayList<>(); + mFieldInlineTooltipPresentations = new ArrayList<>(); + mFieldFilters = new ArrayList<>(); + mAutofillDatatypes = new ArrayList<>(); + mFieldIds = null; + } + mFieldValues.add(value); + mFieldPresentations.add(presentation); + mFieldDialogPresentations.add(dialogPresentation); + mFieldInlinePresentations.add(inlinePresentation); + mFieldInlineTooltipPresentations.add(tooltip); + mFieldFilters.add(filter); + mAutofillDatatypes.add(datatype); + } + private void setLifeTheUniverseAndEverything(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @@ -984,6 +1070,7 @@ public final class Dataset implements Parcelable { mFieldInlinePresentations = new ArrayList<>(); mFieldInlineTooltipPresentations = new ArrayList<>(); mFieldFilters = new ArrayList<>(); + mAutofillDatatypes = null; } mFieldIds.add(id); mFieldValues.add(value); @@ -1007,9 +1094,14 @@ public final class Dataset implements Parcelable { public @NonNull Dataset build() { throwIfDestroyed(); mDestroyed = true; - if (mFieldIds == null) { + if (mFieldIds == null && mAutofillDatatypes == null) { throw new IllegalStateException("at least one value must be set"); } + if (mFieldIds != null && mAutofillDatatypes != null) { + if (mFieldIds.size() > 0 && mAutofillDatatypes.size() > 0) { + throw new IllegalStateException("both field and datatype were populated"); + } + } if (mFieldContent != null) { if (mFieldIds.size() > 1) { throw new IllegalStateException( @@ -1051,6 +1143,7 @@ public final class Dataset implements Parcelable { parcel.writeTypedList(mFieldInlinePresentations, flags); parcel.writeTypedList(mFieldInlineTooltipPresentations, flags); parcel.writeTypedList(mFieldFilters, flags); + parcel.writeStringList(mAutofillDatatypes); parcel.writeParcelable(mFieldContent, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeString(mId); @@ -1081,6 +1174,8 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList<DatasetFieldFilter> filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); + final ArrayList<String> datatypes = + parcel.createStringArrayList(); final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class); final IntentSender authentication = parcel.readParcelable(null, @@ -1114,19 +1209,37 @@ public final class Dataset implements Parcelable { builder.setContent(ids.get(0), fieldContent); } final int inlinePresentationsSize = inlinePresentations.size(); - for (int i = 0; i < ids.size(); i++) { - final AutofillId id = ids.get(i); - final AutofillValue value = values.get(i); - final RemoteViews fieldPresentation = presentations.get(i); - final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); - final InlinePresentation fieldInlinePresentation = - i < inlinePresentationsSize ? inlinePresentations.get(i) : null; - final InlinePresentation fieldInlineTooltipPresentation = - i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; - final DatasetFieldFilter filter = filters.get(i); - builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, - fieldInlinePresentation, fieldInlineTooltipPresentation, filter, - fieldDialogPresentation); + + if (ids.size() == 0 && datatypes.size() > 0) { + for (int i = 0; i < ids.size(); i++) { + final String datatype = datatypes.get(i); + final AutofillValue value = values.get(i); + final RemoteViews fieldPresentation = presentations.get(i); + final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); + final InlinePresentation fieldInlinePresentation = + i < inlinePresentationsSize ? inlinePresentations.get(i) : null; + final InlinePresentation fieldInlineTooltipPresentation = + i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; + final DatasetFieldFilter filter = filters.get(i); + builder.setLifeTheUniverseAndEverything( + datatype, value, fieldPresentation, fieldInlinePresentation, + fieldInlineTooltipPresentation, filter, fieldDialogPresentation); + } + } else { + for (int i = 0; i < ids.size(); i++) { + final AutofillId id = ids.get(i); + final AutofillValue value = values.get(i); + final RemoteViews fieldPresentation = presentations.get(i); + final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); + final InlinePresentation fieldInlinePresentation = + i < inlinePresentationsSize ? inlinePresentations.get(i) : null; + final InlinePresentation fieldInlineTooltipPresentation = + i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; + final DatasetFieldFilter filter = filters.get(i); + builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, + fieldInlinePresentation, fieldInlineTooltipPresentation, filter, + fieldDialogPresentation); + } } builder.setAuthentication(authentication); builder.setId(datasetId); diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index eb5e893416c7..4a848dd86463 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -141,6 +141,19 @@ public final class FillRequest implements Parcelable { private final @NonNull List<FillContext> mFillContexts; /** + * Sends a list of datatypes for the Autofill Provider. + * + * If this is populated, Autofill Provider should return data + * for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants + * + * This is populated if the platform's field detection is enabled. + */ + private final @NonNull List<String> mHints; + + /** * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. * @@ -196,6 +209,7 @@ public final class FillRequest implements Parcelable { private void onConstructed() { Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts"); + Preconditions.checkCollectionElementsNotNull(mHints, "hints"); } @@ -269,6 +283,11 @@ public final class FillRequest implements Parcelable { * <p><b>Note:</b> Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also * include contexts from requests whose {@link SaveInfo} had the * {@link SaveInfo#FLAG_DELAY_SAVE} flag. + * @param hints + * Autofill Provider should return data for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants * @param clientState * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. @@ -312,6 +331,7 @@ public final class FillRequest implements Parcelable { public FillRequest( int id, @NonNull List<FillContext> fillContexts, + @NonNull List<String> hints, @Nullable Bundle clientState, @RequestFlags int flags, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @@ -320,6 +340,9 @@ public final class FillRequest implements Parcelable { this.mFillContexts = fillContexts; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mFillContexts); + this.mHints = hints; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHints); this.mClientState = clientState; this.mFlags = flags; @@ -360,6 +383,17 @@ public final class FillRequest implements Parcelable { } /** + * Autofill Provider should return data for the autofill hints requested here, + * even though the Autofill Provider may not have detected these types. + * The hints would be part of HintConstants: + * https://developer.android.com/reference/androidx/autofill/HintConstants + */ + @DataClass.Generated.Member + public @NonNull List<String> getHints() { + return mHints; + } + + /** * Gets the latest client state bundle set by the service in a * {@link FillResponse.Builder#setClientState(Bundle) fill response}. * @@ -433,6 +467,7 @@ public final class FillRequest implements Parcelable { return "FillRequest { " + "id = " + mId + ", " + "fillContexts = " + mFillContexts + ", " + + "hints = " + mHints + ", " + "clientState = " + mClientState + ", " + "flags = " + requestFlagsToString(mFlags) + ", " + "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " + @@ -447,12 +482,13 @@ public final class FillRequest implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mClientState != null) flg |= 0x4; - if (mInlineSuggestionsRequest != null) flg |= 0x10; - if (mDelayedFillIntentSender != null) flg |= 0x20; + if (mClientState != null) flg |= 0x8; + if (mInlineSuggestionsRequest != null) flg |= 0x20; + if (mDelayedFillIntentSender != null) flg |= 0x40; dest.writeByte(flg); dest.writeInt(mId); dest.writeParcelableList(mFillContexts, flags); + dest.writeStringList(mHints); if (mClientState != null) dest.writeBundle(mClientState); dest.writeInt(mFlags); if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags); @@ -474,15 +510,20 @@ public final class FillRequest implements Parcelable { int id = in.readInt(); List<FillContext> fillContexts = new ArrayList<>(); in.readParcelableList(fillContexts, FillContext.class.getClassLoader()); - Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle(); + List<String> hints = new ArrayList<>(); + in.readStringList(hints); + Bundle clientState = (flg & 0x8) == 0 ? null : in.readBundle(); int flags = in.readInt(); - InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); - IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR); + InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x20) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); + IntentSender delayedFillIntentSender = (flg & 0x40) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR); this.mId = id; this.mFillContexts = fillContexts; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mFillContexts); + this.mHints = hints; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHints); this.mClientState = clientState; this.mFlags = flags; @@ -517,10 +558,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1675460688829L, + time = 1675711417112L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 385b0aa8b867..fa7ace3bbe0d 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -34,6 +34,7 @@ import android.content.pm.ParceledListSlice; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.service.assist.classification.FieldClassification; import android.view.autofill.AutofillId; import android.widget.RemoteViews; @@ -45,6 +46,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Response for an {@link @@ -113,6 +115,7 @@ public final class FillResponse implements Parcelable { private final @StringRes int mServiceDisplayNameResourceId; private final boolean mShowFillDialogIcon; private final boolean mShowSaveDialogIcon; + private final @Nullable FieldClassification[] mDetectedFieldTypes; private FillResponse(@NonNull Builder builder) { mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; @@ -140,6 +143,14 @@ public final class FillResponse implements Parcelable { mServiceDisplayNameResourceId = builder.mServiceDisplayNameResourceId; mShowFillDialogIcon = builder.mShowFillDialogIcon; mShowSaveDialogIcon = builder.mShowSaveDialogIcon; + mDetectedFieldTypes = builder.mDetectedFieldTypes; + } + + /** @hide */ + @TestApi + @NonNull + public Set<FieldClassification> getDetectedFieldClassifications() { + return Set.of(mDetectedFieldTypes); } /** @hide */ @@ -312,6 +323,28 @@ public final class FillResponse implements Parcelable { private int mServiceDisplayNameResourceId; private boolean mShowFillDialogIcon = true; private boolean mShowSaveDialogIcon = true; + private FieldClassification[] mDetectedFieldTypes; + + /** + * Adds a new {@link FieldClassification} to this response, to + * help the platform provide more accurate detection results. + * + * Call this when a field has been detected with a type. + * + * Altough similiarly named with {@link setFieldClassificationIds}, + * it provides a different functionality - setFieldClassificationIds should + * be used when a field is only suspected to be Autofillable. + * This method should be used when a field is certainly Autofillable + * with a certain type. + */ + @NonNull + public Builder setDetectedFieldClassifications( + @NonNull Set<FieldClassification> fieldInfos) { + throwIfDestroyed(); + throwIfDisableAutofillCalled(); + mDetectedFieldTypes = fieldInfos.toArray(new FieldClassification[0]); + return this; + } /** * Triggers a custom UI before autofilling the screen with any data set in this @@ -1122,6 +1155,7 @@ public final class FillResponse implements Parcelable { parcel.writeParcelableArray(mIgnoredIds, flags); parcel.writeLong(mDisableDuration); parcel.writeParcelableArray(mFieldClassificationIds, flags); + parcel.writeParcelableArray(mDetectedFieldTypes, flags); parcel.writeInt(mIconResourceId); parcel.writeInt(mServiceDisplayNameResourceId); parcel.writeBoolean(mShowFillDialogIcon); @@ -1192,6 +1226,12 @@ public final class FillResponse implements Parcelable { builder.setFieldClassificationIds(fieldClassifactionIds); } + final FieldClassification[] detectedFields = + parcel.readParcelableArray(null, FieldClassification.class); + if (detectedFields != null) { + builder.setDetectedFieldClassifications(Set.of(detectedFields)); + } + builder.setIconResourceId(parcel.readInt()); builder.setServiceDisplayNameResourceId(parcel.readInt()); builder.setShowFillDialogIcon(parcel.readBoolean()); diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 5fe1d4f5ca5f..828e466783d1 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -308,7 +308,7 @@ public final class SaveInfo implements Parcelable { * username field, another for password). */ // TODO(b/113281366): improve documentation: add example, document relationship with other - // flagss, etc... + // flags, etc... public static final int FLAG_DELAY_SAVE = 0x4; /** @hide */ @@ -777,17 +777,13 @@ public final class SaveInfo implements Parcelable { /** * Builds a new {@link SaveInfo} instance. * - * @throws IllegalStateException if no - * {@link #Builder(int, AutofillId[]) required ids}, + * If no {@link #Builder(int, AutofillId[]) required ids}, * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE} - * were set + * were set, Save Dialog will only be triggered if platform detection is enabled, which + * is indicated when {@link FillRequest.getHints()} is not empty. */ public SaveInfo build() { throwIfDestroyed(); - Preconditions.checkState( - !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds) - || (mFlags & FLAG_DELAY_SAVE) != 0, - "must have at least one required or optional id or FLAG_DELAYED_SAVE"); mDestroyed = true; return new SaveInfo(this); } diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java index 8ca3a1a2ec99..f0f954dda182 100644 --- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java +++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java @@ -34,6 +34,14 @@ public final class BeginCreateCredentialResponse implements Parcelable { private final @NonNull List<CreateEntry> mCreateEntries; private final @Nullable CreateEntry mRemoteCreateEntry; + /** + * Creates an empty response instance, to be used when there are no {@link CreateEntry} + * to return. + */ + public BeginCreateCredentialResponse() { + this(/*createEntries=*/new ArrayList<>(), /*remoteCreateEntry=*/null); + } + private BeginCreateCredentialResponse(@NonNull Parcel in) { List<CreateEntry> createEntries = new ArrayList<>(); in.readTypedList(createEntries, CreateEntry.CREATOR); @@ -137,13 +145,8 @@ public final class BeginCreateCredentialResponse implements Parcelable { /** * Builds a new instance of {@link BeginCreateCredentialResponse}. - * - * @throws NullPointerException If {@code createEntries} is null. - * @throws IllegalArgumentException If {@code createEntries} is empty. */ public @NonNull BeginCreateCredentialResponse build() { - Preconditions.checkCollectionNotEmpty(mCreateEntries, "createEntries must " - + "not be null, or empty"); return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry); } } diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java index 1df908a80fb4..3d39f6500fcb 100644 --- a/core/java/android/service/credentials/BeginGetCredentialOption.java +++ b/core/java/android/service/credentials/BeginGetCredentialOption.java @@ -39,6 +39,8 @@ import com.android.internal.util.Preconditions; */ @SuppressLint("ParcelNotFinal") public class BeginGetCredentialOption implements Parcelable { + private static final String BUNDLE_ID_KEY = + "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY"; /** * A unique id associated with this request option. */ @@ -129,6 +131,11 @@ public class BeginGetCredentialOption implements Parcelable { mType = Preconditions.checkStringNotEmpty(type, "type must not be empty"); mCandidateQueryData = requireNonNull( candidateQueryData, "candidateQueryData must not be null"); + addIdToBundle(); + } + + private void addIdToBundle() { + mCandidateQueryData.putString(BUNDLE_ID_KEY, mId); } private BeginGetCredentialOption(@NonNull Parcel in) { diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java index 0f64c63d56fb..3652742d4ff4 100644 --- a/core/java/android/service/credentials/BeginGetCredentialResponse.java +++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java @@ -44,6 +44,17 @@ public final class BeginGetCredentialResponse implements Parcelable { /** Remote credential entry to get the response from a different device. */ private final @Nullable CredentialEntry mRemoteCredentialEntry; + /** + * Creates an empty response instance, to be used when there are no {@link CredentialEntry}, + * or {@link Action} to return. + */ + public BeginGetCredentialResponse() { + this(/*credentialEntries=*/new ArrayList<>(), + /*authenticationActions=*/new ArrayList<>(), + /*actions=*/new ArrayList<>(), + /*remoteCredentialEntry=*/null); + } + private BeginGetCredentialResponse(@NonNull List<CredentialEntry> credentialEntries, @NonNull List<Action> authenticationEntries, @NonNull List<Action> actions, @Nullable CredentialEntry remoteCredentialEntry) { @@ -243,16 +254,8 @@ public final class BeginGetCredentialResponse implements Parcelable { /** * Builds a {@link BeginGetCredentialResponse} instance. - * - * @throws IllegalStateException if {@code credentialEntries}, {@code actions} - * and {@code remoteCredentialEntry} are all null or empty. */ public @NonNull BeginGetCredentialResponse build() { - if (mCredentialEntries.isEmpty() && mActions.isEmpty() - && mRemoteCredentialEntry == null && mAuthenticationEntries.isEmpty()) { - throw new IllegalStateException("must set either an authentication, " - + "credential, action or remote entry"); - } return new BeginGetCredentialResponse(mCredentialEntries, mAuthenticationEntries, mActions, mRemoteCredentialEntry); } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 0d513951a4a7..5778518921ca 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1781,11 +1781,13 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing * in milliseconds of the KeyEvent that triggered Assistant and * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID) - * referring to the device that sent the request. + * referring to the device that sent the request. Starting from Android 14, the system will + * add {@link VoiceInteractionService#KEY_SHOW_SESSION_ID}, the Bundle is not null. But the + * application should handle null case before Android 14. * @param showFlags The show flags originally provided to * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}. */ - public void onShow(Bundle args, int showFlags) { + public void onShow(@Nullable Bundle args, int showFlags) { } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 866301399fd6..7da141bc392a 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -479,6 +479,14 @@ public final class SurfaceControl implements Parcelable { private WeakReference<View> mLocalOwnerView; + // A throwable with the stack filled when this SurfaceControl is released (only if + // sDebugUsageAfterRelease) is enabled + private Throwable mReleaseStack = null; + + // Triggers the stack to be saved when any SurfaceControl in this process is released, which can + // be dumped as additional context + private static volatile boolean sDebugUsageAfterRelease = false; + static GlobalTransactionWrapper sGlobalTransaction; static long sTransactionNestCount = 0; @@ -751,6 +759,11 @@ public final class SurfaceControl implements Parcelable { } mNativeObject = nativeObject; mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0; + if (sDebugUsageAfterRelease && mNativeObject == 0) { + mReleaseStack = new Throwable("Assigned invalid nativeObject"); + } else { + mReleaseStack = null; + } } /** @@ -1246,6 +1259,9 @@ public final class SurfaceControl implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + if (sDebugUsageAfterRelease) { + checkNotReleased(); + } dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); @@ -1262,6 +1278,18 @@ public final class SurfaceControl implements Parcelable { } /** + * Enables additional debug logs to track usage-after-release of all SurfaceControls in this + * process. + * @hide + */ + public static void setDebugUsageAfterRelease(boolean debug) { + if (!Build.isDebuggable()) { + return; + } + sDebugUsageAfterRelease = debug; + } + + /** * Checks whether two {@link SurfaceControl} objects represent the same surface. * * @param other The other object to check @@ -1382,6 +1410,9 @@ public final class SurfaceControl implements Parcelable { mFreeNativeResources.run(); mNativeObject = 0; mNativeHandle = 0; + if (sDebugUsageAfterRelease) { + mReleaseStack = new Throwable("Released"); + } mCloseGuard.close(); synchronized (mChoreographerLock) { if (mChoreographer != null) { @@ -1403,8 +1434,15 @@ public final class SurfaceControl implements Parcelable { } private void checkNotReleased() { - if (mNativeObject == 0) throw new NullPointerException( - "Invalid " + this + ", mNativeObject is null. Have you called release() already?"); + if (mNativeObject == 0) { + if (mReleaseStack != null) { + throw new IllegalStateException("Invalid usage after release of " + this, + mReleaseStack); + } else { + throw new NullPointerException("mNativeObject of " + this + + " is null. Have you called release() already?"); + } + } } /** @@ -2738,7 +2776,7 @@ public final class SurfaceControl implements Parcelable { */ @NonNull public Transaction setFrameRateSelectionPriority(@NonNull SurfaceControl sc, int priority) { - sc.checkNotReleased(); + checkPreconditions(sc); nativeSetFrameRateSelectionPriority(mNativeObject, sc.mNativeObject, priority); return this; } diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index e7c610b8bd8d..864ba92ca887 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -82,6 +82,12 @@ public class AutofillFeatureFlags { "autofill_dialog_enabled"; /** + * Indicates that PCC Autofill detection feature is enabled or not. + */ + public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS = + "pcc_classification_hints"; + + /** * Sets the autofill hints allowed list for the fields that can trigger the fill dialog * feature at Activity starting. * @@ -178,6 +184,22 @@ public class AutofillFeatureFlags { public static final String DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED = "pcc_classification_enabled"; + /** + * Give preference to autofill provider's detection. + * @hide + */ + public static final String DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC = "prefer_provider_over_pcc"; + + + /** + * Use data from secondary source if primary not present . + * For eg: if we prefer PCC over provider, and PCC detection didn't classify a field, however, + * autofill provider did, this flag would decide whether we use that result, and show some + * presentation for that particular field. + * @hide + */ + public static final String DEVICE_CONFIG_PCC_USE_FALLBACK = "pcc_use_fallback"; + // END AUTOFILL PCC CLASSIFICATION FLAGS @@ -190,9 +212,11 @@ public class AutofillFeatureFlags { "autofill_inline_tooltip_first_show_delay"; private static final String DIALOG_HINTS_DELIMITER = ":"; + private static final String PCC_HINTS_DELIMITER = ","; private static final boolean DEFAULT_HAS_FILL_DIALOG_UI_FEATURE = false; private static final String DEFAULT_FILL_DIALOG_ENABLED_HINTS = ""; + private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = ""; // CREDENTIAL MANAGER DEFAULTS @@ -206,7 +230,8 @@ public class AutofillFeatureFlags { // AUTOFILL PCC CLASSIFICATION FLAGS DEFAULTS // Default for whether the pcc classification is enabled for autofill. - private static final boolean DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED = false; + /** @hide */ + public static final boolean DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED = false; // END AUTOFILL PCC CLASSIFICATION FLAGS DEFAULTS @@ -225,6 +250,25 @@ public class AutofillFeatureFlags { } /** + * The list of datatypes that is supported by framework + * detection. + * + * @hide + */ + public static String[] getTypeHintsForProvider() { + final String typeHints = DeviceConfig.getString( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS, + DEFAULT_PCC_FEATURE_PROVIDER_HINTS); + if (TextUtils.isEmpty(typeHints)) { + return new String[0]; + } + + return ArrayUtils.filter(typeHints.split(PCC_HINTS_DELIMITER), String[]::new, + (str) -> !TextUtils.isEmpty(str)); + } + + /** * Gets fill dialog enabled hints. * * @hide diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 136846a7bf61..3f452f8ca2f9 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -124,13 +124,11 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.LinearInterpolator; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CursorAnchorInfo; -import android.view.inputmethod.EditorBoundsInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.TextAppearanceInfo; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationManager; import android.widget.AdapterView.OnItemClickListener; @@ -4667,7 +4665,7 @@ public class Editor { * {@link InputMethodManager#isWatchingCursor(View)} returns false. */ private final class CursorAnchorInfoNotifier implements TextViewPositionListener { - final CursorAnchorInfo.Builder mSelectionInfoBuilder = new CursorAnchorInfo.Builder(); + final CursorAnchorInfo.Builder mCursorAnchorInfoBuilder = new CursorAnchorInfo.Builder(); final Matrix mViewToScreenMatrix = new Matrix(); @Override @@ -4687,165 +4685,21 @@ public class Editor { // Skip if the IME has not requested the cursor/anchor position. final int knownCursorAnchorInfoModes = InputConnection.CURSOR_UPDATE_IMMEDIATE | InputConnection.CURSOR_UPDATE_MONITOR; - if ((mInputMethodState.mUpdateCursorAnchorInfoMode & knownCursorAnchorInfoModes) == 0) { + if ((ims.mUpdateCursorAnchorInfoMode & knownCursorAnchorInfoModes) == 0) { return; } - Layout layout = mTextView.getLayout(); - if (layout == null) { - return; - } - final int filter = mInputMethodState.mUpdateCursorAnchorInfoFilter; - boolean includeEditorBounds = - (filter & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0; - boolean includeCharacterBounds = - (filter & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0; - boolean includeInsertionMarker = - (filter & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0; - boolean includeVisibleLineBounds = - (filter & InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS) != 0; - boolean includeTextAppearance = - (filter & InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE) != 0; - boolean includeAll = - (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker - && !includeVisibleLineBounds && !includeTextAppearance); - - includeEditorBounds |= includeAll; - includeCharacterBounds |= includeAll; - includeInsertionMarker |= includeAll; - includeVisibleLineBounds |= includeAll; - includeTextAppearance |= includeAll; - - final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder; - builder.reset(); - - final int selectionStart = mTextView.getSelectionStart(); - builder.setSelectionRange(selectionStart, mTextView.getSelectionEnd()); - - // Construct transformation matrix from view local coordinates to screen coordinates. - mViewToScreenMatrix.reset(); - mTextView.transformMatrixToGlobal(mViewToScreenMatrix); - builder.setMatrix(mViewToScreenMatrix); - - if (includeEditorBounds) { - final RectF editorBounds = new RectF(); - editorBounds.set(0 /* left */, 0 /* top */, - mTextView.getWidth(), mTextView.getHeight()); - final RectF handwritingBounds = new RectF( - -mTextView.getHandwritingBoundsOffsetLeft(), - -mTextView.getHandwritingBoundsOffsetTop(), - mTextView.getWidth() + mTextView.getHandwritingBoundsOffsetRight(), - mTextView.getHeight() + mTextView.getHandwritingBoundsOffsetBottom()); - EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder(); - EditorBoundsInfo editorBoundsInfo = boundsBuilder.setEditorBounds(editorBounds) - .setHandwritingBounds(handwritingBounds).build(); - builder.setEditorBoundsInfo(editorBoundsInfo); - } - - if (includeCharacterBounds || includeInsertionMarker || includeVisibleLineBounds) { - final float viewportToContentHorizontalOffset = - mTextView.viewportToContentHorizontalOffset(); - final float viewportToContentVerticalOffset = - mTextView.viewportToContentVerticalOffset(); - final boolean isTextTransformed = (mTextView.getTransformationMethod() != null - && mTextView.getTransformed() instanceof OffsetMapping); - if (includeCharacterBounds && !isTextTransformed) { - final CharSequence text = mTextView.getText(); - if (text instanceof Spannable) { - final Spannable sp = (Spannable) text; - int composingTextStart = EditableInputConnection.getComposingSpanStart(sp); - int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp); - if (composingTextEnd < composingTextStart) { - final int temp = composingTextEnd; - composingTextEnd = composingTextStart; - composingTextStart = temp; - } - final boolean hasComposingText = - (0 <= composingTextStart) && (composingTextStart - < composingTextEnd); - if (hasComposingText) { - final CharSequence composingText = text.subSequence(composingTextStart, - composingTextEnd); - builder.setComposingText(composingTextStart, composingText); - mTextView.populateCharacterBounds(builder, composingTextStart, - composingTextEnd, viewportToContentHorizontalOffset, - viewportToContentVerticalOffset); - } - } - } - if (includeInsertionMarker) { - // Treat selectionStart as the insertion point. - if (0 <= selectionStart) { - final int offsetTransformed = mTextView.originalToTransformed( - selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR); - final int line = layout.getLineForOffset(offsetTransformed); - final float insertionMarkerX = - layout.getPrimaryHorizontal(offsetTransformed) - + viewportToContentHorizontalOffset; - final float insertionMarkerTop = layout.getLineTop(line) - + viewportToContentVerticalOffset; - final float insertionMarkerBaseline = layout.getLineBaseline(line) - + viewportToContentVerticalOffset; - final float insertionMarkerBottom = - layout.getLineBottom(line, /* includeLineSpacing= */ false) - + viewportToContentVerticalOffset; - final boolean isTopVisible = mTextView - .isPositionVisible(insertionMarkerX, insertionMarkerTop); - final boolean isBottomVisible = mTextView - .isPositionVisible(insertionMarkerX, insertionMarkerBottom); - int insertionMarkerFlags = 0; - if (isTopVisible || isBottomVisible) { - insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; - } - if (!isTopVisible || !isBottomVisible) { - insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; - } - if (layout.isRtlCharAt(offsetTransformed)) { - insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL; - } - builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, - insertionMarkerBaseline, insertionMarkerBottom, - insertionMarkerFlags); - } - } + final CursorAnchorInfo cursorAnchorInfo = + mTextView.getCursorAnchorInfo(ims.mUpdateCursorAnchorInfoFilter, + mCursorAnchorInfoBuilder, mViewToScreenMatrix); - if (includeVisibleLineBounds) { - final Rect visibleRect = new Rect(); - if (mTextView.getContentVisibleRect(visibleRect)) { - // Subtract the viewportToContentVerticalOffset to convert the view - // coordinates to layout coordinates. - final float visibleTop = - visibleRect.top - viewportToContentVerticalOffset; - final float visibleBottom = - visibleRect.bottom - viewportToContentVerticalOffset; - final int firstLine = - layout.getLineForVertical((int) Math.floor(visibleTop)); - final int lastLine = - layout.getLineForVertical((int) Math.ceil(visibleBottom)); - - for (int line = firstLine; line <= lastLine; ++line) { - final float left = layout.getLineLeft(line) - + viewportToContentHorizontalOffset; - final float top = layout.getLineTop(line) - + viewportToContentVerticalOffset; - final float right = layout.getLineRight(line) - + viewportToContentHorizontalOffset; - final float bottom = layout.getLineBottom(line, false) - + viewportToContentVerticalOffset; - builder.addVisibleLineBounds(left, top, right, bottom); - } - } - } - } + if (cursorAnchorInfo != null) { + imm.updateCursorAnchorInfo(mTextView, cursorAnchorInfo); - if (includeTextAppearance) { - builder.setTextAppearanceInfo(TextAppearanceInfo.createFromTextView(mTextView)); + // Drop the immediate flag if any. + mInputMethodState.mUpdateCursorAnchorInfoMode &= + ~InputConnection.CURSOR_UPDATE_IMMEDIATE; } - imm.updateCursorAnchorInfo(mTextView, builder.build()); - - // Drop the immediate flag if any. - mInputMethodState.mUpdateCursorAnchorInfoMode &= - ~InputConnection.CURSOR_UPDATE_IMMEDIATE; } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 77df1f1ab2d1..626df4857c13 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -196,6 +196,7 @@ import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.DeleteGesture; import android.view.inputmethod.DeleteRangeGesture; +import android.view.inputmethod.EditorBoundsInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -209,6 +210,7 @@ import android.view.inputmethod.PreviewableHandwritingGesture; import android.view.inputmethod.RemoveSpaceGesture; import android.view.inputmethod.SelectGesture; import android.view.inputmethod.SelectRangeGesture; +import android.view.inputmethod.TextAppearanceInfo; import android.view.inputmethod.TextBoundsInfo; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; @@ -13658,7 +13660,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @return true if at least part of the text content is visible; false if the text content is * completely clipped or translated out of the visible area. */ - boolean getContentVisibleRect(Rect rect) { + private boolean getContentVisibleRect(Rect rect) { if (!getLocalVisibleRect(rect)) { return false; } @@ -13744,6 +13746,176 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Compute {@link CursorAnchorInfo} from this {@link TextView}. + * + * @param filter the {@link CursorAnchorInfo} update filter which specified the needed + * information from IME. + * @param cursorAnchorInfoBuilder a cached {@link CursorAnchorInfo.Builder} object used to build + * the result {@link CursorAnchorInfo}. + * @param viewToScreenMatrix a cached {@link Matrix} object used to compute the view to screen + * matrix. + * @return the result {@link CursorAnchorInfo} to be passed to IME. + * @hide + */ + @VisibleForTesting + @Nullable + public CursorAnchorInfo getCursorAnchorInfo(@InputConnection.CursorUpdateFilter int filter, + @NonNull CursorAnchorInfo.Builder cursorAnchorInfoBuilder, + @NonNull Matrix viewToScreenMatrix) { + Layout layout = getLayout(); + if (layout == null) { + return null; + } + boolean includeEditorBounds = + (filter & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0; + boolean includeCharacterBounds = + (filter & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0; + boolean includeInsertionMarker = + (filter & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0; + boolean includeVisibleLineBounds = + (filter & InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS) != 0; + boolean includeTextAppearance = + (filter & InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE) != 0; + boolean includeAll = + (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker + && !includeVisibleLineBounds && !includeTextAppearance); + + includeEditorBounds |= includeAll; + includeCharacterBounds |= includeAll; + includeInsertionMarker |= includeAll; + includeVisibleLineBounds |= includeAll; + includeTextAppearance |= includeAll; + + final CursorAnchorInfo.Builder builder = cursorAnchorInfoBuilder; + builder.reset(); + + final int selectionStart = getSelectionStart(); + builder.setSelectionRange(selectionStart, getSelectionEnd()); + + // Construct transformation matrix from view local coordinates to screen coordinates. + viewToScreenMatrix.reset(); + transformMatrixToGlobal(viewToScreenMatrix); + builder.setMatrix(viewToScreenMatrix); + + if (includeEditorBounds) { + final RectF editorBounds = new RectF(); + editorBounds.set(0 /* left */, 0 /* top */, + getWidth(), getHeight()); + final RectF handwritingBounds = new RectF( + -getHandwritingBoundsOffsetLeft(), + -getHandwritingBoundsOffsetTop(), + getWidth() + getHandwritingBoundsOffsetRight(), + getHeight() + getHandwritingBoundsOffsetBottom()); + EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder(); + EditorBoundsInfo editorBoundsInfo = boundsBuilder.setEditorBounds(editorBounds) + .setHandwritingBounds(handwritingBounds).build(); + builder.setEditorBoundsInfo(editorBoundsInfo); + } + + if (includeCharacterBounds || includeInsertionMarker || includeVisibleLineBounds) { + final float viewportToContentHorizontalOffset = + viewportToContentHorizontalOffset(); + final float viewportToContentVerticalOffset = + viewportToContentVerticalOffset(); + final boolean isTextTransformed = (getTransformationMethod() != null + && getTransformed() instanceof OffsetMapping); + if (includeCharacterBounds && !isTextTransformed) { + final CharSequence text = getText(); + if (text instanceof Spannable) { + final Spannable sp = (Spannable) text; + int composingTextStart = EditableInputConnection.getComposingSpanStart(sp); + int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp); + if (composingTextEnd < composingTextStart) { + final int temp = composingTextEnd; + composingTextEnd = composingTextStart; + composingTextStart = temp; + } + final boolean hasComposingText = + (0 <= composingTextStart) && (composingTextStart + < composingTextEnd); + if (hasComposingText) { + final CharSequence composingText = text.subSequence(composingTextStart, + composingTextEnd); + builder.setComposingText(composingTextStart, composingText); + populateCharacterBounds(builder, composingTextStart, + composingTextEnd, viewportToContentHorizontalOffset, + viewportToContentVerticalOffset); + } + } + } + + if (includeInsertionMarker) { + // Treat selectionStart as the insertion point. + if (0 <= selectionStart) { + final int offsetTransformed = originalToTransformed( + selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR); + final int line = layout.getLineForOffset(offsetTransformed); + final float insertionMarkerX = + layout.getPrimaryHorizontal(offsetTransformed) + + viewportToContentHorizontalOffset; + final float insertionMarkerTop = layout.getLineTop(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBaseline = layout.getLineBaseline(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBottom = + layout.getLineBottom(line, /* includeLineSpacing= */ false) + + viewportToContentVerticalOffset; + final boolean isTopVisible = + isPositionVisible(insertionMarkerX, insertionMarkerTop); + final boolean isBottomVisible = + isPositionVisible(insertionMarkerX, insertionMarkerBottom); + int insertionMarkerFlags = 0; + if (isTopVisible || isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isTopVisible || !isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + } + if (layout.isRtlCharAt(offsetTransformed)) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL; + } + builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, + insertionMarkerBaseline, insertionMarkerBottom, + insertionMarkerFlags); + } + } + + if (includeVisibleLineBounds) { + final Rect visibleRect = new Rect(); + if (getContentVisibleRect(visibleRect)) { + // Subtract the viewportToContentVerticalOffset to convert the view + // coordinates to layout coordinates. + final float visibleTop = + visibleRect.top - viewportToContentVerticalOffset; + final float visibleBottom = + visibleRect.bottom - viewportToContentVerticalOffset; + final int firstLine = + layout.getLineForVertical((int) Math.floor(visibleTop)); + final int lastLine = + layout.getLineForVertical((int) Math.ceil(visibleBottom)); + + for (int line = firstLine; line <= lastLine; ++line) { + final float left = layout.getLineLeft(line) + + viewportToContentHorizontalOffset; + final float top = layout.getLineTop(line) + + viewportToContentVerticalOffset; + final float right = layout.getLineRight(line) + + viewportToContentHorizontalOffset; + final float bottom = layout.getLineBottom(line, false) + + viewportToContentVerticalOffset; + builder.addVisibleLineBounds(left, top, right, bottom); + } + } + } + } + + if (includeTextAppearance) { + builder.setTextAppearanceInfo(TextAppearanceInfo.createFromTextView(this)); + } + return builder.build(); + } + + /** * Creates the {@link TextBoundsInfo} for the text lines that intersects with the {@code rectF}. * @hide */ diff --git a/core/proto/android/server/syncstorageengine.proto b/core/proto/android/server/syncstorageengine.proto index 2f35a077c59e..7fa01d8d1c7a 100644 --- a/core/proto/android/server/syncstorageengine.proto +++ b/core/proto/android/server/syncstorageengine.proto @@ -85,4 +85,5 @@ message SyncStatusProto { repeated StatusInfo status = 1; optional bool is_job_namespace_migrated = 2; + optional bool is_job_attribution_fixed = 3; } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a1de6bba1fbf..a1e18a70fdf2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4375,6 +4375,18 @@ --> <string name="config_defaultCredentialManagerHybridService" translatable="false"></string> + <!-- The component name, flattened to a string, for the system's credential manager + provider service. This service allows credential retrieval and storage od credentials. + + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, there will be no user configurable + provider to service credential manager requests. However, credential manager system + services that do not require user consent, will still work. + + See android.credentials.CredentialManager + --> + <string name="config_defaultCredentialProviderService" translatable="false"></string> + <!-- The package name for the system's smartspace service. This service returns smartspace results. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f237ece78f95..5ccc2482ff48 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3729,6 +3729,7 @@ <java-symbol type="string" name="config_defaultAppPredictionService" /> <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultCredentialManagerHybridService" /> + <java-symbol type="string" name="config_defaultCredentialProviderService" /> <java-symbol type="string" name="config_defaultSearchUiService" /> <java-symbol type="string" name="config_defaultSmartspaceService" /> <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" /> diff --git a/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java b/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java new file mode 100644 index 000000000000..1a019878f67f --- /dev/null +++ b/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java @@ -0,0 +1,576 @@ +/* + * 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.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Activity; +import android.app.Instrumentation; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.inputmethod.CursorAnchorInfo; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class EditTextCursorAnchorInfoTest { + private static final CursorAnchorInfo.Builder sCursorAnchorInfoBuilder = + new CursorAnchorInfo.Builder(); + private static final Matrix sMatrix = new Matrix(); + private static final int[] sLocationOnScreen = new int[2]; + private static Typeface sTypeface; + private static final float TEXT_SIZE = 1f; + // The line height of the test font font is 1.2 * textSize. + private static final int LINE_HEIGHT = 12; + private static final CharSequence DEFAULT_TEXT = "X\nXX\nXXX\nXXXX\nXXXXX"; + private static final ImmutableList<RectF> DEFAULT_LINE_BOUNDS = ImmutableList.of( + new RectF(0f, 0f, 10f, LINE_HEIGHT), + new RectF(0f, LINE_HEIGHT, 20f, 2 * LINE_HEIGHT), + new RectF(0f, 2 * LINE_HEIGHT, 30f, 3 * LINE_HEIGHT), + new RectF(0f, 3 * LINE_HEIGHT, 40f, 4 * LINE_HEIGHT), + new RectF(0f, 4 * LINE_HEIGHT, 50f, 5 * LINE_HEIGHT)); + + @Rule + public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>( + TextViewActivity.class); + private Activity mActivity; + private TextView mEditText; + + @BeforeClass + public static void setupClass() { + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + + // The test font has following coverage and width. + // U+0020: 10em + // U+002E (.): 10em + // U+0043 (C): 100em + // U+0049 (I): 1em + // U+004C (L): 50em + // U+0056 (V): 5em + // U+0058 (X): 10em + // U+005F (_): 0em + // U+05D0 : 1em // HEBREW LETTER ALEF + // U+05D1 : 5em // HEBREW LETTER BET + // U+FFFD (invalid surrogate will be replaced to this): 7em + // U+10331 (\uD800\uDF31): 10em + // Undefined : 0.5em + sTypeface = Typeface.createFromAsset(instrumentation.getTargetContext().getAssets(), + "fonts/StaticLayoutLineBreakingTestFont.ttf"); + } + + @Before + public void setup() { + mActivity = mActivityRule.getActivity(); + } + + @Test + public void testMatrix() { + setupEditText("", /* height= */ 100); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + Matrix actualMatrix = cursorAnchorInfo.getMatrix(); + Matrix expectedMatrix = new Matrix(); + expectedMatrix.setTranslate(sLocationOnScreen[0], sLocationOnScreen[1]); + + assertThat(actualMatrix).isEqualTo(expectedMatrix); + } + + @Test + public void testMatrix_withTranslation() { + float translationX = 10f; + float translationY = 20f; + createEditText(""); + mEditText.setTranslationX(translationX); + mEditText.setTranslationY(translationY); + measureEditText(100); + + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + Matrix actualMatrix = cursorAnchorInfo.getMatrix(); + Matrix expectedMatrix = new Matrix(); + expectedMatrix.setTranslate(sLocationOnScreen[0] + translationX, + sLocationOnScreen[1] + translationY); + + assertThat(actualMatrix).isEqualTo(expectedMatrix); + } + + @Test + public void testVisibleLineBounds_allVisible() { + setupEditText(DEFAULT_TEXT, /* height= */ 100); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + assertThat(lineBounds).isEqualTo(DEFAULT_LINE_BOUNDS); + } + + @Test + public void testVisibleLineBounds_allVisible_withLineSpacing() { + float lineSpacing = 10f; + setupEditText("X\nXX\nXXX", /* height= */ 100, lineSpacing, + /* lineMultiplier=*/ 1f); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + assertThat(lineBounds.size()).isEqualTo(3); + assertThat(lineBounds.get(0)).isEqualTo(new RectF(0f, 0f, 10f, LINE_HEIGHT)); + + float line1Top = LINE_HEIGHT + lineSpacing; + float line1Bottom = line1Top + LINE_HEIGHT; + assertThat(lineBounds.get(1)).isEqualTo(new RectF(0f, line1Top, 20f, line1Bottom)); + + float line2Top = 2 * (LINE_HEIGHT + lineSpacing); + float line2Bottom = line2Top + LINE_HEIGHT; + assertThat(lineBounds.get(2)).isEqualTo(new RectF(0f, line2Top, 30f, line2Bottom)); + } + + @Test + public void testVisibleLineBounds_allVisible_withLineMultiplier() { + float lineMultiplier = 2f; + setupEditText("X\nXX\nXXX", /* height= */ 100, /* lineSpacing= */ 0f, + /* lineMultiplier=*/ lineMultiplier); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + assertThat(lineBounds.size()).isEqualTo(3); + assertThat(lineBounds.get(0)).isEqualTo(new RectF(0f, 0f, 10f, LINE_HEIGHT)); + + float line1Top = LINE_HEIGHT * lineMultiplier; + float line1Bottom = line1Top + LINE_HEIGHT; + assertThat(lineBounds.get(1)).isEqualTo(new RectF(0f, line1Top, 20f, line1Bottom)); + + float line2Top = 2 * LINE_HEIGHT * lineMultiplier; + float line2Bottom = line2Top + LINE_HEIGHT; + assertThat(lineBounds.get(2)).isEqualTo(new RectF(0f, line2Top, 30f, line2Bottom)); + } + + @Test + public void testVisibleLineBounds_cutBottomLines() { + // Line top is inclusive and line bottom is exclusive. And if the visible area's + // bottom equals to the line top, this line is still visible. So the line height is + // 3 * LINE_HEIGHT - 1 to avoid including the line 3. + setupEditText(DEFAULT_TEXT, /* height= */ 3 * LINE_HEIGHT - 1); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + assertThat(lineBounds).isEqualTo(DEFAULT_LINE_BOUNDS.subList(0, 3)); + } + + @Test + public void testVisibleLineBounds_scrolled_cutTopLines() { + // First 2 lines are cut. + int scrollY = 2 * LINE_HEIGHT; + setupEditText(/* height= */ 3 * LINE_HEIGHT, + /* scrollY= */ scrollY); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 5); + expectedLineBounds.forEach(rectF -> rectF.offset(0, -scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_scrolled_cutTopAndBottomLines() { + // Line top is inclusive and line bottom is exclusive. And if the visible area's + // bottom equals to the line top, this line is still visible. So the line height is + // 2 * LINE_HEIGHT - 1 which only shows 2 lines. + int scrollY = 2 * LINE_HEIGHT; + setupEditText(/* height= */ 2 * LINE_HEIGHT - 1, + /* scrollY= */ scrollY); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 4); + expectedLineBounds.forEach(rectF -> rectF.offset(0, -scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_scrolled_partiallyVisibleLines() { + // The first 2 lines are completely cut, line 2 and 3 are partially visible. + int scrollY = 2 * LINE_HEIGHT + LINE_HEIGHT / 2; + setupEditText(/* height= */ LINE_HEIGHT, + /* scrollY= */ scrollY); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 4); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, -scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withCompoundDrawable_allVisible() { + int topDrawableHeight = LINE_HEIGHT; + Drawable topDrawable = createDrawable(topDrawableHeight); + Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT); + setupEditText(/* height= */ 100, + /* scrollY= */ 0, topDrawable, bottomDrawable); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = copy(DEFAULT_LINE_BOUNDS); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withCompoundDrawable_cutBottomLines() { + // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT. + // Only first 2 lines are visible. + int topDrawableHeight = LINE_HEIGHT; + Drawable topDrawable = createDrawable(topDrawableHeight); + Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1); + setupEditText(/* height= */ 5 * LINE_HEIGHT, + /* scrollY= */ 0, topDrawable, bottomDrawable); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 2); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withCompoundDrawable_scrolled() { + // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT. + // So 2 lines are visible. Because the view is scrolled vertically by LINE_HEIGHT, + // the line 1 and 2 are visible. + int topDrawableHeight = LINE_HEIGHT; + Drawable topDrawable = createDrawable(topDrawableHeight); + Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1); + int scrollY = LINE_HEIGHT; + setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY, + topDrawable, bottomDrawable); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 3); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight - scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withCompoundDrawable_partiallyVisible() { + // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT. + // And because the view is scrolled vertically by 0.5 * LINE_HEIGHT, + // the line 0, 1 and 2 are visible. + int topDrawableHeight = LINE_HEIGHT; + Drawable topDrawable = createDrawable(topDrawableHeight); + Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1); + int scrollY = LINE_HEIGHT / 2; + setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY, + topDrawable, bottomDrawable); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 3); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight - scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withPaddings_allVisible() { + int topPadding = LINE_HEIGHT; + int bottomPadding = LINE_HEIGHT; + setupEditText(/* height= */ 100, /* scrollY= */ 0, topPadding, bottomPadding); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = copy(DEFAULT_LINE_BOUNDS); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withPaddings_cutBottomLines() { + // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT. + // So 2 lines are visible. + int topPadding = LINE_HEIGHT; + int bottomPadding = 2 * LINE_HEIGHT + 1; + setupEditText(/* height= */ 5 * LINE_HEIGHT, /* scrollY= */ 0, topPadding, bottomPadding); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 2); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withPaddings_scrolled() { + // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT. + // So 2 lines are visible. Because the view is scrolled vertically by LINE_HEIGHT, + // the line 1 and 2 are visible. + int topPadding = LINE_HEIGHT; + int bottomPadding = 2 * LINE_HEIGHT + 1; + int scrollY = LINE_HEIGHT; + setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY, + topPadding, bottomPadding); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 3); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding - scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_withPadding_partiallyVisible() { + // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT. + // And because the view is scrolled vertically by 0.5 * LINE_HEIGHT, the line 0, 1 and 2 + // are visible. + int topPadding = LINE_HEIGHT; + int bottomPadding = 2 * LINE_HEIGHT + 1; + int scrollY = LINE_HEIGHT / 2; + setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY, + topPadding, bottomPadding); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 3); + expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding - scrollY)); + + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_clippedTop() { + // The first line is clipped off. + setupVerticalClippedEditText(LINE_HEIGHT, 5 * LINE_HEIGHT); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 5); + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_clippedBottom() { + // The last line is clipped off. + setupVerticalClippedEditText(0, 4 * LINE_HEIGHT - 1); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 4); + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + @Test + public void testVisibleLineBounds_clippedTopAndBottom() { + // The first and last line are clipped off. + setupVerticalClippedEditText(LINE_HEIGHT, 4 * LINE_HEIGHT - 1); + CursorAnchorInfo cursorAnchorInfo = + mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix); + + List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds(); + + List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 4); + assertThat(lineBounds).isEqualTo(expectedLineBounds); + } + + private List<RectF> copy(List<RectF> rectFList) { + List<RectF> result = new ArrayList<>(); + for (RectF rectF : rectFList) { + result.add(new RectF(rectF)); + } + return result; + } + private List<RectF> subList(List<RectF> rectFList, int start, int end) { + List<RectF> result = new ArrayList<>(); + for (int index = start; index < end; ++index) { + result.add(new RectF(rectFList.get(index))); + } + return result; + } + + private void setupVerticalClippedEditText(int visibleTop, int visibleBottom) { + ScrollView scrollView = new ScrollView(mActivity); + mEditText = new EditText(mActivity); + mEditText.setTypeface(sTypeface); + mEditText.setText(DEFAULT_TEXT); + mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE); + + mEditText.setPadding(0, 0, 0, 0); + mEditText.setCompoundDrawables(null, null, null, null); + mEditText.setCompoundDrawablePadding(0); + + mEditText.scrollTo(0, 0); + mEditText.setLineSpacing(0f, 1f); + + // Place the text layout top to the view's top. + mEditText.setGravity(Gravity.TOP); + int width = 1000; + int height = visibleBottom - visibleTop; + + scrollView.addView(mEditText, new FrameLayout.LayoutParams( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(5 * LINE_HEIGHT, View.MeasureSpec.EXACTLY))); + scrollView.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); + scrollView.layout(0, 0, width, height); + + scrollView.scrollTo(0, visibleTop); + } + + private void setupEditText(CharSequence text, int height) { + createEditText(text); + measureEditText(height); + } + + private void setupEditText(CharSequence text, int height, float lineSpacing, + float lineMultiplier) { + createEditText(text); + mEditText.setLineSpacing(lineSpacing, lineMultiplier); + measureEditText(height); + } + + private void setupEditText(int height, int scrollY) { + createEditText(); + mEditText.scrollTo(0, scrollY); + measureEditText(height); + } + + private void setupEditText(int height, int scrollY, Drawable drawableTop, + Drawable drawableBottom) { + createEditText(); + mEditText.scrollTo(0, scrollY); + mEditText.setCompoundDrawables(null, drawableTop, null, drawableBottom); + measureEditText(height); + } + + private void setupEditText(int height, int scrollY, int paddingTop, + int paddingBottom) { + createEditText(); + mEditText.scrollTo(0, scrollY); + mEditText.setPadding(0, paddingTop, 0, paddingBottom); + measureEditText(height); + } + + private void createEditText() { + createEditText(DEFAULT_TEXT); + } + + private void createEditText(CharSequence text) { + mEditText = new EditText(mActivity); + mEditText.setTypeface(sTypeface); + mEditText.setText(text); + mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE); + + mEditText.setPadding(0, 0, 0, 0); + mEditText.setCompoundDrawables(null, null, null, null); + mEditText.setCompoundDrawablePadding(0); + + mEditText.scrollTo(0, 0); + mEditText.setLineSpacing(0f, 1f); + + // Place the text layout top to the view's top. + mEditText.setGravity(Gravity.TOP); + } + + private void measureEditText(int height) { + // width equals to 1000 is enough to avoid line break for all test cases. + measureEditText(1000, height); + } + + private void measureEditText(int width, int height) { + mEditText.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); + mEditText.layout(0, 0, width, height); + + mEditText.getLocationOnScreen(sLocationOnScreen); + } + + private Drawable createDrawable(int height) { + // width is not important for this drawable, make it 1 pixel. + return createDrawable(1, height); + } + + private Drawable createDrawable(int width, int height) { + ShapeDrawable drawable = new ShapeDrawable(); + drawable.setBounds(new Rect(0, 0, width, height)); + return drawable; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java index d2760022a015..88525aabe53b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java @@ -84,6 +84,15 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio String[] groups = Arrays.copyOfRange(args, 1, args.length); return mShellProtoLog.stopTextLogging(groups, pw) == 0; } + case "save-for-bugreport": { + if (!mShellProtoLog.isProtoEnabled()) { + pw.println("Logging to proto is not enabled for WMShell."); + return false; + } + mShellProtoLog.stopProtoLog(pw, true /* writeToFile */); + mShellProtoLog.startProtoLog(pw); + return true; + } default: { pw.println("Invalid command: " + args[0]); printShellCommandHelp(pw, ""); @@ -108,5 +117,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio pw.println(prefix + " Enable logcat logging for given groups."); pw.println(prefix + "disable-text [group...]"); pw.println(prefix + " Disable logcat logging for given groups."); + pw.println(prefix + "save-for-bugreport"); + pw.println(prefix + " Flush proto logging to file, only if it's enabled."); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b2f61c231911..5275e90b931f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -829,7 +829,15 @@ public class Transitions implements RemoteCallable<Transitions> { } mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct); active.mToken = transitionToken; - mActiveTransitions.add(active); + int insertIdx = 0; + for (; insertIdx < mActiveTransitions.size(); ++insertIdx) { + if (mActiveTransitions.get(insertIdx).mInfo == null) { + // A `startNewTransition` was sent to WMCore, but wasn't acknowledged before WMCore + // made this request, so insert this request beforehand to keep order in sync. + break; + } + } + mActiveTransitions.add(insertIdx, active); } /** Start a new transition directly. */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 595c3b4880df..a9061aeb0314 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -45,6 +45,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.clearInvocations; @@ -119,8 +120,8 @@ public class ShellTransitionTests extends ShellTestCase { @Before public void setUp() { - doAnswer(invocation -> invocation.getArguments()[1]) - .when(mOrganizer).startTransition(any(), any()); + doAnswer(invocation -> new Binder()) + .when(mOrganizer).startNewTransition(anyInt(), any()); } @Test @@ -562,6 +563,32 @@ public class ShellTransitionTests extends ShellTestCase { } @Test + public void testTransitionOrderMatchesCore() { + Transitions transitions = createTestTransitions(); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + IBinder transitToken = new Binder(); + IBinder shellInit = transitions.startTransition(TRANSIT_CLOSE, + new WindowContainerTransaction(), null /* handler */); + // make sure we are testing the "New" API. + verify(mOrganizer, times(1)).startNewTransition(eq(TRANSIT_CLOSE), any()); + // WMCore may not receive the new transition before requesting its own. + transitions.requestStartTransition(transitToken, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); + + // At this point, WM is working on its transition (the shell-initialized one is still + // queued), so continue the transition lifecycle for that. + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + // At this point, if things are not working, we'd get an NPE due to attempting to merge + // into the shellInit transition which hasn't started yet. + assertEquals(1, mDefaultHandler.activeCount()); + } + + @Test public void testShouldRotateSeamlessly() throws Exception { final RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png Binary files differdeleted file mode 100644 index 388d09837252..000000000000 --- a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png +++ /dev/null diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml new file mode 100644 index 000000000000..91b23cc4d2a9 --- /dev/null +++ b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml @@ -0,0 +1,158 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="vector" + android:width="316dp" + android:height="168dp" + android:viewportWidth="316" + android:viewportHeight="168"> + <path + android:name="path" + android:pathData="M 238 56 C 238 42.75 227.26 32 214 32 C 200.75 32 190 42.75 190 56 L 190 74 C 190 77.31 192.69 80 196 80 L 214 80 C 227.26 80 238 69.26 238 56 Z" + android:fillColor="#ffba29" + android:strokeWidth="1"/> + <path + android:name="path_1" + android:pathData="M 199.42 51.71 C 199.34 51.65 199.29 51.56 199.27 51.44 C 199.26 51.31 199.27 51.21 199.33 51.13 C 201.08 48.8 203.23 46.98 205.79 45.67 C 208.35 44.36 211.07 43.71 213.96 43.71 C 216.85 43.71 219.64 44.34 222.25 45.61 C 224.86 46.88 227.03 48.69 228.75 51.05 C 228.86 51.19 228.9 51.32 228.85 51.45 C 228.81 51.57 228.72 51.68 228.58 51.76 C 228.5 51.82 228.38 51.84 228.23 51.84 C 228.08 51.84 227.96 51.76 227.88 51.59 C 226.27 49.34 224.23 47.63 221.78 46.47 C 219.32 45.3 216.72 44.72 213.97 44.72 C 211.22 44.72 208.68 45.32 206.26 46.51 C 203.84 47.7 201.83 49.4 200.22 51.59 C 200.08 51.76 199.94 51.84 199.8 51.84 C 199.66 51.84 199.54 51.8 199.42 51.72 Z M 218.75 72.42 C 216 71.7 213.72 70.28 211.9 68.17 C 210.08 66.06 209.17 63.49 209.17 60.46 C 209.17 59.13 209.65 58.02 210.61 57.15 C 211.57 56.27 212.71 55.84 214.05 55.84 C 215.39 55.84 216.48 56.28 217.43 57.15 C 218.37 58.02 218.85 59.13 218.85 60.46 C 218.85 61.49 219.23 62.34 220 63.02 C 220.76 63.7 221.66 64.04 222.69 64.04 C 223.72 64.04 224.64 63.69 225.38 63 C 226.12 62.31 226.48 61.46 226.48 60.46 C 226.48 57.13 225.25 54.32 222.79 52.04 C 220.33 49.76 217.39 48.62 213.98 48.62 C 210.57 48.62 207.63 49.76 205.19 52.04 C 202.75 54.32 201.52 57.12 201.52 60.46 C 201.52 61.13 201.61 61.97 201.79 63 C 201.97 64.03 202.28 65.22 202.73 66.58 C 202.79 66.72 202.79 66.84 202.75 66.93 C 202.71 67.03 202.6 67.12 202.44 67.2 C 202.3 67.28 202.17 67.3 202.04 67.24 C 201.91 67.18 201.82 67.09 201.77 66.95 C 201.35 65.78 201.04 64.68 200.83 63.64 C 200.62 62.6 200.52 61.54 200.52 60.45 C 200.52 56.81 201.85 53.76 204.5 51.3 C 207.15 48.84 210.31 47.61 213.98 47.61 C 217.65 47.61 220.86 48.84 223.52 51.3 C 226.19 53.76 227.52 56.81 227.52 60.45 C 227.52 61.73 227.05 62.81 226.1 63.7 C 225.16 64.59 224.02 65.03 222.68 65.03 C 221.34 65.03 220.24 64.59 219.28 63.7 C 218.32 62.81 217.84 61.73 217.84 60.45 C 217.84 59.42 217.46 58.56 216.72 57.87 C 215.98 57.18 215.08 56.83 214.05 56.83 C 213.02 56.83 212.08 57.18 211.32 57.87 C 210.56 58.56 210.17 59.43 210.17 60.45 C 210.17 63.26 211.01 65.6 212.69 67.47 C 214.37 69.35 216.49 70.66 219.04 71.41 C 219.21 71.47 219.32 71.55 219.39 71.66 C 219.46 71.77 219.47 71.9 219.41 72.04 C 219.35 72.15 219.28 72.25 219.18 72.35 C 219.08 72.45 218.94 72.47 218.74 72.41 Z M 204.42 43.04 C 204.36 43.04 204.27 43.03 204.15 43 C 204.02 42.97 203.95 42.9 203.92 42.79 C 203.86 42.71 203.84 42.6 203.86 42.46 C 203.87 42.32 203.92 42.22 204.01 42.17 C 205.54 41.31 207.16 40.66 208.86 40.23 C 210.57 39.8 212.3 39.58 214.05 39.58 C 215.8 39.58 217.51 39.79 219.17 40.2 C 220.84 40.62 222.45 41.24 224 42.08 C 224.17 42.16 224.27 42.27 224.31 42.41 C 224.35 42.55 224.35 42.67 224.29 42.79 C 224.23 42.9 224.14 42.98 224.02 43.04 C 223.89 43.1 223.78 43.1 223.67 43.04 C 222.2 42.18 220.65 41.55 219.02 41.16 C 217.39 40.77 215.74 40.58 214.04 40.58 C 212.34 40.58 210.7 40.8 209.1 41.23 C 207.5 41.66 205.94 42.26 204.41 43.04 Z M 209.92 71.96 C 208.36 70.29 207.14 68.55 206.25 66.73 C 205.36 64.91 204.92 62.82 204.92 60.46 C 204.92 58.1 205.82 55.91 207.61 54.23 C 209.4 52.55 211.55 51.71 214.05 51.71 C 216.55 51.71 218.7 52.55 220.49 54.23 C 222.28 55.91 223.18 57.99 223.18 60.46 C 223.18 60.6 223.14 60.72 223.06 60.81 C 222.98 60.91 222.85 60.96 222.68 60.96 C 222.6 60.96 222.49 60.91 222.37 60.81 C 222.25 60.71 222.18 60.6 222.18 60.46 C 222.18 58.27 221.38 56.43 219.78 54.94 C 218.18 53.45 216.27 52.71 214.05 52.71 C 211.83 52.71 209.92 53.45 208.32 54.94 C 206.72 56.43 205.92 58.27 205.92 60.46 C 205.92 62.77 206.32 64.72 207.13 66.34 C 207.94 67.95 209.1 69.58 210.63 71.22 C 210.77 71.39 210.82 71.53 210.78 71.66 C 210.74 71.78 210.69 71.89 210.63 71.97 C 210.55 72.05 210.44 72.1 210.3 72.12 C 210.16 72.13 210.04 72.09 209.92 71.97 Z M 222.34 68.96 C 219.95 68.96 217.88 68.15 216.11 66.54 C 214.35 64.93 213.46 62.9 213.46 60.46 C 213.46 60.32 213.5 60.2 213.58 60.11 C 213.66 60.01 213.79 59.96 213.96 59.96 C 214.1 59.96 214.22 60.01 214.31 60.11 C 214.41 60.21 214.46 60.33 214.46 60.46 C 214.46 62.65 215.24 64.45 216.81 65.86 C 218.38 67.26 220.22 67.96 222.33 67.96 C 222.64 67.96 222.95 67.95 223.27 67.92 C 223.59 67.89 223.9 67.85 224.21 67.8 C 224.32 67.8 224.42 67.83 224.5 67.88 C 224.58 67.93 224.64 68.03 224.67 68.17 C 224.73 68.31 224.71 68.43 224.63 68.52 C 224.55 68.62 224.42 68.68 224.25 68.71 C 224 68.79 223.7 68.86 223.35 68.9 C 223 68.94 222.66 68.96 222.33 68.96 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <path + android:name="path_2" + android:pathData="M 134 72 L 134 60 C 134 46.75 144.75 36 158 36 C 171.25 36 182 46.75 182 60 L 182 72 M 124 72 L 192 72 C 195.712 72 199.275 73.476 201.899 76.101 C 204.524 78.725 206 82.288 206 86 L 206 146 C 206 149.712 204.524 153.275 201.899 155.899 C 199.275 158.524 195.712 160 192 160 L 124 160 C 120.288 160 116.725 158.524 114.101 155.899 C 111.476 153.275 110 149.712 110 146 L 110 86 C 110 82.288 111.476 78.725 114.101 76.101 C 116.725 73.476 120.288 72 124 72" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <path + android:name="path_3" + android:pathData="M 155.67 114.83 C 153.22 114.83 151.15 113.98 149.46 112.29 C 147.77 110.6 146.92 108.53 146.92 106.08 C 146.92 103.63 147.77 101.56 149.46 99.87 C 151.15 98.18 153.22 97.33 155.67 97.33 C 158.12 97.33 160.19 98.18 161.88 99.87 C 163.57 101.56 164.42 103.63 164.42 106.08 C 164.42 108.53 163.57 110.6 161.88 112.29 C 160.19 113.98 158.12 114.83 155.67 114.83 Z M 172.59 137 L 169.67 133.5 L 169.67 124.92 C 168.27 124.38 167.14 123.51 166.29 122.32 C 165.44 121.13 165.01 119.8 165.01 118.32 C 165.01 116.38 165.69 114.72 167.05 113.36 C 168.41 112 170.06 111.32 172.01 111.32 C 173.96 111.32 175.61 112 176.97 113.36 C 178.33 114.72 179.01 116.37 179.01 118.32 C 179.01 119.8 178.58 121.13 177.73 122.32 C 176.88 123.51 175.75 124.37 174.35 124.92 L 174.35 125.33 L 176.68 127.66 L 174.35 129.99 L 176.68 132.32 L 172.6 136.99 Z M 172.01 121.83 C 172.98 121.83 173.81 121.49 174.49 120.81 C 175.17 120.13 175.51 119.3 175.51 118.33 C 175.51 117.36 175.17 116.53 174.49 115.85 C 173.81 115.17 172.98 114.83 172.01 114.83 C 171.04 114.83 170.21 115.17 169.53 115.85 C 168.85 116.53 168.51 117.36 168.51 118.33 C 168.51 119.3 168.85 120.13 169.53 120.81 C 170.21 121.49 171.04 121.83 172.01 121.83 Z M 160.46 119.85 C 160.65 121.44 161.15 122.9 161.95 124.23 C 162.75 125.55 163.77 126.7 165.01 127.67 L 165.01 134.67 L 137.01 134.67 L 137.01 129.13 C 137.01 127.81 137.38 126.6 138.12 125.51 C 138.86 124.42 139.81 123.59 140.98 123 C 143.27 121.83 145.66 120.96 148.13 120.38 C 150.6 119.8 153.12 119.5 155.68 119.5 C 156.46 119.5 157.26 119.53 158.07 119.59 C 158.89 119.65 159.68 119.74 160.46 119.85 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <path + android:name="path_4" + android:pathData="M 63.481 27.427 L 102.118 17.075 C 102.457 16.984 102.815 16.984 103.154 17.075 C 103.493 17.166 103.802 17.344 104.05 17.592 C 104.298 17.841 104.477 18.15 104.568 18.489 L 110.779 41.671 C 110.87 42.01 110.87 42.367 110.779 42.706 C 110.689 43.045 110.51 43.355 110.262 43.603 C 110.014 43.851 109.704 44.03 109.365 44.121 L 70.728 54.473 C 70.216 54.611 69.67 54.539 69.211 54.274 C 68.751 54.008 68.416 53.571 68.279 53.059 L 62.067 29.877 C 61.976 29.538 61.976 29.181 62.067 28.842 C 62.158 28.503 62.336 28.193 62.585 27.945 C 62.833 27.697 63.142 27.518 63.481 27.427" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_5" + android:pathData="M 86.189 50.327 L 93.917 48.256 L 94.952 52.12 L 87.225 54.191 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <path + android:name="path_6" + android:pathData="M 83.36 55.23 L 98.81 51.08" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_7" + android:pathData="M 90.9 37.2 L 88.3 35.7 L 89.4 33.8 C 89.59 33.48 89.37 33.07 89 33.05 L 81.88 32.62 C 81.47 32.6 81.21 33.05 81.43 33.39 L 85.36 39.34 C 85.56 39.65 86.02 39.64 86.21 39.31 L 87.31 37.41 L 89.91 38.91 C 90.39 39.19 91 39.02 91.28 38.54 C 91.56 38.06 91.39 37.45 90.91 37.17 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <path + android:name="path_8" + android:pathData="M 86 160 L 222 160" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <group android:name="group"> + <path + android:name="path_9" + android:pathData="M 98 80 C 92.088 80 86.325 81.872 81.542 85.348 C 76.759 88.823 73.197 93.725 71.37 99.348 C 69.543 104.97 69.543 111.03 71.37 116.652 C 73.197 122.275 76.759 127.177 81.542 130.652 C 86.325 134.128 92.088 136 98 136 C 103.912 136 109.675 134.128 114.458 130.652 C 119.241 127.177 122.803 122.275 124.63 116.652 C 126.457 111.03 126.457 104.97 124.63 99.348 C 122.803 93.725 119.241 88.823 114.458 85.348 C 109.675 81.872 103.912 80 98 80 Z" + android:fillColor="#6dd48b" + android:strokeWidth="1"/> + <path + android:name="path_10" + android:pathData="M 98 90.5 C 93.36 90.5 88.906 92.345 85.626 95.626 C 82.345 98.906 80.5 103.36 80.5 108 C 80.5 112.64 82.345 117.094 85.626 120.374 C 88.906 123.655 93.36 125.5 98 125.5 C 102.64 125.5 107.094 123.655 110.374 120.374 C 113.655 117.094 115.5 112.64 115.5 108 C 115.5 103.36 113.655 98.906 110.374 95.626 C 107.094 92.345 102.64 90.5 98 90.5 Z" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeMiterLimit="10"/> + <path + android:name="path_11" + android:pathData="M 92.31 104.5 C 92.31 105.22 91.72 105.81 91 105.81 C 90.28 105.81 89.69 105.22 89.69 104.5 C 89.69 103.78 90.28 103.19 91 103.19 C 91.72 103.19 92.31 103.78 92.31 104.5 Z M 106.31 104.5 C 106.31 105.22 105.72 105.81 105 105.81 C 104.28 105.81 103.69 105.22 103.69 104.5 C 103.69 103.78 104.28 103.19 105 103.19 C 105.72 103.19 106.31 103.78 106.31 104.5 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <path + android:name="path_12" + android:pathData="M 98.88 107.12 L 98.88 110.62 L 96.61 110.62 M 100.11 116.16 C 98.94 117.33 97.06 117.33 95.89 116.16 C 95.29 115.56 95 114.76 95.02 113.97" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeMiterLimit="10"/> + </group> + <group android:name="group_2"> + <path + android:name="path_13" + android:pathData="M 264.23 104 L 296.23 104" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_14" + android:pathData="M 273.16 81.21 C 272.82 80.19 271.87 79.5 270.79 79.5 L 243.01 79.5 C 242.2 79.5 241.46 79.88 240.98 80.54 C 240.5 81.2 240.38 82.02 240.64 82.79 L 247.31 102.79 C 247.65 103.81 248.6 104.5 249.68 104.5 L 280.93 104.5 L 273.17 81.21 Z M 248.25 87.93 C 247.45 88.14 246.57 87.45 246.28 86.39 C 245.99 85.33 246.41 84.28 247.21 84.07 C 248.01 83.86 248.89 84.55 249.18 85.61 C 249.47 86.68 249.05 87.72 248.25 87.93 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <group android:name="group_1"> + <path + android:name="path_15" + android:pathData="M 279 83 L 285 83 M 277 77 L 285 69 M 271 75 L 271 69" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round"/> + </group> + </group> + <path + android:name="path_16" + android:pathData="M 58.16 105.86 C 57.83 105.28 57.29 104.87 56.64 104.69 L 45.05 101.58 C 43.72 101.22 42.34 102.02 41.99 103.35 L 35.78 126.53 C 35.61 127.17 35.7 127.85 36.03 128.43 C 36.36 129.01 36.9 129.42 37.55 129.6 L 49.14 132.71 C 49.36 132.77 49.57 132.79 49.79 132.79 C 50.89 132.79 51.91 132.05 52.21 130.94 L 58.42 107.76 C 58.59 107.11 58.5 106.44 58.17 105.86 Z M 50.65 107.75 C 50.51 108.28 49.96 108.6 49.43 108.46 C 48.9 108.32 48.58 107.77 48.72 107.24 C 48.86 106.71 49.41 106.39 49.94 106.53 C 50.47 106.67 50.79 107.22 50.65 107.75 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <path + android:name="path_17" + android:pathData="M 230 160 L 246 160" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <path + android:name="path_18" + android:pathData="M 204.63 80 C 202.38 75.27 197.58 72 192 72 L 190 72 L 190 74 C 190 77.31 192.69 80 196 80 L 204.63 80 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <group android:name="group_4"> + <path + android:name="path_19" + android:pathData="M 211.37 102.29 C 210.53 102.82 209.46 102.82 208.63 102.29 C 202.93 98.67 195.29 99.35 190.31 104.32 C 185.34 109.29 184.67 116.93 188.28 122.63 C 188.81 123.46 188.81 124.54 188.28 125.38 C 184.66 131.08 185.34 138.72 190.31 143.69 C 195.29 148.66 202.93 149.34 208.63 145.72 C 209.46 145.19 210.54 145.19 211.37 145.72 C 217.07 149.34 224.71 148.66 229.68 143.69 C 234.66 138.72 235.33 131.08 231.71 125.38 C 231.18 124.55 231.18 123.47 231.71 122.63 C 235.33 116.93 234.65 109.29 229.68 104.32 C 224.71 99.35 217.07 98.67 211.37 102.29 Z" + android:fillColor="#0b57cf" + android:strokeWidth="1" + android:fillType="evenOdd"/> + <group android:name="group_3"> + <path + android:name="path_20" + android:pathData="M 198 110 C 197.47 110 196.961 110.211 196.586 110.586 C 196.211 110.961 196 111.47 196 112 C 196 112.53 196.211 113.039 196.586 113.414 C 196.961 113.789 197.47 114 198 114 C 198.53 114 199.039 113.789 199.414 113.414 C 199.789 113.039 200 112.53 200 112 C 200 111.47 199.789 110.961 199.414 110.586 C 199.039 110.211 198.53 110 198 110 Z M 210 110 C 209.47 110 208.961 110.211 208.586 110.586 C 208.211 110.961 208 111.47 208 112 C 208 112.53 208.211 113.039 208.586 113.414 C 208.961 113.789 209.47 114 210 114 C 210.53 114 211.039 113.789 211.414 113.414 C 211.789 113.039 212 112.53 212 112 C 212 111.47 211.789 110.961 211.414 110.586 C 211.039 110.211 210.53 110 210 110 Z M 222 110 C 221.47 110 220.961 110.211 220.586 110.586 C 220.211 110.961 220 111.47 220 112 C 220 112.53 220.211 113.039 220.586 113.414 C 220.961 113.789 221.47 114 222 114 C 222.53 114 223.039 113.789 223.414 113.414 C 223.789 113.039 224 112.53 224 112 C 224 111.47 223.789 110.961 223.414 110.586 C 223.039 110.211 222.53 110 222 110 Z M 198 122 C 197.47 122 196.961 122.211 196.586 122.586 C 196.211 122.961 196 123.47 196 124 C 196 124.53 196.211 125.039 196.586 125.414 C 196.961 125.789 197.47 126 198 126 C 198.53 126 199.039 125.789 199.414 125.414 C 199.789 125.039 200 124.53 200 124 C 200 123.47 199.789 122.961 199.414 122.586 C 199.039 122.211 198.53 122 198 122 Z M 210 122 C 209.47 122 208.961 122.211 208.586 122.586 C 208.211 122.961 208 123.47 208 124 C 208 124.53 208.211 125.039 208.586 125.414 C 208.961 125.789 209.47 126 210 126 C 210.53 126 211.039 125.789 211.414 125.414 C 211.789 125.039 212 124.53 212 124 C 212 123.47 211.789 122.961 211.414 122.586 C 211.039 122.211 210.53 122 210 122 Z M 222 122 C 221.47 122 220.961 122.211 220.586 122.586 C 220.211 122.961 220 123.47 220 124 C 220 124.53 220.211 125.039 220.586 125.414 C 220.961 125.789 221.47 126 222 126 C 222.53 126 223.039 125.789 223.414 125.414 C 223.789 125.039 224 124.53 224 124 C 224 123.47 223.789 122.961 223.414 122.586 C 223.039 122.211 222.53 122 222 122 Z M 198 134 C 197.47 134 196.961 134.211 196.586 134.586 C 196.211 134.961 196 135.47 196 136 C 196 136.53 196.211 137.039 196.586 137.414 C 196.961 137.789 197.47 138 198 138 C 198.53 138 199.039 137.789 199.414 137.414 C 199.789 137.039 200 136.53 200 136 C 200 135.47 199.789 134.961 199.414 134.586 C 199.039 134.211 198.53 134 198 134 Z M 210 134 C 209.47 134 208.961 134.211 208.586 134.586 C 208.211 134.961 208 135.47 208 136 C 208 136.53 208.211 137.039 208.586 137.414 C 208.961 137.789 209.47 138 210 138 C 210.53 138 211.039 137.789 211.414 137.414 C 211.789 137.039 212 136.53 212 136 C 212 135.47 211.789 134.961 211.414 134.586 C 211.039 134.211 210.53 134 210 134 Z M 222 134 C 221.47 134 220.961 134.211 220.586 134.586 C 220.211 134.961 220 135.47 220 136 C 220 136.53 220.211 137.039 220.586 137.414 C 220.961 137.789 221.47 138 222 138 C 222.53 138 223.039 137.789 223.414 137.414 C 223.789 137.039 224 136.53 224 136 C 224 135.47 223.789 134.961 223.414 134.586 C 223.039 134.211 222.53 134 222 134 Z" + android:fillColor="#ffffff" + android:strokeWidth="1"/> + </group> + <path + android:name="path_21" + android:pathData="M 222 112 L 198 136 L 222 136" + android:strokeColor="#ffffff" + android:strokeWidth="1" + android:strokeLineCap="round"/> + </group> +</vector> diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml new file mode 100644 index 000000000000..53933cb77136 --- /dev/null +++ b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml @@ -0,0 +1,158 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="vector" + android:width="316dp" + android:height="168dp" + android:viewportWidth="316" + android:viewportHeight="168"> + <path + android:name="path" + android:pathData="M 238 56 C 238 42.75 227.26 32 214 32 C 200.75 32 190 42.75 190 56 L 190 74 C 190 77.31 192.69 80 196 80 L 214 80 C 227.26 80 238 69.26 238 56 Z" + android:fillColor="#f09d00" + android:strokeWidth="1"/> + <path + android:name="path_1" + android:pathData="M 199.42 51.71 C 199.34 51.65 199.29 51.56 199.27 51.44 C 199.26 51.31 199.27 51.21 199.33 51.13 C 201.08 48.8 203.23 46.98 205.79 45.67 C 208.35 44.36 211.07 43.71 213.96 43.71 C 216.85 43.71 219.64 44.34 222.25 45.61 C 224.86 46.88 227.03 48.69 228.75 51.05 C 228.86 51.19 228.9 51.32 228.85 51.45 C 228.81 51.57 228.72 51.68 228.58 51.76 C 228.5 51.82 228.38 51.84 228.23 51.84 C 228.08 51.84 227.96 51.76 227.88 51.59 C 226.27 49.34 224.23 47.63 221.78 46.47 C 219.32 45.3 216.72 44.72 213.97 44.72 C 211.22 44.72 208.68 45.32 206.26 46.51 C 203.84 47.7 201.83 49.4 200.22 51.59 C 200.08 51.76 199.94 51.84 199.8 51.84 C 199.66 51.84 199.54 51.8 199.42 51.72 Z M 218.75 72.42 C 216 71.7 213.72 70.28 211.9 68.17 C 210.08 66.06 209.17 63.49 209.17 60.46 C 209.17 59.13 209.65 58.02 210.61 57.15 C 211.57 56.27 212.71 55.84 214.05 55.84 C 215.39 55.84 216.48 56.28 217.43 57.15 C 218.37 58.02 218.85 59.13 218.85 60.46 C 218.85 61.49 219.23 62.34 220 63.02 C 220.76 63.7 221.66 64.04 222.69 64.04 C 223.72 64.04 224.64 63.69 225.38 63 C 226.12 62.31 226.48 61.46 226.48 60.46 C 226.48 57.13 225.25 54.32 222.79 52.04 C 220.33 49.76 217.39 48.62 213.98 48.62 C 210.57 48.62 207.63 49.76 205.19 52.04 C 202.75 54.32 201.52 57.12 201.52 60.46 C 201.52 61.13 201.61 61.97 201.79 63 C 201.97 64.03 202.28 65.22 202.73 66.58 C 202.79 66.72 202.79 66.84 202.75 66.93 C 202.71 67.03 202.6 67.12 202.44 67.2 C 202.3 67.28 202.17 67.3 202.04 67.24 C 201.91 67.18 201.82 67.09 201.77 66.95 C 201.35 65.78 201.04 64.68 200.83 63.64 C 200.62 62.6 200.52 61.54 200.52 60.45 C 200.52 56.81 201.85 53.76 204.5 51.3 C 207.15 48.84 210.31 47.61 213.98 47.61 C 217.65 47.61 220.86 48.84 223.52 51.3 C 226.19 53.76 227.52 56.81 227.52 60.45 C 227.52 61.73 227.05 62.81 226.1 63.7 C 225.16 64.59 224.02 65.03 222.68 65.03 C 221.34 65.03 220.24 64.59 219.28 63.7 C 218.32 62.81 217.84 61.73 217.84 60.45 C 217.84 59.42 217.46 58.56 216.72 57.87 C 215.98 57.18 215.08 56.83 214.05 56.83 C 213.02 56.83 212.08 57.18 211.32 57.87 C 210.56 58.56 210.17 59.43 210.17 60.45 C 210.17 63.26 211.01 65.6 212.69 67.47 C 214.37 69.35 216.49 70.66 219.04 71.41 C 219.21 71.47 219.32 71.55 219.39 71.66 C 219.46 71.77 219.47 71.9 219.41 72.04 C 219.35 72.15 219.28 72.25 219.18 72.35 C 219.08 72.45 218.94 72.47 218.74 72.41 Z M 204.42 43.04 C 204.36 43.04 204.27 43.03 204.15 43 C 204.02 42.97 203.95 42.9 203.92 42.79 C 203.86 42.71 203.84 42.6 203.86 42.46 C 203.87 42.32 203.92 42.22 204.01 42.17 C 205.54 41.31 207.16 40.66 208.86 40.23 C 210.57 39.8 212.3 39.58 214.05 39.58 C 215.8 39.58 217.51 39.79 219.17 40.2 C 220.84 40.62 222.45 41.24 224 42.08 C 224.17 42.16 224.27 42.27 224.31 42.41 C 224.35 42.55 224.35 42.67 224.29 42.79 C 224.23 42.9 224.14 42.98 224.02 43.04 C 223.89 43.1 223.78 43.1 223.67 43.04 C 222.2 42.18 220.65 41.55 219.02 41.16 C 217.39 40.77 215.74 40.58 214.04 40.58 C 212.34 40.58 210.7 40.8 209.1 41.23 C 207.5 41.66 205.94 42.26 204.41 43.04 Z M 209.92 71.96 C 208.36 70.29 207.14 68.55 206.25 66.73 C 205.36 64.91 204.92 62.82 204.92 60.46 C 204.92 58.1 205.82 55.91 207.61 54.23 C 209.4 52.55 211.55 51.71 214.05 51.71 C 216.55 51.71 218.7 52.55 220.49 54.23 C 222.28 55.91 223.18 57.99 223.18 60.46 C 223.18 60.6 223.14 60.72 223.06 60.81 C 222.98 60.91 222.85 60.96 222.68 60.96 C 222.6 60.96 222.49 60.91 222.37 60.81 C 222.25 60.71 222.18 60.6 222.18 60.46 C 222.18 58.27 221.38 56.43 219.78 54.94 C 218.18 53.45 216.27 52.71 214.05 52.71 C 211.83 52.71 209.92 53.45 208.32 54.94 C 206.72 56.43 205.92 58.27 205.92 60.46 C 205.92 62.77 206.32 64.72 207.13 66.34 C 207.94 67.95 209.1 69.58 210.63 71.22 C 210.77 71.39 210.82 71.53 210.78 71.66 C 210.74 71.78 210.69 71.89 210.63 71.97 C 210.55 72.05 210.44 72.1 210.3 72.12 C 210.16 72.13 210.04 72.09 209.92 71.97 Z M 222.34 68.96 C 219.95 68.96 217.88 68.15 216.11 66.54 C 214.35 64.93 213.46 62.9 213.46 60.46 C 213.46 60.32 213.5 60.2 213.58 60.11 C 213.66 60.01 213.79 59.96 213.96 59.96 C 214.1 59.96 214.22 60.01 214.31 60.11 C 214.41 60.21 214.46 60.33 214.46 60.46 C 214.46 62.65 215.24 64.45 216.81 65.86 C 218.38 67.26 220.22 67.96 222.33 67.96 C 222.64 67.96 222.95 67.95 223.27 67.92 C 223.59 67.89 223.9 67.85 224.21 67.8 C 224.32 67.8 224.42 67.83 224.5 67.88 C 224.58 67.93 224.64 68.03 224.67 68.17 C 224.73 68.31 224.71 68.43 224.63 68.52 C 224.55 68.62 224.42 68.68 224.25 68.71 C 224 68.79 223.7 68.86 223.35 68.9 C 223 68.94 222.66 68.96 222.33 68.96 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <path + android:name="path_2" + android:pathData="M 134 72 L 134 60 C 134 46.75 144.75 36 158 36 C 171.25 36 182 46.75 182 60 L 182 72 M 124 72 L 192 72 C 195.712 72 199.275 73.476 201.899 76.101 C 204.524 78.725 206 82.288 206 86 L 206 146 C 206 149.712 204.524 153.275 201.899 155.899 C 199.275 158.524 195.712 160 192 160 L 124 160 C 120.288 160 116.725 158.524 114.101 155.899 C 111.476 153.275 110 149.712 110 146 L 110 86 C 110 82.288 111.476 78.725 114.101 76.101 C 116.725 73.476 120.288 72 124 72" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <path + android:name="path_3" + android:pathData="M 155.67 114.83 C 153.22 114.83 151.15 113.98 149.46 112.29 C 147.77 110.6 146.92 108.53 146.92 106.08 C 146.92 103.63 147.77 101.56 149.46 99.87 C 151.15 98.18 153.22 97.33 155.67 97.33 C 158.12 97.33 160.19 98.18 161.88 99.87 C 163.57 101.56 164.42 103.63 164.42 106.08 C 164.42 108.53 163.57 110.6 161.88 112.29 C 160.19 113.98 158.12 114.83 155.67 114.83 Z M 172.59 137 L 169.67 133.5 L 169.67 124.92 C 168.27 124.38 167.14 123.51 166.29 122.32 C 165.44 121.13 165.01 119.8 165.01 118.32 C 165.01 116.38 165.69 114.72 167.05 113.36 C 168.41 112 170.06 111.32 172.01 111.32 C 173.96 111.32 175.61 112 176.97 113.36 C 178.33 114.72 179.01 116.37 179.01 118.32 C 179.01 119.8 178.58 121.13 177.73 122.32 C 176.88 123.51 175.75 124.37 174.35 124.92 L 174.35 125.33 L 176.68 127.66 L 174.35 129.99 L 176.68 132.32 L 172.6 136.99 Z M 172.01 121.83 C 172.98 121.83 173.81 121.49 174.49 120.81 C 175.17 120.13 175.51 119.3 175.51 118.33 C 175.51 117.36 175.17 116.53 174.49 115.85 C 173.81 115.17 172.98 114.83 172.01 114.83 C 171.04 114.83 170.21 115.17 169.53 115.85 C 168.85 116.53 168.51 117.36 168.51 118.33 C 168.51 119.3 168.85 120.13 169.53 120.81 C 170.21 121.49 171.04 121.83 172.01 121.83 Z M 160.46 119.85 C 160.65 121.44 161.15 122.9 161.95 124.23 C 162.75 125.55 163.77 126.7 165.01 127.67 L 165.01 134.67 L 137.01 134.67 L 137.01 129.13 C 137.01 127.81 137.38 126.6 138.12 125.51 C 138.86 124.42 139.81 123.59 140.98 123 C 143.27 121.83 145.66 120.96 148.13 120.38 C 150.6 119.8 153.12 119.5 155.68 119.5 C 156.46 119.5 157.26 119.53 158.07 119.59 C 158.89 119.65 159.68 119.74 160.46 119.85 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <path + android:name="path_4" + android:pathData="M 63.481 27.427 L 102.118 17.075 C 102.457 16.984 102.815 16.984 103.154 17.075 C 103.493 17.166 103.802 17.344 104.05 17.592 C 104.298 17.841 104.477 18.15 104.568 18.489 L 110.779 41.671 C 110.87 42.01 110.87 42.367 110.779 42.706 C 110.689 43.045 110.51 43.355 110.262 43.603 C 110.014 43.851 109.704 44.03 109.365 44.121 L 70.728 54.473 C 70.216 54.611 69.67 54.539 69.211 54.274 C 68.751 54.008 68.416 53.571 68.279 53.059 L 62.067 29.877 C 61.976 29.538 61.976 29.181 62.067 28.842 C 62.158 28.503 62.336 28.193 62.585 27.945 C 62.833 27.697 63.142 27.518 63.481 27.427" + android:strokeColor="#444746" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_5" + android:pathData="M 86.189 50.327 L 93.917 48.256 L 94.952 52.12 L 87.225 54.191 Z" + android:fillColor="#444746" + android:strokeWidth="1"/> + <path + android:name="path_6" + android:pathData="M 83.36 55.23 L 98.81 51.08" + android:strokeColor="#444746" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_7" + android:pathData="M 90.9 37.2 L 88.3 35.7 L 89.4 33.8 C 89.59 33.48 89.37 33.07 89 33.05 L 81.88 32.62 C 81.47 32.6 81.21 33.05 81.43 33.39 L 85.36 39.34 C 85.56 39.65 86.02 39.64 86.21 39.31 L 87.31 37.41 L 89.91 38.91 C 90.39 39.19 91 39.02 91.28 38.54 C 91.56 38.06 91.39 37.45 90.91 37.17 Z" + android:fillColor="#444746" + android:strokeWidth="1"/> + <path + android:name="path_8" + android:pathData="M 86 160 L 222 160" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <group android:name="group"> + <path + android:name="path_9" + android:pathData="M 98 80 C 92.088 80 86.325 81.872 81.542 85.348 C 76.759 88.823 73.197 93.725 71.37 99.348 C 69.543 104.97 69.543 111.03 71.37 116.652 C 73.197 122.275 76.759 127.177 81.542 130.652 C 86.325 134.128 92.088 136 98 136 C 103.912 136 109.675 134.128 114.458 130.652 C 119.241 127.177 122.803 122.275 124.63 116.652 C 126.457 111.03 126.457 104.97 124.63 99.348 C 122.803 93.725 119.241 88.823 114.458 85.348 C 109.675 81.872 103.912 80 98 80 Z" + android:fillColor="#37be5f" + android:strokeWidth="1"/> + <path + android:name="path_10" + android:pathData="M 98 90.5 C 93.36 90.5 88.906 92.345 85.626 95.626 C 82.345 98.906 80.5 103.36 80.5 108 C 80.5 112.64 82.345 117.094 85.626 120.374 C 88.906 123.655 93.36 125.5 98 125.5 C 102.64 125.5 107.094 123.655 110.374 120.374 C 113.655 117.094 115.5 112.64 115.5 108 C 115.5 103.36 113.655 98.906 110.374 95.626 C 107.094 92.345 102.64 90.5 98 90.5 Z" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeMiterLimit="10"/> + <path + android:name="path_11" + android:pathData="M 92.31 104.5 C 92.31 105.22 91.72 105.81 91 105.81 C 90.28 105.81 89.69 105.22 89.69 104.5 C 89.69 103.78 90.28 103.19 91 103.19 C 91.72 103.19 92.31 103.78 92.31 104.5 Z M 106.31 104.5 C 106.31 105.22 105.72 105.81 105 105.81 C 104.28 105.81 103.69 105.22 103.69 104.5 C 103.69 103.78 104.28 103.19 105 103.19 C 105.72 103.19 106.31 103.78 106.31 104.5 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + <path + android:name="path_12" + android:pathData="M 98.88 107.12 L 98.88 110.62 L 96.61 110.62 M 100.11 116.16 C 98.94 117.33 97.06 117.33 95.89 116.16 C 95.29 115.56 95 114.76 95.02 113.97" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeMiterLimit="10"/> + </group> + <group android:name="group_2"> + <path + android:name="path_13" + android:pathData="M 264.23 104 L 296.23 104" + android:strokeColor="#444746" + android:strokeWidth="1" + android:strokeLineCap="round" + android:strokeMiterLimit="10"/> + <path + android:name="path_14" + android:pathData="M 273.16 81.21 C 272.82 80.19 271.87 79.5 270.79 79.5 L 243.01 79.5 C 242.2 79.5 241.46 79.88 240.98 80.54 C 240.5 81.2 240.38 82.02 240.64 82.79 L 247.31 102.79 C 247.65 103.81 248.6 104.5 249.68 104.5 L 280.93 104.5 L 273.17 81.21 Z M 248.25 87.93 C 247.45 88.14 246.57 87.45 246.28 86.39 C 245.99 85.33 246.41 84.28 247.21 84.07 C 248.01 83.86 248.89 84.55 249.18 85.61 C 249.47 86.68 249.05 87.72 248.25 87.93 Z" + android:fillColor="#444746" + android:strokeWidth="1"/> + <group android:name="group_1"> + <path + android:name="path_15" + android:pathData="M 279 83 L 285 83 M 277 77 L 285 69 M 271 75 L 271 69" + android:strokeColor="#444746" + android:strokeWidth="1" + android:strokeLineCap="round"/> + </group> + </group> + <path + android:name="path_16" + android:pathData="M 58.16 105.86 C 57.83 105.28 57.29 104.87 56.64 104.69 L 45.05 101.58 C 43.72 101.22 42.34 102.02 41.99 103.35 L 35.78 126.53 C 35.61 127.17 35.7 127.85 36.03 128.43 C 36.36 129.01 36.9 129.42 37.55 129.6 L 49.14 132.71 C 49.36 132.77 49.57 132.79 49.79 132.79 C 50.89 132.79 51.91 132.05 52.21 130.94 L 58.42 107.76 C 58.59 107.11 58.5 106.44 58.17 105.86 Z M 50.65 107.75 C 50.51 108.28 49.96 108.6 49.43 108.46 C 48.9 108.32 48.58 107.77 48.72 107.24 C 48.86 106.71 49.41 106.39 49.94 106.53 C 50.47 106.67 50.79 107.22 50.65 107.75 Z" + android:fillColor="#444746" + android:strokeWidth="1"/> + <path + android:name="path_17" + android:pathData="M 230 160 L 246 160" + android:strokeColor="#e1e3e1" + android:strokeWidth="1" + android:strokeLineCap="round"/> + <path + android:name="path_18" + android:pathData="M 204.63 80 C 202.38 75.27 197.58 72 192 72 L 190 72 L 190 74 C 190 77.31 192.69 80 196 80 L 204.63 80 Z" + android:fillColor="#e1e3e1" + android:strokeWidth="1"/> + <group android:name="group_4"> + <path + android:name="path_19" + android:pathData="M 211.37 102.29 C 210.53 102.82 209.46 102.82 208.63 102.29 C 202.93 98.67 195.29 99.35 190.31 104.32 C 185.34 109.29 184.67 116.93 188.28 122.63 C 188.81 123.46 188.81 124.54 188.28 125.38 C 184.66 131.08 185.34 138.72 190.31 143.69 C 195.29 148.66 202.93 149.34 208.63 145.72 C 209.46 145.19 210.54 145.19 211.37 145.72 C 217.07 149.34 224.71 148.66 229.68 143.69 C 234.66 138.72 235.33 131.08 231.71 125.38 C 231.18 124.55 231.18 123.47 231.71 122.63 C 235.33 116.93 234.65 109.29 229.68 104.32 C 224.71 99.35 217.07 98.67 211.37 102.29 Z" + android:fillColor="#7cacf8" + android:strokeWidth="1" + android:fillType="evenOdd"/> + <group android:name="group_3"> + <path + android:name="path_20" + android:pathData="M 198 110 C 197.47 110 196.961 110.211 196.586 110.586 C 196.211 110.961 196 111.47 196 112 C 196 112.53 196.211 113.039 196.586 113.414 C 196.961 113.789 197.47 114 198 114 C 198.53 114 199.039 113.789 199.414 113.414 C 199.789 113.039 200 112.53 200 112 C 200 111.47 199.789 110.961 199.414 110.586 C 199.039 110.211 198.53 110 198 110 Z M 210 110 C 209.47 110 208.961 110.211 208.586 110.586 C 208.211 110.961 208 111.47 208 112 C 208 112.53 208.211 113.039 208.586 113.414 C 208.961 113.789 209.47 114 210 114 C 210.53 114 211.039 113.789 211.414 113.414 C 211.789 113.039 212 112.53 212 112 C 212 111.47 211.789 110.961 211.414 110.586 C 211.039 110.211 210.53 110 210 110 Z M 222 110 C 221.47 110 220.961 110.211 220.586 110.586 C 220.211 110.961 220 111.47 220 112 C 220 112.53 220.211 113.039 220.586 113.414 C 220.961 113.789 221.47 114 222 114 C 222.53 114 223.039 113.789 223.414 113.414 C 223.789 113.039 224 112.53 224 112 C 224 111.47 223.789 110.961 223.414 110.586 C 223.039 110.211 222.53 110 222 110 Z M 198 122 C 197.47 122 196.961 122.211 196.586 122.586 C 196.211 122.961 196 123.47 196 124 C 196 124.53 196.211 125.039 196.586 125.414 C 196.961 125.789 197.47 126 198 126 C 198.53 126 199.039 125.789 199.414 125.414 C 199.789 125.039 200 124.53 200 124 C 200 123.47 199.789 122.961 199.414 122.586 C 199.039 122.211 198.53 122 198 122 Z M 210 122 C 209.47 122 208.961 122.211 208.586 122.586 C 208.211 122.961 208 123.47 208 124 C 208 124.53 208.211 125.039 208.586 125.414 C 208.961 125.789 209.47 126 210 126 C 210.53 126 211.039 125.789 211.414 125.414 C 211.789 125.039 212 124.53 212 124 C 212 123.47 211.789 122.961 211.414 122.586 C 211.039 122.211 210.53 122 210 122 Z M 222 122 C 221.47 122 220.961 122.211 220.586 122.586 C 220.211 122.961 220 123.47 220 124 C 220 124.53 220.211 125.039 220.586 125.414 C 220.961 125.789 221.47 126 222 126 C 222.53 126 223.039 125.789 223.414 125.414 C 223.789 125.039 224 124.53 224 124 C 224 123.47 223.789 122.961 223.414 122.586 C 223.039 122.211 222.53 122 222 122 Z M 198 134 C 197.47 134 196.961 134.211 196.586 134.586 C 196.211 134.961 196 135.47 196 136 C 196 136.53 196.211 137.039 196.586 137.414 C 196.961 137.789 197.47 138 198 138 C 198.53 138 199.039 137.789 199.414 137.414 C 199.789 137.039 200 136.53 200 136 C 200 135.47 199.789 134.961 199.414 134.586 C 199.039 134.211 198.53 134 198 134 Z M 210 134 C 209.47 134 208.961 134.211 208.586 134.586 C 208.211 134.961 208 135.47 208 136 C 208 136.53 208.211 137.039 208.586 137.414 C 208.961 137.789 209.47 138 210 138 C 210.53 138 211.039 137.789 211.414 137.414 C 211.789 137.039 212 136.53 212 136 C 212 135.47 211.789 134.961 211.414 134.586 C 211.039 134.211 210.53 134 210 134 Z M 222 134 C 221.47 134 220.961 134.211 220.586 134.586 C 220.211 134.961 220 135.47 220 136 C 220 136.53 220.211 137.039 220.586 137.414 C 220.961 137.789 221.47 138 222 138 C 222.53 138 223.039 137.789 223.414 137.414 C 223.789 137.039 224 136.53 224 136 C 224 135.47 223.789 134.961 223.414 134.586 C 223.039 134.211 222.53 134 222 134 Z" + android:fillColor="#1f1f1f" + android:strokeWidth="1"/> + </group> + <path + android:name="path_21" + android:pathData="M 222 112 L 198 136 L 222 136" + android:strokeColor="#1f1f1f" + android:strokeWidth="1" + android:strokeLineCap="round"/> + </group> +</vector> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 558c229743c8..823ee5b1f5e4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -5,6 +5,7 @@ package com.android.credentialmanager.createflow import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -165,8 +166,16 @@ fun ConfirmationCard( ) { ContainerCard() { Column() { + val onboardingImageResource = remember { + mutableStateOf(R.drawable.ic_passkeys_onboarding) + } + if (isSystemInDarkTheme()) { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark + } else { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding + } Image( - painter = painterResource(R.drawable.ic_passkeys_onboarding), + painter = painterResource(onboardingImageResource.value), contentDescription = null, modifier = Modifier.align(alignment = Alignment.CenterHorizontally) .padding(top = 24.dp, bottom = 12.dp).size(316.dp, 168.dp) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt index e8b5b19daad0..f6bb3cc271fe 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -119,7 +119,6 @@ internal fun CustomizedLargeTopAppBar( actions = actions, colors = topAppBarColors(), windowInsets = TopAppBarDefaults.windowInsets, - maxHeightWithoutTitle = 120.dp, pinnedHeight = ContainerHeight, scrollBehavior = scrollBehavior, ) @@ -261,7 +260,7 @@ private fun SingleRowTopAppBar( * A two-rows top app bar that is designed to be called by the Large and Medium top app bar * composables. * - * @throws [IllegalArgumentException] if the given [maxHeightWithoutTitle] is equal or smaller than + * @throws [IllegalArgumentException] if the given [MaxHeightWithoutTitle] is equal or smaller than * the [pinnedHeight] */ @OptIn(ExperimentalMaterial3Api::class) @@ -277,11 +276,10 @@ private fun TwoRowsTopAppBar( actions: @Composable RowScope.() -> Unit, windowInsets: WindowInsets, colors: TopAppBarColors, - maxHeightWithoutTitle: Dp, pinnedHeight: Dp, scrollBehavior: TopAppBarScrollBehavior? ) { - if (maxHeightWithoutTitle <= pinnedHeight) { + if (MaxHeightWithoutTitle <= pinnedHeight) { throw IllegalArgumentException( "A TwoRowsTopAppBar max height should be greater than its pinned height" ) @@ -289,7 +287,7 @@ private fun TwoRowsTopAppBar( val pinnedHeightPx: Float val density = LocalDensity.current val maxHeightPx = density.run { - remember { mutableStateOf((maxHeightWithoutTitle + pinnedHeight).toPx()) } + remember { mutableStateOf((MaxHeightWithoutTitle + DefaultTitleHeight).toPx()) } } val titleBottomPaddingPx: Int density.run { @@ -380,7 +378,7 @@ private fun TwoRowsTopAppBar( Box(modifier = Modifier.onGloballyPositioned { coordinates -> density.run { maxHeightPx.value = - maxHeightWithoutTitle.toPx() + coordinates.size.height.toFloat() + MaxHeightWithoutTitle.toPx() + coordinates.size.height.toFloat() } }) { title() } }, @@ -610,6 +608,8 @@ private suspend fun settleAppBar( // Medium or Large app bar. private val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f) +private val MaxHeightWithoutTitle = 124.dp +private val DefaultTitleHeight = 52.dp private val ContainerHeight = 56.dp private val LargeTitleBottomPadding = 28.dp private val TopAppBarHorizontalPadding = 4.dp diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 333aebad44ae..bdd941d537bf 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -49,13 +49,16 @@ }, { // Permission indicators - "name": "CtsPermission4TestCases", + "name": "CtsPermission3TestCases", "options": [ { "exclude-annotation": "org.junit.Ignore" }, { "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "include-filter": "android.permission3.cts.CameraMicIndicatorsPermissionTest" } ] }, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java index 102f2082ebd1..055cd52b23d6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java @@ -16,19 +16,23 @@ package com.android.systemui.dreams; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; + import android.util.Log; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback; import com.android.systemui.dreams.conditions.DreamCondition; import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.util.condition.ConditionalCoreStartable; import javax.inject.Inject; +import javax.inject.Named; /** * A {@link CoreStartable} to retain a monitor for tracking dreaming. */ -public class DreamMonitor implements CoreStartable { +public class DreamMonitor extends ConditionalCoreStartable { private static final String TAG = "DreamMonitor"; // We retain a reference to the monitor so it is not garbage-collected. @@ -39,14 +43,17 @@ public class DreamMonitor implements CoreStartable { @Inject public DreamMonitor(Monitor monitor, DreamCondition dreamCondition, + @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor, DreamStatusBarStateCallback callback) { + super(pretextMonitor); mConditionMonitor = monitor; mDreamCondition = dreamCondition; mCallback = callback; } + @Override - public void start() { + protected void onStart() { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "started"); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java index 87c5f51ce13a..a2dcdf52ad3c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams; import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -33,8 +34,9 @@ import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.util.Log; -import com.android.systemui.CoreStartable; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.util.condition.ConditionalCoreStartable; import javax.inject.Inject; import javax.inject.Named; @@ -43,7 +45,7 @@ import javax.inject.Named; * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be * the designated dream overlay component. */ -public class DreamOverlayRegistrant implements CoreStartable { +public class DreamOverlayRegistrant extends ConditionalCoreStartable { private static final String TAG = "DreamOverlayRegistrant"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final IDreamManager mDreamManager; @@ -102,7 +104,9 @@ public class DreamOverlayRegistrant implements CoreStartable { @Inject public DreamOverlayRegistrant(Context context, @Main Resources resources, - @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent) { + @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent, + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + super(monitor); mContext = context; mResources = resources; mDreamManager = IDreamManager.Stub.asInterface( @@ -111,7 +115,7 @@ public class DreamOverlayRegistrant implements CoreStartable { } @Override - public void start() { + protected void onStart() { final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); filter.addDataSchemeSpecificPart(mOverlayServiceComponent.getPackageName(), diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java index ee2f1af6a99b..244212b45790 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java @@ -16,27 +16,31 @@ package com.android.systemui.dreams.complication; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; + import android.database.ContentObserver; import android.os.UserHandle; import android.provider.Settings; import com.android.settingslib.dream.DreamBackend; -import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.util.condition.ConditionalCoreStartable; import com.android.systemui.util.settings.SecureSettings; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Named; /** * {@link ComplicationTypesUpdater} observes the state of available complication types set by the * user, and pushes updates to {@link DreamOverlayStateController}. */ @SysUISingleton -public class ComplicationTypesUpdater implements CoreStartable { +public class ComplicationTypesUpdater extends ConditionalCoreStartable { private final DreamBackend mDreamBackend; private final Executor mExecutor; private final SecureSettings mSecureSettings; @@ -48,7 +52,9 @@ public class ComplicationTypesUpdater implements CoreStartable { DreamBackend dreamBackend, @Main Executor executor, SecureSettings secureSettings, - DreamOverlayStateController dreamOverlayStateController) { + DreamOverlayStateController dreamOverlayStateController, + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + super(monitor); mDreamBackend = dreamBackend; mExecutor = executor; mSecureSettings = secureSettings; @@ -56,7 +62,7 @@ public class ComplicationTypesUpdater implements CoreStartable { } @Override - public void start() { + public void onStart() { final ContentObserver settingsObserver = new ContentObserver(null /*handler*/) { @Override public void onChange(boolean selfChange) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java index 77e1fc91e6ee..bb1e6e2ef06d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java @@ -18,11 +18,14 @@ package com.android.systemui.dreams.complication; import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW; import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; import android.view.View; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.util.condition.ConditionalCoreStartable; import javax.inject.Inject; import javax.inject.Named; @@ -60,7 +63,7 @@ public class DreamClockTimeComplication implements Complication { * {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with * SystemUI. */ - public static class Registrant implements CoreStartable { + public static class Registrant extends ConditionalCoreStartable { private final DreamOverlayStateController mDreamOverlayStateController; private final DreamClockTimeComplication mComplication; @@ -70,13 +73,15 @@ public class DreamClockTimeComplication implements Complication { @Inject public Registrant( DreamOverlayStateController dreamOverlayStateController, - DreamClockTimeComplication dreamClockTimeComplication) { + DreamClockTimeComplication dreamClockTimeComplication, + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + super(monitor); mDreamOverlayStateController = dreamOverlayStateController; mComplication = dreamClockTimeComplication; } @Override - public void start() { + public void onStart() { mDreamOverlayStateController.addComplication(mComplication); } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java index 1065b94508f8..7f395d863c3f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java @@ -21,6 +21,7 @@ import static com.android.systemui.controls.dagger.ControlsComponent.Visibility. import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE; import static com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW; import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; import android.content.Context; import android.content.Intent; @@ -42,7 +43,9 @@ import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.shared.condition.Monitor; import com.android.systemui.util.ViewController; +import com.android.systemui.util.condition.ConditionalCoreStartable; import java.util.List; @@ -75,7 +78,7 @@ public class DreamHomeControlsComplication implements Complication { /** * {@link CoreStartable} for registering the complication with SystemUI on startup. */ - public static class Registrant implements CoreStartable { + public static class Registrant extends ConditionalCoreStartable { private final DreamHomeControlsComplication mComplication; private final DreamOverlayStateController mDreamOverlayStateController; private final ControlsComponent mControlsComponent; @@ -105,14 +108,16 @@ public class DreamHomeControlsComplication implements Complication { @Inject public Registrant(DreamHomeControlsComplication complication, DreamOverlayStateController dreamOverlayStateController, - ControlsComponent controlsComponent) { + ControlsComponent controlsComponent, + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + super(monitor); mComplication = complication; mControlsComponent = controlsComponent; mDreamOverlayStateController = dreamOverlayStateController; } @Override - public void start() { + public void onStart() { mControlsComponent.getControlsListingController().ifPresent( c -> c.addCallback(mControlsCallback)); mDreamOverlayStateController.addCallback(mOverlayStateCallback); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java index c3aaf0cbf2d7..e39073bb6711 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java @@ -17,6 +17,7 @@ package com.android.systemui.dreams.complication; import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_SMARTSPACE_LAYOUT_PARAMS; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR; import android.content.Context; import android.os.Parcelable; @@ -28,6 +29,8 @@ import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; import com.android.systemui.plugins.BcSmartspaceDataPlugin; +import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.util.condition.ConditionalCoreStartable; import java.util.List; @@ -61,7 +64,7 @@ public class SmartSpaceComplication implements Complication { * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with * SystemUI. */ - public static class Registrant implements CoreStartable { + public static class Registrant extends ConditionalCoreStartable { private final DreamSmartspaceController mSmartSpaceController; private final DreamOverlayStateController mDreamOverlayStateController; private final SmartSpaceComplication mComplication; @@ -81,14 +84,16 @@ public class SmartSpaceComplication implements Complication { public Registrant( DreamOverlayStateController dreamOverlayStateController, SmartSpaceComplication smartSpaceComplication, - DreamSmartspaceController smartSpaceController) { + DreamSmartspaceController smartSpaceController, + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + super(monitor); mDreamOverlayStateController = dreamOverlayStateController; mComplication = smartSpaceComplication; mSmartSpaceController = smartSpaceController; } @Override - public void start() { + public void onStart() { mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index 7d8389a470b2..3a37c6f1542c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -30,11 +30,18 @@ import com.android.systemui.dreams.DreamOverlayService; import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule; import com.android.systemui.dreams.dreamcomplication.dagger.ComplicationComponent; import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule; +import com.android.systemui.process.condition.UserProcessCondition; +import com.android.systemui.shared.condition.Condition; +import com.android.systemui.shared.condition.Monitor; +import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.IntoSet; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.Executor; import javax.inject.Named; @@ -57,6 +64,8 @@ public interface DreamModule { String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled"; String DREAM_SUPPORTED = "dream_supported"; + String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions"; + String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor"; /** * Provides the dream component @@ -115,4 +124,19 @@ public interface DreamModule { static boolean providesDreamSupported(@Main Resources resources) { return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported); } + + /** */ + @Binds + @IntoSet + @Named(DREAM_PRETEXT_CONDITIONS) + Condition bindsUserProcessCondition(UserProcessCondition condition); + + /** */ + @Provides + @Named(DREAM_PRETEXT_MONITOR) + static Monitor providesDockerPretextMonitor( + @Main Executor executor, + @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) { + return new Monitor(executor, pretextConditions); + } } diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java index 7db293d96a50..245cf89a8337 100644 --- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java @@ -16,11 +16,16 @@ package com.android.systemui.process; +import javax.inject.Inject; + /** * A simple wrapper that provides access to process-related details. This facilitates testing by * providing a mockable target around these details. */ public class ProcessWrapper { + @Inject + public ProcessWrapper() {} + public int getUserHandleIdentifier() { return android.os.Process.myUserHandle().getIdentifier(); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index f53f8243131c..9286d29f5070 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -2144,6 +2144,7 @@ public final class NotificationPanelViewController implements Dumpable { void flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { mLastFlingWasExpanding = expand; + mShadeLog.logLastFlingWasExpanding(expand); mHeadsUpTouchHelper.notifyFling(!expand); mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */); setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); @@ -4624,6 +4625,7 @@ public final class NotificationPanelViewController implements Dumpable { ipw.println(mBlockingExpansionForCurrentTouch); ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown); ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown); + ipw.print("mLastFlingWasExpanding="); ipw.println(mLastFlingWasExpanding); ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount); ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount); ipw.print("mPulsing="); ipw.println(mPulsing); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt index 11617be40f53..26c839de08a1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt @@ -20,7 +20,6 @@ import android.view.MotionEvent import com.android.systemui.log.dagger.ShadeLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel -import com.android.systemui.plugins.log.LogMessage import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -234,4 +233,19 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) { } ) } + + fun logLastFlingWasExpanding( + expand: Boolean + ) { + buffer.log( + TAG, + LogLevel.VERBOSE, + { + bool1 = expand + }, + { + "NPVC mLastFlingWasExpanding set to: $bool1" + } + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java index 058042c4bccd..0c95eab979a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java @@ -71,11 +71,9 @@ public class DeviceProvisionedCoordinator implements Coordinator { * marking them as relevant for setup are allowed to show when device is unprovisioned */ private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) { - final boolean hasPermission = checkUidPermission( - Manifest.permission.NOTIFICATION_DURING_SETUP, - sbn.getUid()) == PackageManager.PERMISSION_GRANTED; - return hasPermission - && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP); + // system_server checks the permission so systemui can just check whether the + // extra exists + return sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP); } private int checkUidPermission(String permission, int uid) { diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt new file mode 100644 index 000000000000..c1f0c58e5644 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt @@ -0,0 +1,90 @@ +/* + * 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.theme + +import android.util.Pair +import com.android.systemui.monet.dynamiccolor.DynamicColor +import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors as MDC + +class DynamicColors { + companion object { + @JvmField + val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> = + arrayListOf( + Pair.create("primary_container", MDC.primaryContainer), + Pair.create("on_primary_container", MDC.onPrimaryContainer), + Pair.create("primary", MDC.primary), + Pair.create("on_primary", MDC.onPrimary), + Pair.create("secondary_container", MDC.secondaryContainer), + Pair.create("on_secondary_container", MDC.onSecondaryContainer), + Pair.create("secondary", MDC.secondary), + Pair.create("on_secondary", MDC.onSecondary), + Pair.create("tertiary_container", MDC.tertiaryContainer), + Pair.create("on_tertiary_container", MDC.onTertiaryContainer), + Pair.create("tertiary", MDC.tertiary), + Pair.create("on_tertiary", MDC.onTertiary), + Pair.create("background", MDC.background), + Pair.create("on_background", MDC.onBackground), + Pair.create("surface", MDC.surface), + Pair.create("on_surface", MDC.onSurface), + Pair.create("surface_container_low", MDC.surfaceContainerLow), + Pair.create("surface_container_lowest", MDC.surfaceContainerLowest), + Pair.create("surface_container", MDC.surfaceContainer), + Pair.create("surface_container_high", MDC.surfaceContainerHigh), + Pair.create("surface_container_highest", MDC.surfaceContainerHighest), + Pair.create("surface_bright", MDC.surfaceBright), + Pair.create("surface_dim", MDC.surfaceDim), + Pair.create("surface_variant", MDC.surfaceVariant), + Pair.create("on_surface_variant", MDC.onSurfaceVariant), + Pair.create("outline", MDC.outline), + Pair.create("error", MDC.error), + Pair.create("on_error", MDC.onError), + Pair.create("error_container", MDC.errorContainer), + Pair.create("on_error_container", MDC.onErrorContainer), + Pair.create("primary_fixed", MDC.primaryFixed), + Pair.create("primary_fixed_darker", MDC.primaryFixedDarker), + Pair.create("on_primary_fixed", MDC.onPrimaryFixed), + Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant), + Pair.create("secondary_fixed", MDC.secondaryFixed), + Pair.create("secondary_fixed_darker", MDC.secondaryFixedDarker), + Pair.create("on_secondary_fixed", MDC.onSecondaryFixed), + Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant), + Pair.create("tertiary_fixed", MDC.tertiaryFixed), + Pair.create("tertiary_fixed_darker", MDC.tertiaryFixedDarker), + Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed), + Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant), + Pair.create("control_activated", MDC.controlActivated), + Pair.create("control_normal", MDC.controlNormal), + Pair.create("control_highlight", MDC.controlHighlight), + Pair.create("text_primary_inverse", MDC.textPrimaryInverse), + Pair.create( + "text_secondary_and_tertiary_inverse", + MDC.textSecondaryAndTertiaryInverse + ), + Pair.create("text_primary_inverse_disable_only", MDC.textPrimaryInverseDisableOnly), + Pair.create( + "text_secondary_and_tertiary_inverse_disabled", + MDC.textSecondaryAndTertiaryInverseDisabled + ), + Pair.create("text_hint_inverse", MDC.textHintInverse), + Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor), + Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor), + Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor), + Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor), + Pair.create("palette_key_color_neutral_variant", MDC.neutralVariantPaletteKeyColor) + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index ba39367e290e..3376e232e035 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -65,6 +65,8 @@ public class ThemeOverlayApplier implements Dumpable { @VisibleForTesting static final String SYSUI_PACKAGE = "com.android.systemui"; + static final String OVERLAY_CATEGORY_DYNAMIC_COLOR = + "android.theme.customization.dynamic_color"; static final String OVERLAY_CATEGORY_ACCENT_COLOR = "android.theme.customization.accent_color"; static final String OVERLAY_CATEGORY_SYSTEM_PALETTE = @@ -117,6 +119,7 @@ public class ThemeOverlayApplier implements Dumpable { OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_ACCENT_COLOR, + OVERLAY_CATEGORY_DYNAMIC_COLOR, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, OVERLAY_CATEGORY_ICON_SETTINGS, @@ -127,6 +130,7 @@ public class ThemeOverlayApplier implements Dumpable { static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet( OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, + OVERLAY_CATEGORY_DYNAMIC_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID, @@ -153,6 +157,7 @@ public class ThemeOverlayApplier implements Dumpable { mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, + OVERLAY_CATEGORY_DYNAMIC_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); mTargetPackageToCategories.put(SYSUI_PACKAGE, @@ -164,6 +169,7 @@ public class ThemeOverlayApplier implements Dumpable { mTargetPackageToCategories.put(mThemePickerPackage, Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER)); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_DYNAMIC_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 6b507ba9d9e6..2ad3558fdb16 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -15,11 +15,14 @@ */ package com.android.systemui.theme; +import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; + import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; +import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_BOTH; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX; @@ -51,7 +54,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; -import android.util.TypedValue; +import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -70,6 +73,16 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.monet.ColorScheme; import com.android.systemui.monet.Style; import com.android.systemui.monet.TonalPalette; +import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors; +import com.android.systemui.monet.hct.Hct; +import com.android.systemui.monet.scheme.DynamicScheme; +import com.android.systemui.monet.scheme.SchemeExpressive; +import com.android.systemui.monet.scheme.SchemeFruitSalad; +import com.android.systemui.monet.scheme.SchemeMonochrome; +import com.android.systemui.monet.scheme.SchemeNeutral; +import com.android.systemui.monet.scheme.SchemeRainbow; +import com.android.systemui.monet.scheme.SchemeTonalSpot; +import com.android.systemui.monet.scheme.SchemeVibrant; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -105,9 +118,6 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { protected static final String TAG = "ThemeOverlayController"; private static final boolean DEBUG = true; - protected static final int NEUTRAL = 0; - protected static final int ACCENT = 1; - private final ThemeOverlayApplier mThemeManager; private final UserManager mUserManager; private final BroadcastDispatcher mBroadcastDispatcher; @@ -130,12 +140,17 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private boolean mNeedsOverlayCreation; // Dominant color extracted from wallpaper, NOT the color used on the overlay protected int mMainWallpaperColor = Color.TRANSPARENT; + // UI contrast as reported by AccessibilityManager + private float mUiContrast = 0; // Theme variant: Vibrant, Tonal, Expressive, etc - private Style mThemeStyle = Style.TONAL_SPOT; + @VisibleForTesting + protected Style mThemeStyle = Style.TONAL_SPOT; // Accent colors overlay private FabricatedOverlay mSecondaryOverlay; // Neutral system colors overlay private FabricatedOverlay mNeutralOverlay; + // Dynamic colors overlay + private FabricatedOverlay mDynamicOverlay; // If wallpaper color event will be accepted and change the UI colors. private boolean mAcceptColorEvents = true; // If non-null (per user), colors that were sent to the framework, and processing was deferred @@ -143,6 +158,9 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private final SparseArray<WallpaperColors> mDeferredWallpaperColors = new SparseArray<>(); private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray(); private final WakefulnessLifecycle mWakefulnessLifecycle; + private final AccessibilityManager mAccessibilityManager; + private DynamicScheme mDynamicSchemeDark; + private DynamicScheme mDynamicSchemeLight; // Defers changing themes until Setup Wizard is done. private boolean mDeferredThemeEvaluation; @@ -304,6 +322,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mSkipSettingChange = true; if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has( OVERLAY_CATEGORY_SYSTEM_PALETTE)) { + jsonObject.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR); jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR); jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); jsonObject.remove(OVERLAY_COLOR_INDEX); @@ -372,7 +391,8 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { DumpManager dumpManager, FeatureFlags featureFlags, @Main Resources resources, - WakefulnessLifecycle wakefulnessLifecycle) { + WakefulnessLifecycle wakefulnessLifecycle, + AccessibilityManager accessibilityManager) { mContext = context; mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME); mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET); @@ -388,6 +408,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mUserTracker = userTracker; mResources = resources; mWakefulnessLifecycle = wakefulnessLifecycle; + mAccessibilityManager = accessibilityManager; dumpManager.registerDumpable(TAG, this); } @@ -424,6 +445,12 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { } }, UserHandle.USER_ALL); + mUiContrast = mAccessibilityManager.getUiContrast(); + mAccessibilityManager.addUiContrastChangeListener(mMainExecutor, uiContrast -> { + mUiContrast = uiContrast; + // Force reload so that we update even when the main color has not changed + reevaluateSystemTheme(true /* forceReload */); + }); if (!mIsMonetEnabled) { return; @@ -496,12 +523,11 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { if (mIsMonetEnabled) { mThemeStyle = fetchThemeStyleFromSetting(); - mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle); - mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle); + createOverlays(mMainWallpaperColor); mNeedsOverlayCreation = true; if (DEBUG) { Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay - + " neutral: " + mNeutralOverlay); + + " neutral: " + mNeutralOverlay + " dynamic: " + mDynamicOverlay); } } @@ -519,42 +545,95 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { return ColorScheme.getSeedColor(wallpaperColors); } - /** - * Given a color candidate, return an overlay definition. - */ - protected FabricatedOverlay getOverlay(int color, int type, Style style) { - boolean nightMode = (mResources.getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + private static DynamicScheme dynamicSchemeFromStyle(Style style, int color, + boolean isDark, double contrastLevel) { + Hct sourceColorHct = Hct.fromInt(color); + switch (style) { + case EXPRESSIVE: + return new SchemeExpressive(sourceColorHct, isDark, contrastLevel); + case SPRITZ: + return new SchemeNeutral(sourceColorHct, isDark, contrastLevel); + case TONAL_SPOT: + return new SchemeTonalSpot(sourceColorHct, isDark, contrastLevel); + case FRUIT_SALAD: + return new SchemeFruitSalad(sourceColorHct, isDark, contrastLevel); + case RAINBOW: + return new SchemeRainbow(sourceColorHct, isDark, contrastLevel); + case VIBRANT: + return new SchemeVibrant(sourceColorHct, isDark, contrastLevel); + case MONOCHROMATIC: + return new SchemeMonochrome(sourceColorHct, isDark, contrastLevel); + default: + return null; + } + } - mColorScheme = new ColorScheme(color, nightMode, style); - String name = type == ACCENT ? "accent" : "neutral"; + @VisibleForTesting + protected boolean isNightMode() { + return (mResources.getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + } - FabricatedOverlay.Builder overlay = - new FabricatedOverlay.Builder("com.android.systemui", name, "android"); + @VisibleForTesting + protected FabricatedOverlay newFabricatedOverlay(String name) { + return new FabricatedOverlay.Builder("com.android.systemui", name, "android").build(); + } - if (type == ACCENT) { - assignTonalPaletteToOverlay("accent1", overlay, mColorScheme.getAccent1()); - assignTonalPaletteToOverlay("accent2", overlay, mColorScheme.getAccent2()); - assignTonalPaletteToOverlay("accent3", overlay, mColorScheme.getAccent3()); - } else { - assignTonalPaletteToOverlay("neutral1", overlay, mColorScheme.getNeutral1()); - assignTonalPaletteToOverlay("neutral2", overlay, mColorScheme.getNeutral2()); - } + private void createOverlays(int color) { + boolean nightMode = isNightMode(); + mColorScheme = new ColorScheme(color, nightMode, mThemeStyle); + mNeutralOverlay = createNeutralOverlay(); + mSecondaryOverlay = createAccentOverlay(); + + mDynamicSchemeDark = dynamicSchemeFromStyle( + mThemeStyle, color, true /* isDark */, mUiContrast); + mDynamicSchemeLight = dynamicSchemeFromStyle( + mThemeStyle, color, false /* isDark */, mUiContrast); + mDynamicOverlay = createDynamicOverlay(); + } - return overlay.build(); + protected FabricatedOverlay createNeutralOverlay() { + FabricatedOverlay overlay = newFabricatedOverlay("neutral"); + assignTonalPaletteToOverlay("neutral1", overlay, mColorScheme.getNeutral1()); + assignTonalPaletteToOverlay("neutral2", overlay, mColorScheme.getNeutral2()); + return overlay; } - private void assignTonalPaletteToOverlay(String name, - FabricatedOverlay.Builder overlay, TonalPalette tonalPalette) { + protected FabricatedOverlay createAccentOverlay() { + FabricatedOverlay overlay = newFabricatedOverlay("accent"); + assignTonalPaletteToOverlay("accent1", overlay, mColorScheme.getAccent1()); + assignTonalPaletteToOverlay("accent2", overlay, mColorScheme.getAccent2()); + assignTonalPaletteToOverlay("accent3", overlay, mColorScheme.getAccent3()); + return overlay; + } + private void assignTonalPaletteToOverlay(String name, FabricatedOverlay overlay, + TonalPalette tonalPalette) { String resourcePrefix = "android:color/system_" + name; - int colorDataType = TypedValue.TYPE_INT_COLOR_ARGB8; tonalPalette.getAllShadesMapped().forEach((key, value) -> { String resourceName = resourcePrefix + "_" + key; int colorValue = ColorUtils.setAlphaComponent(value, 0xFF); - overlay.setResourceValue(resourceName, colorDataType, - colorValue); + overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue, + null /* configuration */); + }); + } + + protected FabricatedOverlay createDynamicOverlay() { + FabricatedOverlay overlay = newFabricatedOverlay("dynamic"); + assignDynamicPaletteToOverlay(overlay, true /* isDark */); + assignDynamicPaletteToOverlay(overlay, false /* isDark */); + return overlay; + } + + private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) { + String suffix = isDark ? "dark" : "light"; + DynamicScheme scheme = isDark ? mDynamicSchemeDark : mDynamicSchemeLight; + DynamicColors.ALL_DYNAMIC_COLORS_MAPPED.forEach(p -> { + String resourceName = "android:color/system_" + p.first + "_" + suffix; + int colorValue = p.second.getArgb(scheme); + overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue, + null /* configuration */); }); } @@ -568,16 +647,21 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { for (UserHandle userHandle : allProfiles) { Resources res = userHandle.isSystem() ? mResources : mContext.createContextAsUser(userHandle, 0).getResources(); - if (!(res.getColor(android.R.color.system_accent1_500, mContext.getTheme()) + Resources.Theme theme = mContext.getTheme(); + if (!(res.getColor(android.R.color.system_accent1_500, theme) == mColorScheme.getAccent1().getS500() - && res.getColor(android.R.color.system_accent2_500, mContext.getTheme()) + && res.getColor(android.R.color.system_accent2_500, theme) == mColorScheme.getAccent2().getS500() - && res.getColor(android.R.color.system_accent3_500, mContext.getTheme()) + && res.getColor(android.R.color.system_accent3_500, theme) == mColorScheme.getAccent3().getS500() - && res.getColor(android.R.color.system_neutral1_500, mContext.getTheme()) + && res.getColor(android.R.color.system_neutral1_500, theme) == mColorScheme.getNeutral1().getS500() - && res.getColor(android.R.color.system_neutral2_500, mContext.getTheme()) - == mColorScheme.getNeutral2().getS500())) { + && res.getColor(android.R.color.system_neutral2_500, theme) + == mColorScheme.getNeutral2().getS500() + && res.getColor(android.R.color.system_primary_container_dark, theme) + == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeDark) + && res.getColor(android.R.color.system_primary_container_light, theme) + == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeLight))) { return false; } } @@ -614,12 +698,11 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { if (!colorString.startsWith("#")) { colorString = "#" + colorString; } - int color = Color.parseColor(colorString); - mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle); - mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle); + createOverlays(Color.parseColor(colorString)); mNeedsOverlayCreation = true; categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); + categoryToPackage.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR); } catch (Exception e) { // Color.parseColor doesn't catch any exceptions from the calls it makes Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e); @@ -631,6 +714,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { // fail. categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); + categoryToPackage.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR); } catch (NumberFormatException e) { // This is a package name. All good, let's continue } @@ -647,6 +731,10 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { && mSecondaryOverlay != null) { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier()); } + if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_DYNAMIC_COLOR) + && mDynamicOverlay != null) { + categoryToPackage.put(OVERLAY_CATEGORY_DYNAMIC_COLOR, mDynamicOverlay.getIdentifier()); + } Set<UserHandle> managedProfiles = new HashSet<>(); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { @@ -668,7 +756,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { if (mNeedsOverlayCreation) { mNeedsOverlayCreation = false; mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[]{ - mSecondaryOverlay, mNeutralOverlay + mSecondaryOverlay, mNeutralOverlay, mDynamicOverlay }, currentUser, managedProfiles); } else { mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser, @@ -710,6 +798,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor)); pw.println("mSecondaryOverlay=" + mSecondaryOverlay); pw.println("mNeutralOverlay=" + mNeutralOverlay); + pw.println("mDynamicOverlay=" + mDynamicOverlay); pw.println("mIsMonetEnabled=" + mIsMonetEnabled); pw.println("mColorScheme=" + mColorScheme); pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation); diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 5cf01af92dcd..70523bb6b81d 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -47,12 +47,14 @@ import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -123,9 +125,25 @@ constructor( featureFlags: FeatureFlags, ) : UserRepository { - private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() }) - override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = - _userSwitcherSettings.asStateFlow().filterNotNull() + private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> = + globalSettings + .observerFlow( + names = + arrayOf( + SETTING_SIMPLE_USER_SWITCHER, + Settings.Global.ADD_USERS_WHEN_LOCKED, + Settings.Global.USER_SWITCHER_ENABLED, + ), + userId = UserHandle.USER_SYSTEM, + ) + .onStart { emit(Unit) } // Forces an initial update. + .map { getSettings() } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = runBlocking { getSettings() }, + ) + override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings private val _userInfos = MutableStateFlow<List<UserInfo>?>(null) override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull() @@ -159,7 +177,6 @@ constructor( init { observeSelectedUser() - observeUserSettings() if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) { observeUserSwitching() } @@ -247,23 +264,6 @@ constructor( .launchIn(applicationScope) } - private fun observeUserSettings() { - globalSettings - .observerFlow( - names = - arrayOf( - SETTING_SIMPLE_USER_SWITCHER, - Settings.Global.ADD_USERS_WHEN_LOCKED, - Settings.Global.USER_SWITCHER_ENABLED, - ), - userId = UserHandle.USER_SYSTEM, - ) - .onStart { emit(Unit) } // Forces an initial update. - .map { getSettings() } - .onEach { _userSwitcherSettings.value = it } - .launchIn(applicationScope) - } - private suspend fun getSettings(): UserSwitcherSettingsModel { return withContext(backgroundDispatcher) { val isSimpleUserSwitcher = diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java index 9f4a7c820efc..b9db9c464d30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.settingslib.dream.DreamBackend; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.shared.condition.Monitor; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -66,13 +67,16 @@ public class ComplicationTypesUpdaterTest extends SysuiTestCase { private ComplicationTypesUpdater mController; + @Mock + private Monitor mMonitor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>()); mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor, - mSecureSettings, mDreamOverlayStateController); + mSecureSettings, mDreamOverlayStateController, mMonitor); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java index ec448f94ba83..52aaea16ee70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.shared.condition.Monitor; import org.junit.Before; import org.junit.Test; @@ -69,6 +70,9 @@ public class DreamClockTimeComplicationTest extends SysuiTestCase { @Mock private ComplicationLayoutParams mLayoutParams; + @Mock + private Monitor mMonitor; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -83,7 +87,8 @@ public class DreamClockTimeComplicationTest extends SysuiTestCase { final DreamClockTimeComplication.Registrant registrant = new DreamClockTimeComplication.Registrant( mDreamOverlayStateController, - mComplication); + mComplication, + mMonitor); registrant.start(); verify(mDreamOverlayStateController).addComplication(eq(mComplication)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java index a4cf15c3fafa..fc1d38b2c1fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java @@ -46,6 +46,7 @@ import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.shared.condition.Monitor; import org.junit.Before; import org.junit.Test; @@ -101,6 +102,9 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { @Captor private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor; + @Mock + private Monitor mMonitor; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -126,7 +130,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setHaveFavorites(false); @@ -139,7 +143,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setHaveFavorites(false); @@ -152,7 +156,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setHaveFavorites(false); @@ -165,7 +169,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setHaveFavorites(true); @@ -178,7 +182,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setHaveFavorites(true); @@ -191,7 +195,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() { final DreamHomeControlsComplication.Registrant registrant = new DreamHomeControlsComplication.Registrant(mComplication, - mDreamOverlayStateController, mControlsComponent); + mDreamOverlayStateController, mControlsComponent, mMonitor); registrant.start(); setServiceAvailable(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java index c8b2b2556828..77ca9584f9e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java @@ -33,6 +33,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; import com.android.systemui.plugins.BcSmartspaceDataPlugin; +import com.android.systemui.shared.condition.Condition; +import com.android.systemui.shared.condition.Monitor; import org.junit.Before; import org.junit.Test; @@ -43,6 +45,8 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Collections; +import java.util.HashSet; +import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -60,6 +64,11 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { @Mock private View mBcSmartspaceView; + @Mock + private Monitor mMonitor; + + private final Set<Condition> mPreconditions = new HashSet<>(); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -79,7 +88,8 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { return new SmartSpaceComplication.Registrant( mDreamOverlayStateController, mComplication, - mSmartspaceController); + mSmartspaceController, + mMonitor); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java index 701cf95736ea..af5245934c12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java @@ -117,10 +117,6 @@ public class DeviceProvisionedCoordinatorTest extends SysuiTestCase { extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true); mNotification.extras = extras; - // GIVEN notification has the permission to display during setup - when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID)) - .thenReturn(PackageManager.PERMISSION_GRANTED); - // THEN don't filter out the notification assertFalse(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0)); } @@ -130,15 +126,10 @@ public class DeviceProvisionedCoordinatorTest extends SysuiTestCase { // GIVEN device is unprovisioned when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false); - // GIVEN notification has a flag to allow the notification during setup + // GIVEN notification does not have the flag to allow the notification during setup Bundle extras = new Bundle(); - extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true); mNotification.extras = extras; - // GIVEN notification does NOT have permission to display during setup - when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID)) - .thenReturn(PackageManager.PERMISSION_DENIED); - // THEN filter out the notification assertTrue(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index 3032ff1fe61f..83439f0fc60d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -17,6 +17,7 @@ package com.android.systemui.theme; import static com.android.systemui.theme.ThemeOverlayApplier.ANDROID_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; +import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_FONT; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_ANDROID; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_LAUNCHER; @@ -113,6 +114,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { }; when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR, + ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, @@ -123,6 +126,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR, + ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 1710709aa851..f9b5767bf53c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -47,8 +47,9 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.view.accessibility.AccessibilityManager; -import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -57,7 +58,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.monet.ColorScheme; import com.android.systemui.monet.Style; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -115,6 +115,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private Resources mResources; @Mock private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock + private AccessibilityManager mAccessibilityManager; @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver; @Captor @@ -127,13 +129,13 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback; @Captor private ArgumentCaptor<ContentObserver> mSettingsObserver; - private Style mCurrentStyle; @Before public void setup() { MockitoAnnotations.initMocks(this); when(mFeatureFlags.isEnabled(Flags.MONET)).thenReturn(true); when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); + when(mAccessibilityManager.getUiContrast()).thenReturn(0.5f); when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); when(mResources.getColor(eq(android.R.color.system_accent1_500), any())) .thenReturn(Color.RED); @@ -148,15 +150,19 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mThemeOverlayController = new ThemeOverlayController(mContext, mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, - mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) { - @Nullable - @Override - protected FabricatedOverlay getOverlay(int color, int type, Style style) { + mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, + mAccessibilityManager) { + @VisibleForTesting + protected boolean isNightMode() { + return false; + } + + @VisibleForTesting + protected FabricatedOverlay newFabricatedOverlay(String name) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) - .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000))); - mCurrentStyle = style; - mColorScheme = new ColorScheme(color, false /* nightMode */, style); + .thenReturn(new OverlayIdentifier( + Integer.toHexString(mColorScheme.getSeed() | 0xff000000))); return overlay; } }; @@ -416,7 +422,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId()); - assertThat(mCurrentStyle).isEqualTo(style); + assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(style); } } @@ -432,7 +438,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId()); - assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT); + assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.TONAL_SPOT); } @Test @@ -726,17 +732,20 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mThemeOverlayController = new ThemeOverlayController(mContext, mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, - mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) { - @Nullable - @Override - protected FabricatedOverlay getOverlay(int color, int type, Style style) { + mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, + mAccessibilityManager) { + @VisibleForTesting + protected boolean isNightMode() { + return false; + } + + @VisibleForTesting + protected FabricatedOverlay newFabricatedOverlay(String name) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever")); - mColorScheme = new ColorScheme(color, false /* nightMode */, style); return overlay; } - }; mThemeOverlayController.start(); @@ -763,14 +772,19 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mThemeOverlayController = new ThemeOverlayController(mContext, mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, - mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) { - @Nullable - @Override - protected FabricatedOverlay getOverlay(int color, int type, Style style) { + mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, + mAccessibilityManager) { + @VisibleForTesting + protected boolean isNightMode() { + return false; + } + + @VisibleForTesting + protected FabricatedOverlay newFabricatedOverlay(String name) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) - .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000))); - mColorScheme = new ColorScheme(color, false /* nightMode */, style); + .thenReturn(new OverlayIdentifier( + Integer.toHexString(mColorScheme.getSeed() | 0xff000000))); return overlay; } }; diff --git a/services/Android.bp b/services/Android.bp index 3f3ba067632b..f8097ec1bb92 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -191,6 +191,10 @@ java_library { "service-sdksandbox.stubs.system_server", ], + vintf_fragments: [ + "manifest_services.xml", + ], + // Uncomment to enable output of certain warnings (deprecated, unchecked) //javacflags: ["-Xlint"], } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d3593f04ffe1..03cf9b460b1e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -4384,9 +4384,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED); - // TODO: replace name with Settings Secure Key private final Uri mAlwaysOnMagnificationUri = Settings.Secure.getUriFor( - "accessibility_magnification_always_on_enabled"); + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED); private final Uri mUiContrastUri = Settings.Secure.getUriFor( CONTRAST_LEVEL); @@ -4620,10 +4619,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) { - // TODO: replace name const with Settings Secure Key final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), - "accessibility_magnification_always_on_enabled", + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, 0, userState.mUserId) == 1; final boolean isAlwaysOnFeatureFlagEnabled = mMagnificationController .isAlwaysOnMagnificationFeatureFlagEnabled(); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 6b61e978ea7b..b991a026faa1 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -193,6 +193,32 @@ public final class AutofillManagerService final AugmentedAutofillState mAugmentedAutofillState = new AugmentedAutofillState(); + /** + * Lock used to synchronize access to flags. + */ + private final Object mFlagLock = new Object(); + + // Flag holders for Autofill PCC classification + + @GuardedBy("mFlagLock") + private boolean mPccClassificationEnabled; + + @GuardedBy("mFlagLock") + private boolean mPccPreferProviderOverPcc; + + @GuardedBy("mFlagLock") + private boolean mPccUseFallbackDetection; + + @GuardedBy("mFlagLock") + private String mPccProviderHints; + + // Default flag values for Autofill PCC + + private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = ""; + private static final boolean DEFAULT_PREFER_PROVIDER_OVER_PCC = true; + + private static final boolean DEFAULT_PCC_USE_FALLBACK = true; + public AutofillManagerService(Context context) { super(context, new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE), @@ -302,6 +328,10 @@ public final class AutofillManagerService case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES: case AutofillFeatureFlags.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT: case AutofillFeatureFlags.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT: + case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED: + case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS: + case AutofillFeatureFlags.DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC: + case AutofillFeatureFlags.DEVICE_CONFIG_PCC_USE_FALLBACK: setDeviceConfigProperties(); break; case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES: @@ -579,13 +609,38 @@ public final class AutofillManagerService AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES, AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM); if (verbose) { - Slog.v(mTag, "setDeviceConfigProperties(): " + Slog.v(mTag, "setDeviceConfigProperties() for AugmentedAutofill: " + "augmentedIdleTimeout=" + mAugmentedServiceIdleUnbindTimeoutMs + ", augmentedRequestTimeout=" + mAugmentedServiceRequestTimeoutMs + ", smartSuggestionMode=" + getSmartSuggestionModeToString(mSupportedSmartSuggestionModes)); } } + synchronized (mFlagLock) { + mPccClassificationEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED, + AutofillFeatureFlags.DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED); + mPccPreferProviderOverPcc = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + AutofillFeatureFlags.DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC, + DEFAULT_PREFER_PROVIDER_OVER_PCC); + mPccUseFallbackDetection = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + AutofillFeatureFlags.DEVICE_CONFIG_PCC_USE_FALLBACK, + DEFAULT_PCC_USE_FALLBACK); + mPccProviderHints = DeviceConfig.getString( + DeviceConfig.NAMESPACE_AUTOFILL, + AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS, + DEFAULT_PCC_FEATURE_PROVIDER_HINTS); + if (verbose) { + Slog.v(mTag, "setDeviceConfigProperties() for PCC: " + + "mPccClassificationEnabled=" + mPccClassificationEnabled + + ", mPccPreferProviderOverPcc=" + mPccPreferProviderOverPcc + + ", mPccUseFallbackDetection=" + mPccUseFallbackDetection + + ", mPccProviderHints=" + mPccProviderHints); + } + } } private void updateCachedServices() { @@ -791,6 +846,46 @@ public final class AutofillManagerService } } + /** + * Whether the Autofill PCC Classification feature is enabled. + */ + public boolean isPccClassificationEnabled() { + synchronized (mFlagLock) { + return mPccClassificationEnabled; + } + } + + /** + * Whether the Autofill Provider shouldbe preferred over PCC results for selecting datasets. + */ + public boolean preferProviderOverPcc() { + synchronized (mFlagLock) { + return mPccPreferProviderOverPcc; + } + } + + /** + * Whether to use the fallback for detection. + * If true, use data from secondary source if primary not present . + * For eg: if we prefer PCC over provider, and PCC detection didn't classify a field, however, + * autofill provider did, this flag would decide whether we use that result, and show some + * presentation for that particular field. + */ + public boolean shouldUsePccFallback() { + synchronized (mFlagLock) { + return mPccUseFallbackDetection; + } + } + + /** + * Provides Autofill Hints that would be requested by the service from the Autofill Provider. + */ + public String getPccProviderHints() { + synchronized (mFlagLock) { + return mPccProviderHints; + } + } + @Nullable @VisibleForTesting static Map<String, String[]> getAllowedCompatModePackages(String setting) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2b529bfe5e32..c5c92885406f 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -170,6 +170,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState "android.service.autofill.action.DELAYED_FILL"; private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID"; + private static final String PCC_HINTS_DELIMITER = ","; + final Object mLock; private final AutofillManagerServiceImpl mService; @@ -564,6 +566,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mPendingInlineSuggestionsRequest.isServiceSupported()) { mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest, @@ -672,8 +675,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ false); + final List<String> hints = getTypeHintsForProvider(); + mDelayedFillPendingIntent = createPendingIntent(requestId); - request = new FillRequest(requestId, contexts, mClientState, flags, + request = new FillRequest(requestId, contexts, hints, mClientState, flags, /*inlineSuggestionsRequest=*/ null, /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null ? null @@ -705,6 +710,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Get the list of valid autofill hint types from Device flags + * Returns empty list if PCC is off or no types available + */ + private List<String> getTypeHintsForProvider() { + if (!mService.getMaster().isPccClassificationEnabled()) { + return Collections.EMPTY_LIST; + } + final String typeHints = mService.getMaster().getPccProviderHints(); + if (TextUtils.isEmpty(typeHints)) { + return new ArrayList<>(); + } + + return List.of(typeHints.split(PCC_HINTS_DELIMITER)); + } + + /** * Assist Data Receiver for PCC */ private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub { diff --git a/services/core/Android.bp b/services/core/Android.bp index bd072f523c7f..c8caab93d76c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -146,6 +146,7 @@ java_library_static { ], static_libs: [ + "android.frameworks.location.altitude-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", "android.hardware.boot-V1.0-java", // HIDL diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 9c2de65f2777..17ef9a232401 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -104,8 +104,18 @@ public abstract class BatteryStatsInternal { /** * Reports any activity that could potentially have caused the CPU to wake up. - * Accepts a timestamp to allow the reporter to report it before or after the event. + * Accepts a timestamp to allow free ordering between the event and its reporting. + * @param subsystem The subsystem this activity should be attributed to. + * @param elapsedMillis The time when this activity happened in the elapsed timebase. + * @param uids The uid (or uids) that should be blamed for this activity. */ public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem, long elapsedMillis, @NonNull int... uids); + + /** + * Reports a sound trigger recognition event that may have woken up the CPU. + * @param elapsedMillis The time when the event happened in the elapsed timebase. + * @param uid The uid that requested this trigger. + */ + public abstract void noteWakingSoundTrigger(long elapsedMillis, int uid); } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index e1d1f6ce1866..2aeaa2f704a2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -962,7 +962,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = "enable_wait_for_finish_attach_application"; - private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true; + private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false; /** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */ public volatile boolean mEnableWaitForFinishAttachApplication = diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index f73594c81db6..19235c9985a7 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -483,6 +483,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub Objects.requireNonNull(uids); mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids); } + + @Override + public void noteWakingSoundTrigger(long elapsedMillis, int uid) { + // TODO(b/267717665): Pipe to noteCpuWakingActivity once SoundTrigger starts using this. + Slog.w(TAG, "Sound trigger event dispatched to uid " + uid); + } } @Override diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index ec2e2540a15a..0318b2464137 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -488,31 +488,65 @@ public class SyncManager { * migrated already. */ private void migrateSyncJobNamespaceIfNeeded() { - if (mSyncStorageEngine.isJobNamespaceMigrated()) { + final boolean namespaceMigrated = mSyncStorageEngine.isJobNamespaceMigrated(); + final boolean attributionFixed = mSyncStorageEngine.isJobAttributionFixed(); + if (namespaceMigrated && attributionFixed) { return; } final JobScheduler jobSchedulerDefaultNamespace = mContext.getSystemService(JobScheduler.class); - final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs(); - // Wait until we've confirmed that all syncs have been migrated to the new namespace - // before we persist successful migration to our status file. This is done to avoid + if (!namespaceMigrated) { + final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs(); + // Wait until we've confirmed that all syncs have been migrated to the new namespace + // before we persist successful migration to our status file. This is done to avoid + // internal consistency issues if the devices reboots right after SyncManager has + // done the migration on its side but before JobScheduler has finished persisting + // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk, + // then nothing that happened afterwards should have been persisted either, so there's + // no concern over activity happening after the migration causing issues. + boolean allSyncsMigrated = true; + for (int i = pendingJobs.size() - 1; i >= 0; --i) { + final JobInfo job = pendingJobs.get(i); + final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras()); + if (op != null) { + // This is a sync. Move it over to SyncManager's namespace. + mJobScheduler.scheduleAsPackage(job, + op.owningPackage, op.target.userId, op.wakeLockName()); + jobSchedulerDefaultNamespace.cancel(job.getId()); + allSyncsMigrated = false; + } + } + mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated); + } + + // Fix attribution for any syncs that were previously scheduled using + // JobScheduler.schedule() instead of JobScheduler.scheduleAsPackage(). + final List<JobInfo> namespacedJobs = LocalServices.getService(JobSchedulerInternal.class) + .getSystemScheduledOwnJobs(mJobScheduler.getNamespace()); + // Wait until we've confirmed that all syncs have been proper attribution + // before we persist attribution state to our status file. This is done to avoid // internal consistency issues if the devices reboots right after SyncManager has - // done the migration on its side but before JobScheduler has finished persisting + // rescheduled the job on its side but before JobScheduler has finished persisting // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk, // then nothing that happened afterwards should have been persisted either, so there's // no concern over activity happening after the migration causing issues. - boolean allSyncsMigrated = true; - for (int i = pendingJobs.size() - 1; i >= 0; --i) { - final JobInfo job = pendingJobs.get(i); + // This case is done to fix issues for a subset of test devices. + // TODO: remove this attribution check/fix code + boolean allSyncsAttributed = true; + for (int i = namespacedJobs.size() - 1; i >= 0; --i) { + final JobInfo job = namespacedJobs.get(i); final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras()); if (op != null) { - // This is a sync. Move it over to SyncManager's namespace. - mJobScheduler.schedule(job); - jobSchedulerDefaultNamespace.cancel(job.getId()); - allSyncsMigrated = false; + // This is a sync. Make sure it's properly attributed to the app + // instead of the system. + // Since the job ID stays the same, scheduleAsPackage will replace the scheduled + // job, so we don't need to call cancel as well. + mJobScheduler.scheduleAsPackage(job, + op.owningPackage, op.target.userId, op.wakeLockName()); + allSyncsAttributed = false; } } - mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated); + mSyncStorageEngine.setJobAttributionFixed(allSyncsAttributed); } private synchronized void verifyJobScheduler() { diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 9f3302deba13..b890bbd65b95 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -173,6 +173,7 @@ public class SyncStorageEngine { private volatile boolean mIsClockValid; private volatile boolean mIsJobNamespaceMigrated; + private volatile boolean mIsJobAttributionFixed; static { sAuthorityRenames = new HashMap<String, String>(); @@ -852,6 +853,20 @@ public class SyncStorageEngine { return mIsJobNamespaceMigrated; } + void setJobAttributionFixed(boolean fixed) { + if (mIsJobAttributionFixed == fixed) { + return; + } + mIsJobAttributionFixed = fixed; + // This isn't urgent enough to write synchronously. Post it to the handler thread so + // SyncManager can move on with whatever it was doing. + mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY); + } + + boolean isJobAttributionFixed() { + return mIsJobAttributionFixed; + } + public Pair<Long, Long> getBackoff(EndPoint info) { synchronized (mAuthorities) { AuthorityInfo authority = getAuthorityLocked(info, "getBackoff"); @@ -2120,6 +2135,10 @@ public class SyncStorageEngine { mIsJobNamespaceMigrated = proto.readBoolean(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED); break; + case (int) SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED: + mIsJobAttributionFixed = + proto.readBoolean(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED); + break; case ProtoInputStream.NO_MORE_FIELDS: return; } @@ -2389,6 +2408,7 @@ public class SyncStorageEngine { } proto.write(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED, mIsJobNamespaceMigrated); + proto.write(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED, mIsJobAttributionFixed); proto.flush(); } diff --git a/services/core/java/com/android/server/location/altitude/AltitudeService.java b/services/core/java/com/android/server/location/altitude/AltitudeService.java new file mode 100644 index 000000000000..b321e4dfdf26 --- /dev/null +++ b/services/core/java/com/android/server/location/altitude/AltitudeService.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.altitude; + +import android.content.Context; +import android.frameworks.location.altitude.AddMslAltitudeToLocationRequest; +import android.frameworks.location.altitude.AddMslAltitudeToLocationResponse; +import android.frameworks.location.altitude.IAltitudeService; +import android.location.Location; +import android.location.altitude.AltitudeConverter; +import android.os.RemoteException; + +import com.android.server.SystemService; + +import java.io.IOException; + +/** + * Manages altitude information exchange through the HAL, e.g., geoid height requests such that + * vendors can perform altitude conversions. + * + * @hide + */ +public class AltitudeService extends IAltitudeService.Stub { + + private final AltitudeConverter mAltitudeConverter = new AltitudeConverter(); + private final Context mContext; + + /** @hide */ + public AltitudeService(Context context) { + mContext = context; + } + + @Override + public AddMslAltitudeToLocationResponse addMslAltitudeToLocation( + AddMslAltitudeToLocationRequest request) throws RemoteException { + Location location = new Location(""); + location.setLatitude(request.latitudeDegrees); + location.setLongitude(request.longitudeDegrees); + location.setAltitude(request.altitudeMeters); + location.setVerticalAccuracyMeters(request.verticalAccuracyMeters); + try { + mAltitudeConverter.addMslAltitudeToLocation(mContext, location); + } catch (IOException e) { + throw new RemoteException(e); + } + + AddMslAltitudeToLocationResponse response = new AddMslAltitudeToLocationResponse(); + response.mslAltitudeMeters = location.getMslAltitudeMeters(); + response.mslAltitudeAccuracyMeters = location.getMslAltitudeAccuracyMeters(); + return response; + } + + @Override + public String getInterfaceHash() { + return IAltitudeService.HASH; + } + + @Override + public int getInterfaceVersion() { + return IAltitudeService.VERSION; + } + + /** @hide */ + public static class Lifecycle extends SystemService { + + private static final String SERVICE_NAME = IAltitudeService.DESCRIPTOR + "/default"; + + private AltitudeService mService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mService = new AltitudeService(getContext()); + publishBinderService(SERVICE_NAME, mService); + } + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0aa822b0aab5..94d5d53e2416 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -114,6 +114,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -271,6 +272,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.compat.IPlatformCompat; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.MetricsLogger; @@ -577,11 +579,11 @@ public class NotificationManagerService extends SystemService { private float mInCallNotificationVolume; private Binder mCallNotificationToken = null; - private static final boolean ONGOING_DISMISSAL = SystemProperties.getBoolean( - "persist.sysui.notification.ongoing_dismissal", true); @VisibleForTesting protected boolean mSystemExemptFromDismissal = false; + private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver; + // used as a mutex for access to all active notifications & listeners final Object mNotificationLock = new Object(); @GuardedBy("mNotificationLock") @@ -1208,7 +1210,8 @@ public class NotificationManagerService extends SystemService { } } - int mustNotHaveFlags = ONGOING_DISMISSAL ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT; + int mustNotHaveFlags = mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING) + ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT; cancelNotification(callingUid, callingPid, pkg, tag, id, /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ mustNotHaveFlags, @@ -2219,7 +2222,8 @@ public class NotificationManagerService extends SystemService { TelephonyManager telephonyManager, ActivityManagerInternal ami, MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper, UsageStatsManagerInternal usageStatsManagerInternal, - TelecomManager telecomManager, NotificationChannelLogger channelLogger) { + TelecomManager telecomManager, NotificationChannelLogger channelLogger, + SystemUiSystemPropertiesFlags.FlagResolver flagResolver) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2417,6 +2421,8 @@ public class NotificationManagerService extends SystemService { mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource( com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); + mFlagResolver = flagResolver; + mStatsManager = statsManager; mToastRateLimiter = toastRateLimiter; @@ -2548,7 +2554,7 @@ public class NotificationManagerService extends SystemService { AppGlobals.getPermissionManager()), LocalServices.getService(UsageStatsManagerInternal.class), getContext().getSystemService(TelecomManager.class), - new NotificationChannelLoggerImpl()); + new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver()); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -6516,7 +6522,7 @@ public class NotificationManagerService extends SystemService { // Fix the notification as best we can. try { - fixNotification(notification, pkg, tag, id, userId); + fixNotification(notification, pkg, tag, id, userId, notificationUid); } catch (Exception e) { if (notification.isForegroundService()) { throw new SecurityException("Invalid FGS notification", e); @@ -6694,14 +6700,15 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void fixNotification(Notification notification, String pkg, String tag, int id, - int userId) throws NameNotFoundException, RemoteException { + @UserIdInt int userId, int notificationUid) throws NameNotFoundException, + RemoteException { final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser( pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); // Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS - if (ONGOING_DISMISSAL) { + if (mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)) { if (((notification.flags & FLAG_ONGOING_EVENT) > 0) && canBeNonDismissible(ai, notification)) { notification.flags |= FLAG_NO_DISMISS; @@ -6710,17 +6717,31 @@ public class NotificationManagerService extends SystemService { } } - int canColorize = mPackageManagerClient.checkPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg); + int canColorize = getContext().checkPermission( + android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, -1, notificationUid); + if (canColorize == PERMISSION_GRANTED) { notification.flags |= Notification.FLAG_CAN_COLORIZE; } else { notification.flags &= ~Notification.FLAG_CAN_COLORIZE; } + if (notification.extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, false)) { + int hasShowDuringSetupPerm = getContext().checkPermission( + android.Manifest.permission.NOTIFICATION_DURING_SETUP, -1, notificationUid); + if (hasShowDuringSetupPerm != PERMISSION_GRANTED) { + notification.extras.remove(Notification.EXTRA_ALLOW_DURING_SETUP); + if (DBG) { + Slog.w(TAG, "warning: pkg " + pkg + " attempting to show during setup" + + " without holding perm " + + Manifest.permission.NOTIFICATION_DURING_SETUP); + } + } + } + if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) { - int fullscreenIntentPermission = mPackageManagerClient.checkPermission( - android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg); + int fullscreenIntentPermission = getContext().checkPermission( + android.Manifest.permission.USE_FULL_SCREEN_INTENT, -1, notificationUid); if (fullscreenIntentPermission != PERMISSION_GRANTED) { notification.fullScreenIntent = null; Slog.w(TAG, "Package " + pkg + @@ -6765,8 +6786,8 @@ public class NotificationManagerService extends SystemService { // Ensure MediaStyle has correct permissions for remote device extras if (notification.isStyle(Notification.MediaStyle.class)) { - int hasMediaContentControlPermission = mPackageManager.checkPermission( - android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId); + int hasMediaContentControlPermission = getContext().checkPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, -1, notificationUid); if (hasMediaContentControlPermission != PERMISSION_GRANTED) { notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_DEVICE); notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_ICON); @@ -6780,8 +6801,8 @@ public class NotificationManagerService extends SystemService { // Ensure only allowed packages have a substitute app name if (notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) { - int hasSubstituteAppNamePermission = mPackageManager.checkPermission( - permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg, userId); + int hasSubstituteAppNamePermission = getContext().checkPermission( + permission.SUBSTITUTE_NOTIFICATION_APP_NAME, -1, notificationUid); if (hasSubstituteAppNamePermission != PERMISSION_GRANTED) { notification.extras.remove(Notification.EXTRA_SUBSTITUTE_APP_NAME); if (DBG) { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 062f0fcd0902..d471c8abb1b2 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -301,7 +301,7 @@ public final class OverlayManagerService extends SystemService { && shellPkgName.equals(overlayInfo.packageName)); initIfNeeded(); - onSwitchUser(UserHandle.USER_SYSTEM); + onStartUser(UserHandle.USER_SYSTEM); publishBinderService(Context.OVERLAY_SERVICE, mService); publishLocalService(OverlayManagerService.class, this); @@ -324,7 +324,7 @@ public final class OverlayManagerService extends SystemService { final UserInfo userInfo = users.get(i); if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) { // Initialize any users that can't be switched to, as their state would - // never be setup in onSwitchUser(). We will switch to the system user right + // never be setup in onStartUser(). We will switch to the system user right // after this, and its state will be setup there. updatePackageManagerLocked(mImpl.updateOverlaysForUser(users.get(i).id)); } @@ -333,13 +333,13 @@ public final class OverlayManagerService extends SystemService { } @Override - public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { - onSwitchUser(to.getUserIdentifier()); + public void onUserStarting(TargetUser user) { + onStartUser(user.getUserIdentifier()); } - private void onSwitchUser(@UserIdInt int newUserId) { + private void onStartUser(@UserIdInt int newUserId) { try { - traceBegin(TRACE_TAG_RRO, "OMS#onSwitchUser " + newUserId); + traceBegin(TRACE_TAG_RRO, "OMS#onStartUser " + newUserId); // ensure overlays in the settings are up-to-date, and propagate // any asset changes to the rest of the system synchronized (mLock) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 198c339b8e4f..477a8a63a199 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -3154,8 +3154,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - final ActivityOptions clientOptions = ActivityOptions.makeBasic(); - clientOptions.setIgnorePendingIntentCreatorForegroundState(true); + final ActivityOptions clientOptions = ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED); PendingIntent clientIntent = PendingIntent.getActivityAsUser( mContext, 0, Intent.createChooser( new Intent(Intent.ACTION_SET_WALLPAPER), diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 45076379dc4f..502bfd1a5d67 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -781,6 +781,7 @@ class ActivityClientController extends IActivityClientController.Stub { // the running transition finish. final Transition transition = r != null && r.mTransitionController.inPlayingTransition(r) + && !r.mTransitionController.isCollecting() ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null; if (transition != null) { r.mTransitionController.requestStartTransition(transition, null /*startTask */, @@ -820,6 +821,7 @@ class ActivityClientController extends IActivityClientController.Stub { // visibility while playing transition, there won't able to commit visibility until // the running transition finish. final Transition transition = r.mTransitionController.inPlayingTransition(r) + && !r.mTransitionController.isCollecting() ? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null; if (transition != null) { r.mTransitionController.requestStartTransition(transition, null /*startTask */, diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index e04900c94f15..47bdba34ee24 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -250,10 +250,7 @@ final class DisplayRotationCompatPolicy { } ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); - if (topActivity == null - // Checking windowing mode on activity level because we don't want to - // show toast in case of activity embedding. - || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + if (!isTreatmentEnabledForActivity(topActivity)) { return; } showToast(R.string.display_rotation_camera_compat_toast_after_rotation); @@ -309,21 +306,28 @@ final class DisplayRotationCompatPolicy { } boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) { - return isTreatmentEnabledForDisplay() && isCameraActiveInFullscreen(activity); + return isTreatmentEnabledForDisplay() + && isCameraActive(activity, /* mustBeFullscreen */ true); } + /** * Whether camera compat treatment is applicable for the given activity. * * <p>Conditions that need to be met: * <ul> - * <li>{@link #isCameraActiveForPackage} is {@code true} for the activity. + * <li>Camera is active for the package. * <li>The activity is in fullscreen * <li>The activity has fixed orientation but not "locked" or "nosensor" one. * </ul> */ boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) { - return activity != null && isCameraActiveInFullscreen(activity) + return isTreatmentEnabledForActivity(activity, /* mustBeFullscreen */ true); + } + + private boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity, + boolean mustBeFullscreen) { + return activity != null && isCameraActive(activity, mustBeFullscreen) && activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED // "locked" and "nosensor" values are often used by camera apps that can't // handle dynamic changes so we shouldn't force rotate them. @@ -331,8 +335,10 @@ final class DisplayRotationCompatPolicy { && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED; } - private boolean isCameraActiveInFullscreen(@NonNull ActivityRecord activity) { - return !activity.inMultiWindowMode() + private boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) { + // Checking windowing mode on activity level because we don't want to + // apply treatment in case of activity embedding. + return (!mustBeFullscreen || !activity.inMultiWindowMode()) && mCameraIdPackageBiMap.containsPackageName(activity.packageName) && activity.mLetterboxUiController.shouldForceRotateForCameraCompat(); } @@ -385,7 +391,8 @@ final class DisplayRotationCompatPolicy { } // Checking that the whole app is in multi-window mode as we shouldn't show toast // for the activity embedding case. - if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW + && isTreatmentEnabledForActivity(topActivity, /* mustBeFullscreen */ false)) { showToast(R.string.display_rotation_camera_compat_toast_in_split_screen); } } diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java index 2345e3f5a3f0..c8518c5587a8 100644 --- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java @@ -97,7 +97,7 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR if (response != null) { respondToClientWithResponseAndFinish(response); } else { - respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREDENTIAL, + respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS, "Invalid response"); } } @@ -119,6 +119,12 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR } } + @Override + public void onUiSelectorInvocationFailure() { + respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS, + "No create options available."); + } + private void respondToClientWithResponseAndFinish(CreateCredentialResponse response) { Log.i(TAG, "respondToClientWithResponseAndFinish"); if (isSessionCancelled()) { @@ -166,8 +172,8 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR Log.i(TAG, "in onProviderStatusChanged - isUiInvocationNeeded"); getProviderDataAndInitiateUi(); } else { - respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREDENTIAL, - "No credentials available"); + respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS, + "No create options available."); } } } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 685723910edf..edffad299e0f 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -472,8 +472,8 @@ public final class CredentialManagerService if (providerSessions.isEmpty()) { try { callback.onError( - CreateCredentialException.TYPE_NO_CREDENTIAL, - "No credentials available on this device."); + CreateCredentialException.TYPE_NO_CREATE_OPTIONS, + "No create options available."); } catch (RemoteException e) { Log.i( TAG, diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java index 797601a51c5a..a6f6a830056f 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java @@ -56,19 +56,29 @@ public class CredentialManagerUi { }; private void handleUiResult(int resultCode, Bundle resultData) { - if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION) { - UserSelectionDialogResult selection = UserSelectionDialogResult - .fromResultData(resultData); - if (selection != null) { - mCallbacks.onUiSelection(selection); - } else { - Slog.i(TAG, "No selection found in UI result"); - } - } else if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED) { - mCallbacks.onUiCancellation(/* isUserCancellation= */ true); - } else if (resultCode - == UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS) { - mCallbacks.onUiCancellation(/* isUserCancellation= */ false); + switch (resultCode) { + case UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION: + UserSelectionDialogResult selection = UserSelectionDialogResult + .fromResultData(resultData); + if (selection != null) { + mCallbacks.onUiSelection(selection); + } else { + Slog.i(TAG, "No selection found in UI result"); + } + break; + case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED: + mCallbacks.onUiCancellation(/* isUserCancellation= */ true); + break; + case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS: + mCallbacks.onUiCancellation(/* isUserCancellation= */ false); + break; + case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE: + mCallbacks.onUiSelectorInvocationFailure(); + break; + default: + Slog.i(TAG, "Unknown error code returned from the UI"); + mCallbacks.onUiSelectorInvocationFailure(); + break; } } @@ -80,6 +90,9 @@ public class CredentialManagerUi { void onUiSelection(UserSelectionDialogResult selection); /** Called when the UI is canceled without a successful provider result. */ void onUiCancellation(boolean isUserCancellation); + + /** Called when the selector UI fails to come up (mostly due to parsing issue today). */ + void onUiSelectorInvocationFailure(); } public CredentialManagerUi(Context context, int userId, CredentialManagerUiCallback callbacks) { diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java index e732c23ca2d9..332499911f9c 100644 --- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java @@ -147,6 +147,12 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest } @Override + public void onUiSelectorInvocationFailure() { + respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, + "No credentials to show on the selector."); + } + + @Override public void onProviderStatusChanged(ProviderSession.Status status, ComponentName componentName) { Log.i(TAG, "in onStatusChanged with status: " + status); diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 20e358c8f617..3f285b7d9c8d 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -96,13 +96,14 @@ public final class ProviderCreateSession extends ProviderSession< if (propagateToProvider) { return new BeginCreateCredentialRequest( type, - candidateQueryData + candidateQueryData, + callingAppInfo ); } return new BeginCreateCredentialRequest( type, - candidateQueryData, - callingAppInfo); + candidateQueryData + ); } @Nullable @@ -168,7 +169,16 @@ public final class ProviderCreateSession extends ProviderSession< private void onUpdateResponse(BeginCreateCredentialResponse response) { Log.i(TAG, "updateResponse with save entries"); mProviderResponse = response; - updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED); + if (isEmptyResponse(response)) { + updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE); + } else { + updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED); + } + } + + private boolean isEmptyResponse(BeginCreateCredentialResponse response) { + return (response.getCreateEntries() == null || response.getCreateEntries().isEmpty()) + && response.getRemoteCreateEntry() == null; } @Override @@ -294,7 +304,7 @@ public final class ProviderCreateSession extends ProviderSession< ProviderPendingIntentResponse pendingIntentResponse) { if (pendingIntentResponse == null) { Log.i(TAG, "pendingIntentResponse is null"); - return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREDENTIAL); + return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREATE_OPTIONS); } if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) { CreateCredentialException exception = PendingIntentResultHandler @@ -306,7 +316,7 @@ public final class ProviderCreateSession extends ProviderSession< } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) { return new CreateCredentialException(CreateCredentialException.TYPE_USER_CANCELED); } else { - return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREDENTIAL); + return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREATE_OPTIONS); } return null; } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 9fba95bfdd34..764aac3719b7 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -108,7 +108,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential Log.i(TAG, "Unable to create provider session"); return null; } - private static BeginGetCredentialRequest constructQueryPhaseRequest( android.credentials.GetCredentialRequest filteredRequest, CallingAppInfo callingAppInfo, @@ -306,18 +305,20 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId); credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId, credentialEntry.getSlice(), - /*fillInIntent=*/setUpFillInIntent(credentialEntry.getType()))); + /*fillInIntent=*/setUpFillInIntent(credentialEntry + .getBeginGetCredentialOption().getId()))); } return credentialUiEntries; } - private Intent setUpFillInIntent(@Nullable String id) { + private Intent setUpFillInIntent(@NonNull String id) { // TODO: Determine if we should skip this entry if entry id is not set, or is set // but does not resolve to a valid option. For now, not skipping it because // it may be possible that the provider adds their own extras and expects to receive // those and complete the flow. - if (id == null || mBeginGetOptionToCredentialOptionMap.get(id) == null) { + if (mBeginGetOptionToCredentialOptionMap.get(id) == null) { Log.i(TAG, "Id from Credential Entry does not resolve to a valid option"); + return new Intent(); } return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST, new GetCredentialRequest( @@ -445,7 +446,22 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Updates the response being maintained in state by this provider session. */ private void onUpdateResponse(BeginGetCredentialResponse response) { mProviderResponse = response; - updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED); + if (isEmptyResponse(response)) { + updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE); + } else { + updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED); + } + } + + private boolean isEmptyResponse(BeginGetCredentialResponse response) { + if ((response.getCredentialEntries() == null || response.getCredentialEntries().isEmpty()) + && (response.getAuthenticationActions() == null || response + .getAuthenticationActions().isEmpty()) + && (response.getActions() == null || response.getActions().isEmpty()) + && response.getRemoteCredentialEntry() == null) { + return true; + } + return false; } private void onUpdateEmptyResponse() { diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java index 1ae0f3c0d2f3..1aec9340e332 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java @@ -133,7 +133,7 @@ public abstract class ProviderSession<T, R> PENDING_INTENT_INVOKED, CREDENTIAL_RECEIVED_FROM_SELECTION, SAVE_ENTRIES_RECEIVED, CANCELED, - NO_CREDENTIALS, COMPLETE + NO_CREDENTIALS, EMPTY_RESPONSE, COMPLETE } /** Converts exception to a provider session status. */ diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java index 9f1bd8f69dde..fdd0e81db9d9 100644 --- a/services/credentials/java/com/android/server/credentials/RequestSession.java +++ b/services/credentials/java/com/android/server/credentials/RequestSession.java @@ -144,6 +144,11 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan finishSession(/*propagateCancellation=*/false); } + @Override + public void onUiSelectorInvocationFailure() { + Log.i(TAG, "onUiSelectorInvocationFailure"); + } + protected void finishSession(boolean propagateCancellation) { Log.i(TAG, "finishing session"); if (propagateCancellation) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cae6c39a1a10..271a3d3779b2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -142,6 +142,7 @@ import com.android.server.integrity.AppIntegrityManagerService; import com.android.server.lights.LightsService; import com.android.server.locales.LocaleManagerService; import com.android.server.location.LocationManagerService; +import com.android.server.location.altitude.AltitudeService; import com.android.server.logcat.LogcatManagerService; import com.android.server.media.MediaRouterService; import com.android.server.media.metrics.MediaMetricsManagerService; @@ -2119,6 +2120,14 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartAltitudeService"); + try { + mSystemServiceManager.startService(AltitudeService.Lifecycle.class); + } catch (Throwable e) { + reportWtf("starting AltitudeService service", e); + } + t.traceEnd(); + t.traceBegin("StartLocationTimeZoneManagerService"); try { mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS); diff --git a/services/manifest_services.xml b/services/manifest_services.xml new file mode 100644 index 000000000000..76389154a885 --- /dev/null +++ b/services/manifest_services.xml @@ -0,0 +1,7 @@ +<manifest version="1.0" type="framework"> + <hal format="aidl"> + <name>android.frameworks.location.altitude</name> + <version>1</version> + <fqname>IAltitudeService/default</fqname> + </hal> +</manifest> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 32b98647e78b..cc5c2f3154df 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -448,8 +448,7 @@ public class AccessibilityManagerServiceTest { mA11yms.getCurrentUserIdLocked()); Settings.Secure.putIntForUser( mTestableContext.getContentResolver(), - // TODO: replace name with Settings Secure Key - "accessibility_magnification_always_on_enabled", + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, 1, mA11yms.getCurrentUserIdLocked()); mA11yms.readAlwaysOnMagnificationLocked(userState); @@ -466,8 +465,7 @@ public class AccessibilityManagerServiceTest { mA11yms.getCurrentUserIdLocked()); Settings.Secure.putIntForUser( mTestableContext.getContentResolver(), - // TODO: replace name with Settings Secure Key - "accessibility_magnification_always_on_enabled", + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, 1, mA11yms.getCurrentUserIdLocked()); mA11yms.readAlwaysOnMagnificationLocked(userState); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 38c2f40c2856..c31c23f9464a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP; import static android.app.Notification.FLAG_AUTO_CANCEL; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_CAN_COLORIZE; @@ -71,6 +72,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.google.common.truth.Truth.assertThat; @@ -201,6 +203,7 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.app.IAppOpsService; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; import com.android.internal.messages.nano.SystemMessageProto; @@ -373,6 +376,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; + + TestFlagResolver mTestFlagResolver = new TestFlagResolver(); + private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @Mock @@ -521,7 +527,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), - mTelecomManager, mLogger); + mTelecomManager, mLogger, mTestFlagResolver); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -1594,6 +1600,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception { + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); DeviceConfig.setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, @@ -1622,6 +1630,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception { + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); DeviceConfig.setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, @@ -4211,8 +4221,35 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testNoNotificationDuringSetupPermission() throws Exception { + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.NOTIFICATION_DURING_SETUP, PERMISSION_GRANTED); + Bundle extras = new Bundle(); + extras.putBoolean(EXTRA_ALLOW_DURING_SETUP, true); + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setContentTitle("foo") + .addExtras(extras) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, + "testNoNotificationDuringSetupPermission", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + NotificationRecord posted = mService.findNotificationLocked( + PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId()); + + assertTrue(posted.getNotification().extras.containsKey(EXTRA_ALLOW_DURING_SETUP)); + } + + @Test public void testNoFakeColorizedPermission() throws Exception { - when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_DENIED); Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setContentTitle("foo") @@ -4237,9 +4274,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testMediaStyleRemote_hasPermission() throws RemoteException { String deviceName = "device"; - when(mPackageManager.checkPermission( - eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt())) - .thenReturn(PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, PERMISSION_GRANTED); Notification.MediaStyle style = new Notification.MediaStyle(); style.setRemotePlaybackInfo(deviceName, 0, null); Notification.Builder nb = new Notification.Builder(mContext, @@ -4266,9 +4302,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testMediaStyleRemote_noPermission() throws RemoteException { String deviceName = "device"; - when(mPackageManager.checkPermission( - eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt())) - .thenReturn(PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, PERMISSION_DENIED); Notification.MediaStyle style = new Notification.MediaStyle(); style.setRemotePlaybackInfo(deviceName, 0, null); Notification.Builder nb = new Notification.Builder(mContext, @@ -4294,9 +4329,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSubstituteAppName_hasPermission() throws RemoteException { String subName = "Substitute Name"; - when(mPackageManager.checkPermission( - eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt())) - .thenReturn(PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, PERMISSION_GRANTED); Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, subName); Notification.Builder nb = new Notification.Builder(mContext, @@ -4321,9 +4355,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSubstituteAppName_noPermission() throws RemoteException { - when(mPackageManager.checkPermission( - eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt())) - .thenReturn(PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, PERMISSION_DENIED); Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Substitute Name"); Notification.Builder nb = new Notification.Builder(mContext, @@ -8290,7 +8323,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertNotNull(n.publicVersion.bigContentView); assertNotNull(n.publicVersion.headsUpContentView); - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); assertNull(n.contentView); assertNull(n.bigContentView); @@ -10092,8 +10125,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { // Given: a notification from an app on the system partition has the flag // FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); @@ -10104,21 +10137,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(systemAppInfo); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be set assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test public void fixMediaNotification_withOnGoingFlag_shouldBeNonDismissible() throws Exception { // Given: a media notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .setStyle(new Notification.MediaStyle() @@ -10126,32 +10156,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be set assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test public void fixNonExemptNotification_withOnGoingFlag_shouldBeDismissible() throws Exception { // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should not be set assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test @@ -10159,28 +10183,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { // Given: a non-exempt notification has the flag FLAG_NO_DISMISS set (even though this is // not allowed) - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .build(); n.flags |= Notification.FLAG_NO_DISMISS; // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be cleared assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test public void fixSystemNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception { // Given: a notification from an app on the system partition doesn't have the flag // FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(false) .build(); @@ -10191,13 +10212,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(systemAppInfo); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should not be set assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test @@ -10205,8 +10223,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { // Given: a notification from an app on the system partition doesn't have the flag // FLAG_ONGOING_EVENT set, but has the flag FLAG_NO_DISMISS set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(false) .build(); @@ -10218,20 +10236,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(systemAppInfo); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be cleared assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test public void fixMediaNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception { // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(false) .setStyle(new Notification.MediaStyle() @@ -10239,13 +10254,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should not be set assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test @@ -10253,8 +10265,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set, // but has the flag FLAG_NO_DISMISS set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(false) .setStyle(new Notification.MediaStyle() @@ -10263,34 +10275,27 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { n.flags |= Notification.FLAG_NO_DISMISS; // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be cleared assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test public void fixNonExempt_Notification_withoutOnGoingFlag_shouldBeDismissible() throws Exception { // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(false) .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should not be set assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test @@ -10298,21 +10303,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true); // Given: a notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); mService.setSystemExemptFromDismissal(false); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be set assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } @Test @@ -10322,21 +10324,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid, PKG)).thenReturn(AppOpsManager.MODE_ALLOWED); // Given: a notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); mService.setSystemExemptFromDismissal(true); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should be set assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); - // Avoid affecting other tests - mService.setOngoingDismissal(false); mService.setSystemExemptFromDismissal(false); } @@ -10347,20 +10347,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid, PKG)).thenReturn(AppOpsManager.MODE_ALLOWED); // Given: a notification has the flag FLAG_ONGOING_EVENT set - // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on - mService.setOngoingDismissal(true); + // feature flag: ALLOW_DISMISS_ONGOING is on + mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); mService.setSystemExemptFromDismissal(false); Notification n = new Notification.Builder(mContext, "test") .setOngoing(true) .build(); // When: fix the notification with NotificationManagerService - mService.fixNotification(n, PKG, "tag", 9, 0); + mService.fixNotification(n, PKG, "tag", 9, 0, mUid); // Then: the notification's flag FLAG_NO_DISMISS should not be set assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS); - - // Avoid affecting other tests - mService.setOngoingDismissal(false); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 98c156e6f3b5..6f2627a15cf5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -48,7 +48,6 @@ import android.companion.ICompanionDeviceManager; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; @@ -169,7 +168,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(ActivityManagerInternal.class), mock(MultiRateLimiter.class), mock(PermissionHelper.class), mock(UsageStatsManagerInternal.class), mock (TelecomManager.class), - mock(NotificationChannelLogger.class)); + mock(NotificationChannelLogger.class), new TestFlagResolver()); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java b/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java new file mode 100644 index 000000000000..3b9726ec7cbd --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; + +import java.util.HashMap; +import java.util.Map; + +public class TestFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver { + private Map<SystemUiSystemPropertiesFlags.Flag, Boolean> mOverrides = new HashMap<>(); + + @Override + public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) { + return mOverrides.getOrDefault(flag, flag.mDefaultValue); + } + + public void setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, boolean isEnabled) { + mOverrides.put(flag, isEnabled); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java index 1306d573e38c..dd6923db6dc0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java @@ -36,8 +36,6 @@ public class TestableNotificationManagerService extends NotificationManagerServi int countLogSmartSuggestionsVisible = 0; Set<Integer> mChannelToastsSent = new HashSet<>(); - public boolean ONGOING_DISMISSAL = false; - String stringArrayResourceValue; @Nullable NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; @@ -162,11 +160,6 @@ public class TestableNotificationManagerService extends NotificationManagerServi } } - // Mock SystemProperties - protected void setOngoingDismissal(boolean ongoingDismissal) { - ONGOING_DISMISSAL = ongoingDismissal; - } - protected void setSystemExemptFromDismissal(boolean isOn) { mSystemExemptFromDismissal = isOn; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 4954e89c1ffd..c2b3783b7311 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -147,6 +147,20 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { } @Test + public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() { + configureActivity(SCREEN_ORIENTATION_UNSPECIFIED); + spyOn(mTask); + spyOn(mDisplayRotationCompatPolicy); + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode(); + + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + + verify(mDisplayRotationCompatPolicy, never()).showToast( + R.string.display_rotation_camera_compat_toast_in_split_screen); + } + + @Test public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() { when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( /* checkDeviceConfig */ anyBoolean())) @@ -172,7 +186,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); - doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); + doReturn(true).when(mActivity).inMultiWindowMode(); spyOn(mDisplayRotationCompatPolicy); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); @@ -184,6 +198,18 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { } @Test + public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() { + configureActivity(SCREEN_ORIENTATION_UNSPECIFIED); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + spyOn(mDisplayRotationCompatPolicy); + + mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished(); + + verify(mDisplayRotationCompatPolicy, never()).showToast( + R.string.display_rotation_camera_compat_toast_after_rotation); + } + + @Test public void testOnScreenRotationAnimationFinished_showToast() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java new file mode 100644 index 000000000000..a27a5fd8a286 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Class for testing {@link SurfaceControl}. + * + * Build/Install/Run: + * atest WmTests:SurfaceControlTests + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SurfaceControlTests { + + @SmallTest + @Test + public void testUseValidSurface() { + SurfaceControl sc = buildTestSurface(); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setVisibility(sc, false); + sc.release(); + } + + @SmallTest + @Test + public void testUseInvalidSurface() { + SurfaceControl sc = buildTestSurface(); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + sc.release(); + try { + t.setVisibility(sc, false); + fail("Expected exception from updating invalid surface"); + } catch (Exception e) { + // Expected exception + } + } + + @SmallTest + @Test + public void testUseInvalidSurface_debugEnabled() { + SurfaceControl sc = buildTestSurface(); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + try { + SurfaceControl.setDebugUsageAfterRelease(true); + sc.release(); + try { + t.setVisibility(sc, false); + fail("Expected exception from updating invalid surface"); + } catch (IllegalStateException ise) { + assertNotNull(ise.getCause()); + } catch (Exception e) { + fail("Expected IllegalStateException with cause"); + } + } finally { + SurfaceControl.setDebugUsageAfterRelease(false); + } + } + + @SmallTest + @Test + public void testWriteInvalidSurface_debugEnabled() { + SurfaceControl sc = buildTestSurface(); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + Parcel p = Parcel.obtain(); + try { + SurfaceControl.setDebugUsageAfterRelease(true); + sc.release(); + try { + sc.writeToParcel(p, 0 /* flags */); + fail("Expected exception from writing invalid surface to parcel"); + } catch (IllegalStateException ise) { + assertNotNull(ise.getCause()); + } catch (Exception e) { + fail("Expected IllegalStateException with cause"); + } + } finally { + SurfaceControl.setDebugUsageAfterRelease(false); + p.recycle(); + } + } + + private SurfaceControl buildTestSurface() { + return new SurfaceControl.Builder() + .setContainerLayer() + .setName("SurfaceControlTests") + .setCallsite("SurfaceControlTests") + .build(); + } +} diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 12e5ed95fecb..77b263824b78 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1627,6 +1627,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser int status, int mRequest, long mFunctions, boolean mChargingFunctions); public abstract void getUsbSpeedCb(int speed); + + public abstract void resetCb(int status); } private static final class UsbHandlerLegacy extends UsbHandler { @@ -1988,14 +1990,30 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser return true; } + /** + * This callback function is only applicable for USB Gadget HAL, + * USBHandlerLegacy does not supported it. + */ @Override public void setCurrentUsbFunctionsCb(long functions, int status, int mRequest, long mFunctions, boolean mChargingFunctions){ } + /** + * This callback function is only applicable for USB Gadget HAL, + * USBHandlerLegacy does not supported it. + */ @Override public void getUsbSpeedCb(int speed){ } + + /** + * This callback function is only applicable for USB Gadget HAL, + * USBHandlerLegacy does not supported it. + */ + @Override + public void resetCb(int status){ + } } private static final class UsbHandlerHal extends UsbHandler { @@ -2147,6 +2165,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } break; case MSG_RESET_USB_GADGET: + operationId = sUsbOperationCount.incrementAndGet(); synchronized (mGadgetProxyLock) { if (mUsbGadgetHal == null) { Slog.e(TAG, "reset Usb Gadget mUsbGadgetHal is null"); @@ -2160,7 +2179,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser if (mConfigured) { mResetUsbGadgetDisableDebounce = true; } - mUsbGadgetHal.reset(); + mUsbGadgetHal.reset(operationId); } catch (Exception e) { Slog.e(TAG, "reset Usb Gadget failed", e); mResetUsbGadgetDisableDebounce = false; @@ -2222,6 +2241,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser mUsbSpeed = speed; } + @Override + public void resetCb(int status) { + if (status != Status.SUCCESS) + Slog.e(TAG, "resetCb fail"); + } + private void setUsbConfig(long config, boolean chargingFunctions, int operationId) { if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest); /** @@ -2363,6 +2388,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser mHandler.getUsbSpeedCb(speed); } + public void resetCb(int status) { + mHandler.resetCb(status); + } + /** * Returns a dup of the control file descriptor for the given function. */ diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java index bdfe60ac07c1..d47ccc7f05a0 100644 --- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java +++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java @@ -23,6 +23,7 @@ import static com.android.server.usb.UsbDeviceManager.logAndPrintException; import android.annotation.Nullable; import android.hardware.usb.gadget.IUsbGadget; import android.hardware.usb.gadget.IUsbGadgetCallback; +import android.hardware.usb.Status; import android.hardware.usb.UsbManager.UsbGadgetHalVersion; import android.os.ServiceManager; import android.os.IBinder; @@ -139,14 +140,15 @@ public final class UsbGadgetAidl implements UsbGadgetHal { } @Override - public void reset() { + public void reset(long operationId) { try { synchronized (mGadgetProxyLock) { - mGadgetProxy.reset(); + mGadgetProxy.reset(new UsbGadgetCallback(), operationId); } } catch (RemoteException e) { logAndPrintException(mPw, - "RemoteException while calling getUsbSpeed", e); + "RemoteException while calling getUsbSpeed" + + ", opID:" + operationId, e); return; } } @@ -155,7 +157,7 @@ public final class UsbGadgetAidl implements UsbGadgetHal { public void setCurrentUsbFunctions(int mRequest, long mFunctions, boolean mChargingFunctions, int timeout, long operationId) { try { - mUsbGadgetCallback = new UsbGadgetCallback(mRequest, + mUsbGadgetCallback = new UsbGadgetCallback(null, mRequest, mFunctions, mChargingFunctions); synchronized (mGadgetProxyLock) { mGadgetProxy.setCurrentUsbFunctions(mFunctions, mUsbGadgetCallback, @@ -174,6 +176,7 @@ public final class UsbGadgetAidl implements UsbGadgetHal { } private class UsbGadgetCallback extends IUsbGadgetCallback.Stub { + public IndentingPrintWriter mPw; public int mRequest; public long mFunctions; public boolean mChargingFunctions; @@ -181,8 +184,9 @@ public final class UsbGadgetAidl implements UsbGadgetHal { UsbGadgetCallback() { } - UsbGadgetCallback(int request, long functions, + UsbGadgetCallback(IndentingPrintWriter pw, int request, long functions, boolean chargingFunctions) { + mPw = pw; mRequest = request; mFunctions = functions; mChargingFunctions = chargingFunctions; @@ -191,6 +195,15 @@ public final class UsbGadgetAidl implements UsbGadgetHal { @Override public void setCurrentUsbFunctionsCb(long functions, int status, long transactionId) { + if (status == Status.SUCCESS) { + logAndPrint(Log.INFO, mPw, "Usb setCurrentUsbFunctionsCb" + + " ,functions:" + functions + " ,status:" + status + + " ,transactionId:" + transactionId); + } else { + logAndPrint(Log.ERROR, mPw, "Usb setCurrentUsbFunctionsCb failed" + + " ,functions:" + functions + " ,status:" + status + + " ,transactionId:" + transactionId); + } mDeviceManager.setCurrentUsbFunctionsCb(functions, status, mRequest, mFunctions, mChargingFunctions); } @@ -198,15 +211,38 @@ public final class UsbGadgetAidl implements UsbGadgetHal { @Override public void getCurrentUsbFunctionsCb(long functions, int status, long transactionId) { + if (status == Status.SUCCESS) { + logAndPrint(Log.INFO, mPw, "Usb getCurrentUsbFunctionsCb" + + " ,functions:" + functions + " ,status:" + status + + " ,transactionId:" + transactionId); + } else { + logAndPrint(Log.ERROR, mPw, "Usb getCurrentUsbFunctionsCb failed" + + " ,functions:" + functions + " ,status:" + status + + " ,transactionId:" + transactionId); + } mDeviceManager.getCurrentUsbFunctionsCb(functions, status); } @Override public void getUsbSpeedCb(int speed, long transactionId) { + logAndPrint(Log.INFO, mPw, "getUsbSpeedCb speed:" + + speed + " ,transactionId:" + transactionId); mDeviceManager.getUsbSpeedCb(speed); } @Override + public void resetCb(int status, long transactionId) { + if (status == Status.SUCCESS) { + logAndPrint(Log.INFO, mPw, "Usb resetCb status:" + + status + " ,transactionId:" + transactionId); + } else { + logAndPrint(Log.ERROR, mPw, "Usb resetCb status" + + status + " ,transactionId:" + transactionId); + } + mDeviceManager.resetCb(status); + } + + @Override public String getInterfaceHash() { return IUsbGadgetCallback.HASH; } diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java index 267247b5b835..7b52f46e77cc 100644 --- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java +++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java @@ -112,8 +112,11 @@ public interface UsbGadgetHal { * This function is used to reset USB gadget driver. * Performs USB data connection reset. The connection will disconnect and * reconnect. + * + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. */ - public void reset(); + public void reset(long transactionId); /** * Invoked to query the version of current gadget hal implementation. diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java index 3e5ecc5eddf4..2c5fcb86b0c5 100644 --- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java +++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java @@ -188,7 +188,7 @@ public final class UsbGadgetHidl implements UsbGadgetHal { } @Override - public void reset() { + public void reset(long transactionId) { try { synchronized(mGadgetProxyLock) { if (android.hardware.usb.gadget.V1_2.IUsbGadget.castFrom(mGadgetProxy) != null) { @@ -199,7 +199,7 @@ public final class UsbGadgetHidl implements UsbGadgetHal { } } catch (RemoteException e) { logAndPrintException(mPw, - "RemoteException while calling getUsbSpeed", e); + "RemoteException while calling reset", e); return; } } diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index 061b71b25275..d676eeec4024 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -110,8 +110,15 @@ public final class AnomalyReporter { return; } - // Don't report if the server-side flag isn't loaded, as it implies other anomaly report - // related config hasn't loaded. + //always write atoms to statsd + TelephonyStatsLog.write( + TELEPHONY_ANOMALY_DETECTED, + carrierId, + eventId.getLeastSignificantBits(), + eventId.getMostSignificantBits()); + + // Don't report via Intent if the server-side flag isn't loaded, as it implies other anomaly + // report related config hasn't loaded. try { boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED, @@ -122,12 +129,6 @@ public final class AnomalyReporter { return; } - TelephonyStatsLog.write( - TELEPHONY_ANOMALY_DETECTED, - carrierId, - eventId.getLeastSignificantBits(), - eventId.getMostSignificantBits()); - // If this event has already occurred, skip sending intents for it; regardless log its // invocation here. Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1; diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index b83b400cb811..c4d760f8db52 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -1012,6 +1012,8 @@ public final class DataFailCause { public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004; /** No response received from the DNS Server due to a timeout*/ public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005; + /** Expected to update or bring down an ePDG tunnel, but no tunnel found*/ + public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006; // OEM sepecific error codes. To be used by OEMs when they don't // want to reveal error code which would be replaced by ERROR_UNSPECIFIED @@ -1505,6 +1507,7 @@ public final class DataFailCause { sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID"); sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE"); sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT"); + sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND"); sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1"); sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2"); sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3"); diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java index 4103ca7c8ca8..210e3ea2a9b2 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -148,6 +148,10 @@ public class UsbHandlerTest { public void getUsbSpeedCb(int speed){ } + @Override + public void resetCb(int status){ + } + } @Before |