summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/test-current.txt34
-rw-r--r--core/java/android/credentials/CredentialManager.java110
-rw-r--r--core/java/android/credentials/CredentialProviderInfo.aidl (renamed from core/java/android/credentials/IListEnabledProvidersCallback.aidl)14
-rw-r--r--core/java/android/credentials/CredentialProviderInfo.java215
-rw-r--r--core/java/android/credentials/ICredentialManager.aidl9
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java39
-rw-r--r--core/java/android/service/credentials/CredentialEntry.java2
-rw-r--r--core/java/android/service/credentials/CredentialProviderInfoFactory.java (renamed from core/java/android/service/credentials/CredentialProviderInfo.java)297
-rw-r--r--core/java/android/view/VelocityTracker.java7
-rw-r--r--core/jni/android_view_InputEventSender.cpp29
-rw-r--r--core/tests/coretests/src/android/credentials/CredentialManagerTest.java107
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java163
-rw-r--r--libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java9
-rw-r--r--libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java111
-rw-r--r--libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java39
-rw-r--r--libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java113
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java8
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicyConfig.java4
-rw-r--r--media/java/android/media/projection/MediaProjection.java13
-rw-r--r--media/tests/AudioPolicyTest/Android.bp1
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java158
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt8
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt118
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt237
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt4
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java46
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java21
-rw-r--r--services/core/java/com/android/server/power/stats/CpuWakeupStats.java81
-rw-r--r--services/credentials/java/com/android/server/credentials/ClearRequestSession.java17
-rw-r--r--services/credentials/java/com/android/server/credentials/CreateRequestSession.java22
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java105
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java10
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java22
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java18
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderClearSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderCreateSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderSession.java12
-rw-r--r--services/credentials/java/com/android/server/credentials/RequestSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java8
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java3
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java15
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java (renamed from services/credentials/java/com/android/server/credentials/metrics/PreCandidateMetric.java)73
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java52
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt51
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt3
106 files changed, 2963 insertions, 735 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 589b055692bc..211f60350cfa 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40645,6 +40645,7 @@ package android.service.credentials {
public class CredentialEntry implements android.os.Parcelable {
ctor public CredentialEntry(@NonNull String, @NonNull String, @NonNull android.app.slice.Slice);
ctor public CredentialEntry(@NonNull android.service.credentials.BeginGetCredentialOption, @NonNull android.app.slice.Slice);
+ ctor public CredentialEntry(@NonNull String, @NonNull android.app.slice.Slice);
method public int describeContents();
method @NonNull public String getBeginGetCredentialOptionId();
method @NonNull public android.app.slice.Slice getSlice();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c3456ee2d976..a3bc3922126b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1072,6 +1072,40 @@ package android.content.rollback {
}
+package android.credentials {
+
+ public final class CredentialManager {
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.QUERY_ALL_PACKAGES, "android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"}) public java.util.List<android.credentials.CredentialProviderInfo> getCredentialProviderServicesForTesting(int);
+ method public static boolean isServiceEnabled(@NonNull android.content.Context);
+ field public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0; // 0x0
+ field public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1; // 0x1
+ field public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2; // 0x2
+ }
+
+ public final class CredentialProviderInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getCapabilities();
+ method @NonNull public android.content.ComponentName getComponentName();
+ method @Nullable public CharSequence getLabel(@NonNull android.content.Context);
+ method @Nullable public android.graphics.drawable.Drawable getServiceIcon(@NonNull android.content.Context);
+ method @NonNull public android.content.pm.ServiceInfo getServiceInfo();
+ method @NonNull public boolean hasCapability(@NonNull String);
+ method public boolean isEnabled();
+ method public boolean isSystemProvider();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CredentialProviderInfo> CREATOR;
+ }
+
+ public static final class CredentialProviderInfo.Builder {
+ ctor public CredentialProviderInfo.Builder(@NonNull android.content.pm.ServiceInfo);
+ method @NonNull public android.credentials.CredentialProviderInfo.Builder addCapabilities(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.credentials.CredentialProviderInfo build();
+ method @NonNull public android.credentials.CredentialProviderInfo.Builder setEnabled(boolean);
+ method @NonNull public android.credentials.CredentialProviderInfo.Builder setSystemProvider(boolean);
+ }
+
+}
+
package android.credentials.ui {
public final class AuthenticationEntry implements android.os.Parcelable {
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index f0230e7f4593..0806f1db2bb7 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -26,12 +26,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
-import android.content.pm.ServiceInfo;
import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
@@ -75,21 +75,21 @@ public final class CredentialManager {
*
* @hide
*/
- public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
+ @TestApi public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
/**
* Returns system credential providers only.
*
* @hide
*/
- public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
+ @TestApi public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
/**
* Returns user credential providers only.
*
* @hide
*/
- public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
+ @TestApi public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
private final Context mContext;
private final ICredentialManager mService;
@@ -263,44 +263,6 @@ public final class CredentialManager {
}
/**
- * Gets a list of all user configurable credential providers registered on the system. This API
- * is intended for browsers and settings apps.
- *
- * @param cancellationSignal an optional signal that allows for cancelling this call
- * @param executor the callback will take place on this {@link Executor}
- * @param callback the callback invoked when the request succeeds or fails
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public void listEnabledProviders(
- @Nullable CancellationSignal cancellationSignal,
- @CallbackExecutor @NonNull Executor executor,
- @NonNull
- OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
- callback) {
- requireNonNull(executor, "executor must not be null");
- requireNonNull(callback, "callback must not be null");
-
- if (cancellationSignal != null && cancellationSignal.isCanceled()) {
- Log.w(TAG, "listEnabledProviders already canceled");
- return;
- }
-
- ICancellationSignal cancelRemote = null;
- try {
- cancelRemote =
- mService.listEnabledProviders(
- new ListEnabledProvidersTransport(executor, callback));
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
-
- if (cancellationSignal != null && cancelRemote != null) {
- cancellationSignal.setRemote(cancelRemote);
- }
- }
-
- /**
* Sets a list of all user configurable credential providers registered on the system. This API
* is intended for settings apps.
*
@@ -348,36 +310,43 @@ public final class CredentialManager {
}
/**
- * Returns the list of ServiceInfo for all discovered credential providers on this device.
+ * Returns the list of CredentialProviderInfo for all discovered credential providers on this
+ * device but will include test system providers as well.
*
* @hide
*/
@NonNull
- @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
- public List<ServiceInfo> getCredentialProviderServicesForTesting(
- @ProviderFilter int providerFilter) {
+ @TestApi
+ @RequiresPermission(
+ anyOf = {
+ android.Manifest.permission.QUERY_ALL_PACKAGES,
+ android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
+ })
+ public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+ @ProviderFilter int providerFilter) {
try {
- return mService.getCredentialProviderServices(
- mContext.getUserId(),
- /* disableSystemAppVerificationForTests= */ true,
- providerFilter);
+ return mService.getCredentialProviderServicesForTesting(providerFilter);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns the list of ServiceInfo for all discovered credential providers on this device.
+ * Returns the list of CredentialProviderInfo for all discovered credential providers on this
+ * device.
*
* @hide
*/
@NonNull
- @RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
- public List<ServiceInfo> getCredentialProviderServices(
+ @RequiresPermission(
+ anyOf = {
+ android.Manifest.permission.QUERY_ALL_PACKAGES,
+ android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS
+ })
+ public List<CredentialProviderInfo> getCredentialProviderServices(
int userId, @ProviderFilter int providerFilter) {
try {
- return mService.getCredentialProviderServices(
- userId, /* disableSystemAppVerificationForTests= */ false, providerFilter);
+ return mService.getCredentialProviderServices(userId, providerFilter);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -388,7 +357,9 @@ public final class CredentialManager {
*
* @hide
*/
- public static boolean isServiceEnabled(Context context) {
+ @TestApi
+ public static boolean isServiceEnabled(@NonNull Context context) {
+ requireNonNull(context, "context must not be null");
if (context == null) {
return false;
}
@@ -578,33 +549,6 @@ public final class CredentialManager {
}
}
- private static class ListEnabledProvidersTransport extends IListEnabledProvidersCallback.Stub {
- // TODO: listen for cancellation to release callback.
-
- private final Executor mExecutor;
- private final OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
- mCallback;
-
- private ListEnabledProvidersTransport(
- Executor executor,
- OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
- callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void onResponse(ListEnabledProvidersResponse response) {
- mExecutor.execute(() -> mCallback.onResult(response));
- }
-
- @Override
- public void onError(String errorType, String message) {
- mExecutor.execute(
- () -> mCallback.onError(new ListEnabledProvidersException(errorType, message)));
- }
- }
-
private static class SetEnabledProvidersTransport extends ISetEnabledProvidersCallback.Stub {
// TODO: listen for cancellation to release callback.
diff --git a/core/java/android/credentials/IListEnabledProvidersCallback.aidl b/core/java/android/credentials/CredentialProviderInfo.aidl
index 3a8e25ed954a..30b7742d17f8 100644
--- a/core/java/android/credentials/IListEnabledProvidersCallback.aidl
+++ b/core/java/android/credentials/CredentialProviderInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,4 @@
package android.credentials;
-import android.credentials.ListEnabledProvidersResponse;
-
-/**
- * Listener for an listEnabledProviders request.
- *
- * @hide
- */
-interface IListEnabledProvidersCallback {
- oneway void onResponse(in ListEnabledProvidersResponse response);
- oneway void onError(String errorType, String message);
-} \ No newline at end of file
+parcelable CredentialProviderInfo; \ No newline at end of file
diff --git a/core/java/android/credentials/CredentialProviderInfo.java b/core/java/android/credentials/CredentialProviderInfo.java
new file mode 100644
index 000000000000..7276770d281e
--- /dev/null
+++ b/core/java/android/credentials/CredentialProviderInfo.java
@@ -0,0 +1,215 @@
+/*
+ * 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.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link ServiceInfo} and meta-data about a credential provider.
+ *
+ * @hide
+ */
+@TestApi
+public final class CredentialProviderInfo implements Parcelable {
+ @NonNull private final ServiceInfo mServiceInfo;
+ @NonNull private final List<String> mCapabilities = new ArrayList<>();
+ @Nullable private final CharSequence mOverrideLabel;
+ private final boolean mIsSystemProvider;
+ private final boolean mIsEnabled;
+
+ /**
+ * Constructs an information instance of the credential provider.
+ *
+ * @param builder the builder object.
+ */
+ private CredentialProviderInfo(@NonNull Builder builder) {
+ mServiceInfo = builder.mServiceInfo;
+ mCapabilities.addAll(builder.mCapabilities);
+ mIsSystemProvider = builder.mIsSystemProvider;
+ mIsEnabled = builder.mIsEnabled;
+ mOverrideLabel = builder.mOverrideLabel;
+ }
+
+ /** Returns true if the service supports the given {@code credentialType}, false otherwise. */
+ @NonNull
+ public boolean hasCapability(@NonNull String credentialType) {
+ return mCapabilities.contains(credentialType);
+ }
+
+ /** Returns the service info. */
+ @NonNull
+ public ServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ /** Returns whether it is a system provider. */
+ public boolean isSystemProvider() {
+ return mIsSystemProvider;
+ }
+
+ /** Returns the service icon. */
+ @Nullable
+ public Drawable getServiceIcon(@NonNull Context context) {
+ return mServiceInfo.loadIcon(context.getPackageManager());
+ }
+
+ /** Returns the service label. */
+ @Nullable
+ public CharSequence getLabel(@NonNull Context context) {
+ if (mOverrideLabel != null) {
+ return mOverrideLabel;
+ }
+ return mServiceInfo.loadSafeLabel(context.getPackageManager());
+ }
+
+ /** Returns a list of capabilities this provider service can support. */
+ @NonNull
+ public List<String> getCapabilities() {
+ return Collections.unmodifiableList(mCapabilities);
+ }
+
+ /** Returns whether the provider is enabled by the user. */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /** Returns the component name for the service. */
+ @NonNull
+ public ComponentName getComponentName() {
+ return mServiceInfo.getComponentName();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mServiceInfo, flags);
+ dest.writeBoolean(mIsSystemProvider);
+ dest.writeStringList(mCapabilities);
+ dest.writeBoolean(mIsEnabled);
+ TextUtils.writeToParcel(mOverrideLabel, dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialProviderInfo {"
+ + "serviceInfo="
+ + mServiceInfo
+ + ", "
+ + "isSystemProvider="
+ + mIsSystemProvider
+ + ", "
+ + "isEnabled="
+ + mIsEnabled
+ + ", "
+ + "overrideLabel="
+ + mOverrideLabel
+ + ", "
+ + "capabilities="
+ + String.join(",", mCapabilities)
+ + "}";
+ }
+
+ private CredentialProviderInfo(@NonNull Parcel in) {
+ mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR);
+ mIsSystemProvider = in.readBoolean();
+ in.readStringList(mCapabilities);
+ mIsEnabled = in.readBoolean();
+ mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ }
+
+ public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR =
+ new Parcelable.Creator<CredentialProviderInfo>() {
+ @Override
+ public CredentialProviderInfo[] newArray(int size) {
+ return new CredentialProviderInfo[size];
+ }
+
+ @Override
+ public CredentialProviderInfo createFromParcel(@NonNull Parcel in) {
+ return new CredentialProviderInfo(in);
+ }
+ };
+
+ /** A builder for {@link CredentialProviderInfo} objects. */
+ public static final class Builder {
+
+ @NonNull private ServiceInfo mServiceInfo;
+ @NonNull private List<String> mCapabilities = new ArrayList<>();
+ private boolean mIsSystemProvider = false;
+ private boolean mIsEnabled = false;
+ @Nullable private CharSequence mOverrideLabel = null;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param serviceInfo the service info of the credential provider service.
+ */
+ public Builder(@NonNull ServiceInfo serviceInfo) {
+ mServiceInfo = serviceInfo;
+ }
+
+ /** Sets whether it is a system provider. */
+ public @NonNull Builder setSystemProvider(boolean isSystemProvider) {
+ mIsSystemProvider = isSystemProvider;
+ return this;
+ }
+
+ /**
+ * Sets the label to be used instead of getting from the system (for unit tests).
+ *
+ * @hide
+ */
+ public @NonNull Builder setOverrideLabel(@NonNull CharSequence overrideLabel) {
+ mOverrideLabel = overrideLabel;
+ return this;
+ }
+
+ /** Sets a list of capabilities this provider service can support. */
+ public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) {
+ mCapabilities.addAll(capabilities);
+ return this;
+ }
+
+ /** Sets whether it is enabled by the user. */
+ public @NonNull Builder setEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ return this;
+ }
+
+ /** Builds a new {@link CredentialProviderInfo} instance. */
+ public @NonNull CredentialProviderInfo build() {
+ return new CredentialProviderInfo(this);
+ }
+ }
+}
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index 625fc8ab5dad..8c2cb5aa0d77 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -18,7 +18,7 @@ package android.credentials;
import java.util.List;
-import android.content.pm.ServiceInfo;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
import android.credentials.GetCredentialRequest;
@@ -27,7 +27,6 @@ import android.credentials.UnregisterCredentialDescriptionRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.IGetCredentialCallback;
-import android.credentials.IListEnabledProvidersCallback;
import android.credentials.ISetEnabledProvidersCallback;
import android.content.ComponentName;
import android.os.ICancellationSignal;
@@ -45,8 +44,6 @@ interface ICredentialManager {
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
- @nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);
-
void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
void registerCredentialDescription(in RegisterCredentialDescriptionRequest request, String callingPackage);
@@ -55,6 +52,8 @@ interface ICredentialManager {
boolean isEnabledCredentialProviderService(in ComponentName componentName, String callingPackage);
- List<ServiceInfo> getCredentialProviderServices(in int userId, in boolean disableSystemAppVerificationForTests, in int providerFilter);
+ List<CredentialProviderInfo> getCredentialProviderServices(in int userId, in int providerFilter);
+
+ List<CredentialProviderInfo> getCredentialProviderServicesForTesting(in int providerFilter);
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 067ae4d438cc..490e55ba260f 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
+import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.Surface;
@@ -53,6 +54,8 @@ public final class VirtualDisplayConfig implements Parcelable {
private final int mDisplayIdToMirror;
private final boolean mWindowManagerMirroringEnabled;
private ArraySet<String> mDisplayCategories = null;
+ @Nullable
+ private ContentRecordingSession mContentRecordingSession;
private final float mRequestedRefreshRate;
private VirtualDisplayConfig(
@@ -65,6 +68,7 @@ public final class VirtualDisplayConfig implements Parcelable {
@Nullable String uniqueId,
int displayIdToMirror,
boolean windowManagerMirroringEnabled,
+ ContentRecordingSession session,
@NonNull ArraySet<String> displayCategories,
float requestedRefreshRate) {
mName = name;
@@ -76,6 +80,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId = uniqueId;
mDisplayIdToMirror = displayIdToMirror;
mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
+ mContentRecordingSession = session;
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
}
@@ -156,6 +161,17 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Returns the recording session associated with this VirtualDisplay. Only used for
+ * recording via {@link MediaProjection}.
+ *
+ * @hide
+ */
+ @Nullable
+ public ContentRecordingSession getContentRecordingSession() {
+ return mContentRecordingSession;
+ }
+
+ /**
* Returns the display categories.
*
* @see Builder#setDisplayCategories
@@ -186,6 +202,7 @@ public final class VirtualDisplayConfig implements Parcelable {
dest.writeString8(mUniqueId);
dest.writeInt(mDisplayIdToMirror);
dest.writeBoolean(mWindowManagerMirroringEnabled);
+ dest.writeTypedObject(mContentRecordingSession, flags);
dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
}
@@ -211,6 +228,7 @@ public final class VirtualDisplayConfig implements Parcelable {
&& Objects.equals(mUniqueId, that.mUniqueId)
&& mDisplayIdToMirror == that.mDisplayIdToMirror
&& mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
+ && Objects.equals(mContentRecordingSession, that.mContentRecordingSession)
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
&& mRequestedRefreshRate == that.mRequestedRefreshRate;
}
@@ -219,8 +237,8 @@ public final class VirtualDisplayConfig implements Parcelable {
public int hashCode() {
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
- mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
- mRequestedRefreshRate);
+ mDisplayIdToMirror, mWindowManagerMirroringEnabled, mContentRecordingSession,
+ mDisplayCategories, mRequestedRefreshRate);
return hashCode;
}
@@ -237,6 +255,7 @@ public final class VirtualDisplayConfig implements Parcelable {
+ " mUniqueId=" + mUniqueId
+ " mDisplayIdToMirror=" + mDisplayIdToMirror
+ " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
+ + " mContentRecordingSession=" + mContentRecordingSession
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ ")";
@@ -252,6 +271,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId = in.readString8();
mDisplayIdToMirror = in.readInt();
mWindowManagerMirroringEnabled = in.readBoolean();
+ mContentRecordingSession = in.readTypedObject(ContentRecordingSession.CREATOR);
mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
}
@@ -283,6 +303,8 @@ public final class VirtualDisplayConfig implements Parcelable {
private String mUniqueId = null;
private int mDisplayIdToMirror = DEFAULT_DISPLAY;
private boolean mWindowManagerMirroringEnabled = false;
+ @Nullable
+ private ContentRecordingSession mContentRecordingSession;
private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
@@ -375,6 +397,18 @@ public final class VirtualDisplayConfig implements Parcelable {
}
/**
+ * Sets the recording session associated with this {@link VirtualDisplay}. Only used for
+ * recording via {@link MediaProjection}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setContentRecordingSession(@Nullable ContentRecordingSession session) {
+ mContentRecordingSession = session;
+ return this;
+ }
+
+ /**
* Sets the display categories.
*
* <p>The categories of the display indicate the type of activities allowed to run on that
@@ -435,6 +469,7 @@ public final class VirtualDisplayConfig implements Parcelable {
mUniqueId,
mDisplayIdToMirror,
mWindowManagerMirroringEnabled,
+ mContentRecordingSession,
mDisplayCategories,
mRequestedRefreshRate);
}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index 85eac6edb6d2..e9cebd2e6af7 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -131,9 +131,7 @@ public class CredentialEntry implements Parcelable {
* @param slice the slice containing the metadata to be shown on the UI. Must be
* constructed through the androidx.credentials jetpack library.
*
- * @hide
*/
- // TODO: Unhide this constructor when the registry APIs are stable
public CredentialEntry(@NonNull String type, @NonNull Slice slice) {
mBeginGetCredentialOptionId = null;
mType = requireNonNull(type, "type must not be null");
diff --git a/core/java/android/service/credentials/CredentialProviderInfo.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index b5464db98d0e..fd9360f00d4a 100644
--- a/core/java/android/service/credentials/CredentialProviderInfo.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -34,40 +34,27 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.credentials.CredentialManager;
-import android.graphics.drawable.Drawable;
+import android.credentials.CredentialProviderInfo;
import android.os.Bundle;
import android.os.RemoteException;
-import android.text.TextUtils;
+import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
- * {@link ServiceInfo} and meta-data about a credential provider.
+ * {@link CredentialProviderInfo} generator.
*
* @hide
*/
-public final class CredentialProviderInfo {
- private static final String TAG = "CredentialProviderInfo";
-
- @NonNull
- private final ServiceInfo mServiceInfo;
- @NonNull
- private final List<String> mCapabilities;
-
- @NonNull
- private final Context mContext;
- @Nullable
- private final Drawable mIcon;
- @Nullable
- private final CharSequence mLabel;
- private final boolean mIsSystemProvider;
+public final class CredentialProviderInfoFactory {
+ private static final String TAG = "CredentialProviderInfoFactory";
/**
* Constructs an information instance of the credential provider.
@@ -79,14 +66,18 @@ public final class CredentialProviderInfo {
* @throws PackageManager.NameNotFoundException If provider service is not found
* @throws SecurityException If provider does not require the relevant permission
*/
- public CredentialProviderInfo(@NonNull Context context,
- @NonNull ComponentName serviceComponent, int userId, boolean isSystemProvider)
+ public static CredentialProviderInfo create(
+ @NonNull Context context,
+ @NonNull ComponentName serviceComponent,
+ int userId,
+ boolean isSystemProvider)
throws PackageManager.NameNotFoundException {
- this(
+ return create(
context,
getServiceInfoOrThrow(serviceComponent, userId),
isSystemProvider,
- /* disableSystemAppVerificationForTests= */ false);
+ /* disableSystemAppVerificationForTests= */ false,
+ /* isEnabled= */ false);
}
/**
@@ -98,13 +89,16 @@ public final class CredentialProviderInfo {
* @param isSystemProvider whether the provider app is a system provider
* @param disableSystemAppVerificationForTests whether to disable system app permission
* verification so that tests can install system providers
+ * @param isEnabled whether the user enabled this provider
* @throws SecurityException If provider does not require the relevant permission
*/
- public CredentialProviderInfo(
+ public static CredentialProviderInfo create(
@NonNull Context context,
@NonNull ServiceInfo serviceInfo,
boolean isSystemProvider,
- boolean disableSystemAppVerificationForTests) {
+ boolean disableSystemAppVerificationForTests,
+ boolean isEnabled)
+ throws SecurityException {
verifyProviderPermission(serviceInfo);
if (isSystemProvider) {
if (!isValidSystemProvider(
@@ -114,23 +108,11 @@ public final class CredentialProviderInfo {
"Provider is not a valid system provider: " + serviceInfo);
}
}
- mIsSystemProvider = isSystemProvider;
- mContext = requireNonNull(context, "context must not be null");
- mServiceInfo = requireNonNull(serviceInfo, "serviceInfo must not be null");
- mCapabilities = new ArrayList<>();
- mIcon = mServiceInfo.loadIcon(mContext.getPackageManager());
- mLabel =
- mServiceInfo.loadSafeLabel(
- mContext.getPackageManager(),
- 0 /* do not ellipsize */,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
- Log.i(
- TAG,
- "mLabel is : "
- + mLabel
- + ", for: "
- + mServiceInfo.getComponentName().flattenToString());
- populateProviderCapabilities(context, serviceInfo);
+
+ return populateMetadata(context, serviceInfo)
+ .setSystemProvider(isSystemProvider)
+ .setEnabled(isEnabled)
+ .build();
}
private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -138,19 +120,14 @@ public final class CredentialProviderInfo {
if (permission.equals(serviceInfo.permission)) {
return;
}
-
- Slog.e(
- TAG,
- "Credential Provider Service from : "
- + serviceInfo.packageName
- + "does not require permission"
- + permission);
throw new SecurityException(
"Service does not require the expected permission : " + permission);
}
private static boolean isSystemProviderWithValidPermission(
ServiceInfo serviceInfo, Context context) {
+ requireNonNull(context, "context must not be null");
+
final String permission = Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE;
try {
ApplicationInfo appInfo =
@@ -177,67 +154,88 @@ public final class CredentialProviderInfo {
Context context,
ServiceInfo serviceInfo,
boolean disableSystemAppVerificationForTests) {
- boolean isValidSystemTestProvider =
- isTestSystemProvider(serviceInfo, disableSystemAppVerificationForTests);
- if (isValidSystemTestProvider) {
- return true;
+ requireNonNull(context, "context must not be null");
+
+ if (disableSystemAppVerificationForTests) {
+ Bundle metadata = serviceInfo.metaData;
+ if (metadata == null) {
+ Slog.e(TAG, "isValidSystemProvider - metadata is null: " + serviceInfo);
+ return false;
+ }
+ return metadata.getBoolean(
+ CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
}
+
return isSystemProviderWithValidPermission(serviceInfo, context);
}
- private static boolean isTestSystemProvider(
- ServiceInfo serviceInfo, boolean disableSystemAppVerificationForTests) {
- if (!disableSystemAppVerificationForTests) {
- return false;
- }
+ private static CredentialProviderInfo.Builder populateMetadata(
+ @NonNull Context context, ServiceInfo serviceInfo) {
+ requireNonNull(context, "context must not be null");
- Bundle metadata = serviceInfo.metaData;
+ final CredentialProviderInfo.Builder builder =
+ new CredentialProviderInfo.Builder(serviceInfo);
+ final PackageManager pm = context.getPackageManager();
+
+ // 1. Get the metadata for the service.
+ final Bundle metadata = serviceInfo.metaData;
if (metadata == null) {
- Slog.e(TAG, "metadata is null: " + serviceInfo);
- return false;
+ Log.i(TAG, "populateMetadata - metadata is null");
+ return builder;
}
- return metadata.getBoolean(CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
- }
- private void populateProviderCapabilities(@NonNull Context context, ServiceInfo serviceInfo) {
- final PackageManager pm = context.getPackageManager();
+ // 2. Extract the capabilities from the bundle.
try {
- Bundle metadata = serviceInfo.metaData;
Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
if (metadata == null || resources == null) {
- Log.i(TAG, "populateProviderCapabilities - metadata or resources is null");
- return;
- }
-
- String[] capabilities = resources.getStringArray(metadata.getInt(
- CredentialProviderService.CAPABILITY_META_DATA_KEY));
- if (capabilities == null || capabilities.length == 0) {
- Slog.i(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
- return;
+ Log.i(TAG, "populateMetadata - resources is null");
+ return builder;
}
- for (String capability : capabilities) {
- if (capability.isEmpty()) {
- Slog.i(TAG, "Skipping empty capability");
- continue;
- }
- Slog.i(TAG, "Capabilities found for provider: " + capability);
- mCapabilities.add(capability);
- }
+ builder.addCapabilities(populateProviderCapabilities(resources, metadata, serviceInfo));
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, e.getMessage());
+ }
+
+ return builder;
+ }
+
+ private static List<String> populateProviderCapabilities(
+ Resources resources, Bundle metadata, ServiceInfo serviceInfo) {
+ List<String> output = new ArrayList<>();
+ String[] capabilities = new String[0];
+
+ try {
+ capabilities =
+ resources.getStringArray(
+ metadata.getInt(CredentialProviderService.CAPABILITY_META_DATA_KEY));
} catch (Resources.NotFoundException e) {
- Slog.e(TAG, e.getMessage());
+ Slog.e(TAG, "Failed to get capabilities: " + e.getMessage());
+ }
+
+ if (capabilities == null || capabilities.length == 0) {
+ Slog.e(TAG, "No capabilities found for provider:" + serviceInfo.packageName);
+ return output;
}
+
+ for (String capability : capabilities) {
+ if (capability.isEmpty()) {
+ Slog.e(TAG, "Skipping empty capability");
+ continue;
+ }
+ Slog.e(TAG, "Capabilities found for provider: " + capability);
+ output.add(capability);
+ }
+ return output;
}
- private static ServiceInfo getServiceInfoOrThrow(@NonNull ComponentName serviceComponent,
- int userId) throws PackageManager.NameNotFoundException {
+ private static ServiceInfo getServiceInfoOrThrow(
+ @NonNull ComponentName serviceComponent, int userId)
+ throws PackageManager.NameNotFoundException {
try {
- ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
- serviceComponent,
- PackageManager.GET_META_DATA,
- userId);
+ ServiceInfo si =
+ AppGlobals.getPackageManager()
+ .getServiceInfo(serviceComponent, PackageManager.GET_META_DATA, userId);
if (si != null) {
return si;
}
@@ -256,6 +254,8 @@ public final class CredentialProviderInfo {
@NonNull Context context,
@UserIdInt int userId,
boolean disableSystemAppVerificationForTests) {
+ requireNonNull(context, "context must not be null");
+
final List<ServiceInfo> services = new ArrayList<>();
final List<ResolveInfo> resolveInfos = new ArrayList<>();
@@ -268,15 +268,20 @@ public final class CredentialProviderInfo {
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (disableSystemAppVerificationForTests) {
+ if (serviceInfo != null) {
+ services.add(serviceInfo);
+ }
+ continue;
+ }
+
try {
- PackageManager.ApplicationInfoFlags appInfoFlags =
- disableSystemAppVerificationForTests
- ? PackageManager.ApplicationInfoFlags.of(0)
- : PackageManager.ApplicationInfoFlags.of(
- PackageManager.MATCH_SYSTEM_ONLY);
ApplicationInfo appInfo =
context.getPackageManager()
- .getApplicationInfo(serviceInfo.packageName, appInfoFlags);
+ .getApplicationInfo(
+ serviceInfo.packageName,
+ PackageManager.ApplicationInfoFlags.of(
+ PackageManager.MATCH_SYSTEM_ONLY));
if (appInfo == null || serviceInfo == null) {
continue;
@@ -300,19 +305,22 @@ public final class CredentialProviderInfo {
public static List<CredentialProviderInfo> getAvailableSystemServices(
@NonNull Context context,
@UserIdInt int userId,
- boolean disableSystemAppVerificationForTests) {
+ boolean disableSystemAppVerificationForTests,
+ Set<ServiceInfo> enabledServices) {
requireNonNull(context, "context must not be null");
+
final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
for (ServiceInfo si :
getAvailableSystemServiceInfos(
context, userId, disableSystemAppVerificationForTests)) {
try {
CredentialProviderInfo cpi =
- new CredentialProviderInfo(
+ CredentialProviderInfoFactory.create(
context,
si,
/* isSystemProvider= */ true,
- disableSystemAppVerificationForTests);
+ disableSystemAppVerificationForTests,
+ enabledServices.contains(si));
if (cpi.isSystemProvider()) {
providerInfos.add(cpi);
} else {
@@ -325,45 +333,12 @@ public final class CredentialProviderInfo {
return providerInfos;
}
- /**
- * Returns true if the service supports the given {@code credentialType}, false otherwise.
- */
- @NonNull
- public boolean hasCapability(@NonNull String credentialType) {
- return mCapabilities.contains(credentialType);
- }
-
- /** Returns the service info. */
- @NonNull
- public ServiceInfo getServiceInfo() {
- return mServiceInfo;
- }
-
- public boolean isSystemProvider() {
- return mIsSystemProvider;
- }
-
- /** Returns the service icon. */
- @Nullable
- public Drawable getServiceIcon() {
- return mIcon;
- }
-
- /** Returns the service label. */
- @Nullable
- public CharSequence getServiceLabel() {
- return mLabel;
- }
-
- /** Returns an immutable list of capabilities this provider service can support. */
- @NonNull
- public List<String> getCapabilities() {
- return Collections.unmodifiableList(mCapabilities);
- }
+ private static @Nullable PackagePolicy getDeviceManagerPolicy(
+ @NonNull Context context, int userId) {
+ Context newContext = context.createContextAsUser(UserHandle.of(userId), 0);
- private static @Nullable PackagePolicy getDeviceManagerPolicy(@NonNull Context context) {
try {
- DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ DevicePolicyManager dpm = newContext.getSystemService(DevicePolicyManager.class);
return dpm.getCredentialManagerPolicy();
} catch (SecurityException e) {
// If the current user is not enrolled in DPM then this can throw a security error.
@@ -381,21 +356,53 @@ public final class CredentialProviderInfo {
public static List<CredentialProviderInfo> getCredentialProviderServices(
@NonNull Context context,
int userId,
- boolean disableSystemAppVerificationForTests,
- int providerFilter) {
+ int providerFilter,
+ Set<ServiceInfo> enabledServices) {
+ requireNonNull(context, "context must not be null");
+
+ // Get the device policy.
+ PackagePolicy pp = getDeviceManagerPolicy(context, userId);
+
+ // Generate the provider list.
+ final boolean disableSystemAppVerificationForTests = false;
+ ProviderGenerator generator =
+ new ProviderGenerator(
+ context, pp, disableSystemAppVerificationForTests, providerFilter);
+ generator.addUserProviders(
+ getUserProviders(
+ context, userId, disableSystemAppVerificationForTests, enabledServices));
+ generator.addSystemProviders(
+ getAvailableSystemServices(
+ context, userId, disableSystemAppVerificationForTests, enabledServices));
+ return generator.getProviders();
+ }
+
+ /**
+ * Returns the valid credential provider services available for the user with the given {@code
+ * userId}. Includes test providers.
+ */
+ @NonNull
+ public static List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+ @NonNull Context context,
+ int userId,
+ int providerFilter,
+ Set<ServiceInfo> enabledServices) {
requireNonNull(context, "context must not be null");
// Get the device policy.
- PackagePolicy pp = getDeviceManagerPolicy(context);
+ PackagePolicy pp = getDeviceManagerPolicy(context, userId);
// Generate the provider list.
+ final boolean disableSystemAppVerificationForTests = true;
ProviderGenerator generator =
new ProviderGenerator(
context, pp, disableSystemAppVerificationForTests, providerFilter);
generator.addUserProviders(
- getUserProviders(context, userId, disableSystemAppVerificationForTests));
+ getUserProviders(
+ context, userId, disableSystemAppVerificationForTests, enabledServices));
generator.addSystemProviders(
- getAvailableSystemServices(context, userId, disableSystemAppVerificationForTests));
+ getAvailableSystemServices(
+ context, userId, disableSystemAppVerificationForTests, enabledServices));
return generator.getProviders();
}
@@ -484,7 +491,8 @@ public final class CredentialProviderInfo {
private static List<CredentialProviderInfo> getUserProviders(
@NonNull Context context,
@UserIdInt int userId,
- boolean disableSystemAppVerificationForTests) {
+ boolean disableSystemAppVerificationForTests,
+ Set<ServiceInfo> enabledServices) {
final List<CredentialProviderInfo> services = new ArrayList<>();
final List<ResolveInfo> resolveInfos =
context.getPackageManager()
@@ -496,11 +504,12 @@ public final class CredentialProviderInfo {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
try {
CredentialProviderInfo cpi =
- new CredentialProviderInfo(
+ CredentialProviderInfoFactory.create(
context,
serviceInfo,
/* isSystemProvider= */ false,
- disableSystemAppVerificationForTests);
+ disableSystemAppVerificationForTests,
+ enabledServices.contains(serviceInfo));
if (!cpi.isSystemProvider()) {
services.add(cpi);
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 85aea85907b5..4a7ed644f9e2 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -32,9 +32,10 @@ import java.util.Map;
*
* Use {@link #obtain} to retrieve a new instance of the class when you are going
* to begin tracking. Put the motion events you receive into it with
- * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call
- * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
- * and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id.
+ * {@link #addMovement(MotionEvent)}. When you want to determine the velocity, call
+ * {@link #computeCurrentVelocity(int)} and then call the velocity-getter methods like
+ * {@link #getXVelocity(int)}, {@link #getYVelocity(int)}, or {@link #getAxisVelocity(int, int)}
+ * to retrieve velocity for different axes and/or pointer IDs.
*/
public final class VelocityTracker {
private static final SynchronizedPool<VelocityTracker> sPool =
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 4bc567abf27a..ad54004ec81c 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -20,20 +20,21 @@
#include <android_runtime/AndroidRuntime.h>
#include <input/InputTransport.h>
+#include <inttypes.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Looper.h>
+
+#include <optional>
+#include <unordered_map>
+
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
#include "core_jni_helpers.h"
-#include <inttypes.h>
-#include <unordered_map>
-
-
using android::base::Result;
namespace android {
@@ -67,7 +68,7 @@ private:
jobject mSenderWeakGlobal;
InputPublisher mInputPublisher;
sp<MessageQueue> mMessageQueue;
- std::unordered_map<uint32_t, uint32_t> mPublishedSeqMap;
+ std::unordered_map<uint32_t, std::optional<uint32_t>> mPublishedSeqMap;
uint32_t mNextPublishedSeq;
@@ -165,8 +166,14 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent
getInputChannelName().c_str(), status);
return status;
}
+ // mPublishedSeqMap tracks all sequences published from this sender. Only the last
+ // sequence number is used to signal this motion event is finished.
+ if (i == event->getHistorySize()) {
+ mPublishedSeqMap.emplace(publishedSeq, seq);
+ } else {
+ mPublishedSeqMap.emplace(publishedSeq, std::nullopt);
+ }
}
- mPublishedSeqMap.emplace(publishedSeq, seq);
return OK;
}
@@ -277,8 +284,16 @@ bool NativeInputEventSender::notifyConsumerResponse(
// does something wrong and sends bad data. Just ignore and process other events.
return true;
}
- const uint32_t seq = it->second;
+
+ const std::optional<uint32_t> seqOptional = it->second;
mPublishedSeqMap.erase(it);
+ // If this optional does not have a value, it means we are processing an event that had history
+ // and was split. There are more events coming, so we can't call 'dispatchInputEventFinished'
+ // yet. The final split event will have a valid sequence number.
+ if (!seqOptional.has_value()) {
+ return true;
+ }
+ const uint32_t seq = seqOptional.value();
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
diff --git a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
index 43334ab08b2f..e31d5aef9b69 100644
--- a/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
+++ b/core/tests/coretests/src/android/credentials/CredentialManagerTest.java
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.slice.Slice;
import android.content.Context;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -47,6 +48,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -59,6 +61,17 @@ public class CredentialManagerTest {
@Mock
private Activity mMockActivity;
+ private static final int TEST_USER_ID = 1;
+ private static final CredentialProviderInfo TEST_CREDENTIAL_PROVIDER_INFO =
+ new CredentialProviderInfo.Builder(new ServiceInfo())
+ .setSystemProvider(true)
+ .setOverrideLabel("test")
+ .addCapabilities(Arrays.asList("passkey"))
+ .setEnabled(true)
+ .build();
+ private static final List<CredentialProviderInfo> TEST_CREDENTIAL_PROVIDER_INFO_LIST =
+ Arrays.asList(TEST_CREDENTIAL_PROVIDER_INFO);
+
private GetCredentialRequest mGetRequest;
private CreateCredentialRequest mCreateRequest;
@@ -438,95 +451,53 @@ public class CredentialManagerTest {
}
@Test
- public void testListEnabledProviders_nullExecutor() {
- assertThrows(NullPointerException.class,
- () -> mCredentialManager.listEnabledProviders(null, null, result -> {
- }));
-
+ public void testGetCredentialProviderServices_allProviders() throws RemoteException {
+ verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
}
@Test
- public void testListEnabledProviders_nullCallback() {
- assertThrows(NullPointerException.class,
- () -> mCredentialManager.listEnabledProviders(null, mExecutor, null));
-
+ public void testGetCredentialProviderServices_userProviders() throws RemoteException {
+ verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
}
@Test
- public void testListEnabledProviders_alreadyCancelled() throws RemoteException {
- final CancellationSignal cancellation = new CancellationSignal();
- cancellation.cancel();
-
- mCredentialManager.listEnabledProviders(cancellation, mExecutor, result -> {
- });
-
- verify(mMockCredentialManagerService, never()).listEnabledProviders(any());
+ public void testGetCredentialProviderServices_systemProviders() throws RemoteException {
+ verifyGetCredentialProviderServices(CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY);
}
@Test
- public void testListEnabledProviders_cancel() throws RemoteException {
- final ICancellationSignal serviceSignal = mock(ICancellationSignal.class);
- final CancellationSignal cancellation = new CancellationSignal();
-
- OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
- mock(OutcomeReceiver.class);
-
- when(mMockCredentialManagerService.listEnabledProviders(any())).thenReturn(serviceSignal);
-
- mCredentialManager.listEnabledProviders(cancellation, mExecutor, callback);
-
- verify(mMockCredentialManagerService).listEnabledProviders(any());
-
- cancellation.cancel();
- verify(serviceSignal).cancel();
+ public void testGetCredentialProviderServicesForTesting_allProviders() throws RemoteException {
+ verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
}
@Test
- public void testListEnabledProviders_failed() throws RemoteException {
- ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
- IListEnabledProvidersCallback.class);
- ArgumentCaptor<ListEnabledProvidersException> errorCaptor = ArgumentCaptor.forClass(
- ListEnabledProvidersException.class);
-
- OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
- mock(OutcomeReceiver.class);
-
- when(mMockCredentialManagerService.listEnabledProviders(
- callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.listEnabledProviders(null, mExecutor, callback);
- verify(mMockCredentialManagerService).listEnabledProviders(any());
-
- final String errorType = "type";
- callbackCaptor.getValue().onError("type", "unknown error");
- verify(callback).onError(errorCaptor.capture());
-
- assertThat(errorCaptor.getValue().getType()).isEqualTo(errorType);
+ public void testGetCredentialProviderServicesForTesting_userProviders() throws RemoteException {
+ verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
}
@Test
- public void testListEnabledProviders_success() throws RemoteException {
- ListEnabledProvidersResponse response = ListEnabledProvidersResponse.create(
- List.of("foo", "bar", "baz"));
+ public void testGetCredentialProviderServicesForTesting_systemProviders() throws RemoteException {
+ verifyGetCredentialProviderServicesForTesting(CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY);
+ }
- OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException> callback =
- mock(OutcomeReceiver.class);
+ private void verifyGetCredentialProviderServices(int testFilter) throws RemoteException {
+ when(mMockCredentialManagerService.getCredentialProviderServices(
+ TEST_USER_ID, testFilter)).thenReturn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
- ArgumentCaptor<IListEnabledProvidersCallback> callbackCaptor = ArgumentCaptor.forClass(
- IListEnabledProvidersCallback.class);
- ArgumentCaptor<ListEnabledProvidersResponse> responseCaptor = ArgumentCaptor.forClass(
- ListEnabledProvidersResponse.class);
+ List<CredentialProviderInfo> output =
+ mCredentialManager.getCredentialProviderServices(TEST_USER_ID, testFilter);
- when(mMockCredentialManagerService.listEnabledProviders(
- callbackCaptor.capture())).thenReturn(mock(ICancellationSignal.class));
- mCredentialManager.listEnabledProviders(null, mExecutor, callback);
+ assertThat(output).containsExactlyElementsIn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
+ }
- verify(mMockCredentialManagerService).listEnabledProviders(any());
+ private void verifyGetCredentialProviderServicesForTesting(int testFilter) throws RemoteException {
+ when(mMockCredentialManagerService.getCredentialProviderServicesForTesting(
+ testFilter)).thenReturn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
- callbackCaptor.getValue().onResponse(response);
+ List<CredentialProviderInfo> output =
+ mCredentialManager.getCredentialProviderServicesForTesting(testFilter);
- verify(callback).onResult(responseCaptor.capture());
- assertThat(responseCaptor.getValue().getProviderComponentNames()).containsExactlyElementsIn(
- response.getProviderComponentNames());
+ assertThat(output).containsExactlyElementsIn(TEST_CREDENTIAL_PROVIDER_INFO_LIST);
}
@Test
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 70d3b3509b2f..146abea2bc31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -764,17 +764,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (pendingIntent2 == null) {
- // Launching a solo task.
- ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
- activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
- options1 = activityOptions.toBundle();
- addActivityOptions(options1, null /* launchTarget */);
- if (shortcutInfo1 != null) {
- wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
- } else {
- wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
- }
- mSyncQueue.queue(wct);
+ // Launching a solo intent or shortcut as fullscreen.
+ launchAsFullscreenWithRemoteAnimation(pendingIntent1, fillInIntent1, shortcutInfo1,
+ options1, adapter, wct);
return;
}
@@ -797,13 +789,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
- // Launching a solo task.
- ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
- activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
- options1 = activityOptions.toBundle();
- addActivityOptions(options1, null /* launchTarget */);
- wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
- mSyncQueue.queue(wct);
+ // Launching a solo intent as fullscreen.
+ launchAsFullscreenWithRemoteAnimation(pendingIntent, fillInIntent, null, options1,
+ adapter, wct);
return;
}
@@ -822,13 +810,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (options1 == null) options1 = new Bundle();
if (taskId == INVALID_TASK_ID) {
- // Launching a solo task.
- ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
- activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
- options1 = activityOptions.toBundle();
- addActivityOptions(options1, null /* launchTarget */);
- wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
- mSyncQueue.queue(wct);
+ // Launching a solo shortcut as fullscreen.
+ launchAsFullscreenWithRemoteAnimation(null, null, shortcutInfo, options1, adapter, wct);
return;
}
@@ -838,6 +821,49 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
instanceId);
}
+ private void launchAsFullscreenWithRemoteAnimation(@Nullable PendingIntent pendingIntent,
+ @Nullable Intent fillInIntent, @Nullable ShortcutInfo shortcutInfo,
+ @Nullable Bundle options, RemoteAnimationAdapter adapter,
+ WindowContainerTransaction wct) {
+ LegacyTransitions.ILegacyTransition transition =
+ (transit, apps, wallpapers, nonApps, finishedCallback, t) -> {
+ if (apps == null || apps.length == 0) {
+ onRemoteAnimationFinished(apps);
+ t.apply();
+ try {
+ adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ return;
+ }
+
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ }
+ }
+ t.apply();
+
+ try {
+ adapter.getRunner().onAnimationStart(
+ transit, apps, wallpapers, nonApps, finishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ };
+
+ addActivityOptions(options, null /* launchTarget */);
+ if (shortcutInfo != null) {
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, options);
+ } else if (pendingIntent != null) {
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options);
+ } else {
+ Slog.e(TAG, "Pending intent and shortcut are null is invalid case.");
+ }
+ mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ }
+
private void startWithLegacyTransition(WindowContainerTransaction wct,
@Nullable PendingIntent mainPendingIntent, @Nullable Intent mainFillInIntent,
@Nullable ShortcutInfo mainShortcutInfo, @Nullable Bundle mainOptions,
@@ -894,23 +920,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (options == null) options = new Bundle();
addActivityOptions(options, mMainStage);
- options = wrapAsSplitRemoteAnimation(adapter, options);
updateWindowBounds(mSplitLayout, wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
// TODO(b/268008375): Merge APIs to start a split pair into one.
if (mainTaskId != INVALID_TASK_ID) {
+ options = wrapAsSplitRemoteAnimation(adapter, options);
wct.startTask(mainTaskId, options);
- } else if (mainShortcutInfo != null) {
- wct.startShortcut(mContext.getPackageName(), mainShortcutInfo, options);
+ mSyncQueue.queue(wct);
} else {
- wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, options);
+ if (mainShortcutInfo != null) {
+ wct.startShortcut(mContext.getPackageName(), mainShortcutInfo, options);
+ } else {
+ wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, options);
+ }
+ mSyncQueue.queue(wrapAsSplitRemoteAnimation(adapter), WindowManager.TRANSIT_OPEN, wct);
}
- wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
-
- mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
});
@@ -967,6 +995,54 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return activityOptions.toBundle();
}
+ private LegacyTransitions.ILegacyTransition wrapAsSplitRemoteAnimation(
+ RemoteAnimationAdapter adapter) {
+ LegacyTransitions.ILegacyTransition transition =
+ (transit, apps, wallpapers, nonApps, finishedCallback, t) -> {
+ if (apps == null || apps.length == 0) {
+ onRemoteAnimationFinished(apps);
+ t.apply();
+ try {
+ adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ return;
+ }
+
+ // Wrap the divider bar into non-apps target to animate together.
+ nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+ getDividerBarLegacyTarget());
+
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ // Reset the surface position of the opening app to prevent offset.
+ t.setPosition(apps[i].leash, 0, 0);
+ }
+ }
+ t.apply();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ onRemoteAnimationFinished(apps);
+ finishedCallback.onAnimationFinished();
+ }
+ };
+ Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
+ try {
+ adapter.getRunner().onAnimationStart(
+ transit, apps, wallpapers, nonApps, wrapCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ };
+
+ return transition;
+ }
+
private void setEnterInstanceId(InstanceId instanceId) {
if (instanceId != null) {
mLogger.enterRequested(instanceId, ENTER_REASON_LAUNCHER);
@@ -993,6 +1069,27 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
+ mIsDividerRemoteAnimating = false;
+ mShouldUpdateRecents = true;
+ mSplitRequest = null;
+ // If any stage has no child after finished animation, that side of the split will display
+ // nothing. This might happen if starting the same app on the both sides while not
+ // supporting multi-instance. Exit the split screen and expand that app to full screen.
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ mSplitUnsupportedToast.show();
+ return;
+ }
+
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
+ prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
+ mSyncQueue.queue(evictWct);
+ }
+
+
/**
* Collects all the current child tasks of a specific split and prepares transaction to evict
* them to display.
diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
index 5ecec4ddd1ad..3125f088c72b 100644
--- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.java
@@ -72,6 +72,7 @@ public final class LowLightDreamManager {
public static final int AMBIENT_LIGHT_MODE_LOW_LIGHT = 2;
private final DreamManager mDreamManager;
+ private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
@Nullable
private final ComponentName mLowLightDreamComponent;
@@ -81,8 +82,10 @@ public final class LowLightDreamManager {
@Inject
public LowLightDreamManager(
DreamManager dreamManager,
+ LowLightTransitionCoordinator lowLightTransitionCoordinator,
@Named(LOW_LIGHT_DREAM_COMPONENT) @Nullable ComponentName lowLightDreamComponent) {
mDreamManager = dreamManager;
+ mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
mLowLightDreamComponent = lowLightDreamComponent;
}
@@ -111,7 +114,9 @@ public final class LowLightDreamManager {
mAmbientLightMode = ambientLightMode;
- mDreamManager.setSystemDreamComponent(mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT
- ? mLowLightDreamComponent : null);
+ boolean shouldEnterLowLight = mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT;
+ mLowLightTransitionCoordinator.notifyBeforeLowLightTransition(shouldEnterLowLight,
+ () -> mDreamManager.setSystemDreamComponent(
+ shouldEnterLowLight ? mLowLightDreamComponent : null));
}
}
diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java
new file mode 100644
index 000000000000..874a2d5af75e
--- /dev/null
+++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dream.lowlight;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Helper class that allows listening and running animations before entering or exiting low light.
+ */
+@Singleton
+public class LowLightTransitionCoordinator {
+ /**
+ * Listener that is notified before low light entry.
+ */
+ public interface LowLightEnterListener {
+ /**
+ * Callback that is notified before the device enters low light.
+ *
+ * @return an optional animator that will be waited upon before entering low light.
+ */
+ Animator onBeforeEnterLowLight();
+ }
+
+ /**
+ * Listener that is notified before low light exit.
+ */
+ public interface LowLightExitListener {
+ /**
+ * Callback that is notified before the device exits low light.
+ *
+ * @return an optional animator that will be waited upon before exiting low light.
+ */
+ Animator onBeforeExitLowLight();
+ }
+
+ private LowLightEnterListener mLowLightEnterListener;
+ private LowLightExitListener mLowLightExitListener;
+
+ @Inject
+ public LowLightTransitionCoordinator() {
+ }
+
+ /**
+ * Sets the listener for the low light enter event.
+ *
+ * Only one listener can be set at a time. This method will overwrite any previously set
+ * listener. Null can be used to unset the listener.
+ */
+ public void setLowLightEnterListener(@Nullable LowLightEnterListener lowLightEnterListener) {
+ mLowLightEnterListener = lowLightEnterListener;
+ }
+
+ /**
+ * Sets the listener for the low light exit event.
+ *
+ * Only one listener can be set at a time. This method will overwrite any previously set
+ * listener. Null can be used to unset the listener.
+ */
+ public void setLowLightExitListener(@Nullable LowLightExitListener lowLightExitListener) {
+ mLowLightExitListener = lowLightExitListener;
+ }
+
+ /**
+ * Notifies listeners that the device is about to enter or exit low light.
+ *
+ * @param entering true if listeners should be notified before entering low light, false if this
+ * is notifying before exiting.
+ * @param callback callback that will be run after listeners complete.
+ */
+ void notifyBeforeLowLightTransition(boolean entering, Runnable callback) {
+ Animator animator = null;
+
+ if (entering && mLowLightEnterListener != null) {
+ animator = mLowLightEnterListener.onBeforeEnterLowLight();
+ } else if (!entering && mLowLightExitListener != null) {
+ animator = mLowLightExitListener.onBeforeExitLowLight();
+ }
+
+ // If the listener returned an animator to indicate it was running an animation, run the
+ // callback after the animation completes, otherwise call the callback directly.
+ if (animator != null) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ callback.run();
+ }
+ });
+ } else {
+ callback.run();
+ }
+ }
+}
diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
index 91a170f7ae14..4b95d8c84bac 100644
--- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
+++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.java
@@ -21,7 +21,10 @@ import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE
import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -44,44 +47,52 @@ public class LowLightDreamManagerTest {
private DreamManager mDreamManager;
@Mock
+ private LowLightTransitionCoordinator mTransitionCoordinator;
+
+ @Mock
private ComponentName mDreamComponent;
+ LowLightDreamManager mLowLightDreamManager;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ // Automatically run any provided Runnable to mTransitionCoordinator to simplify testing.
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(1)).run();
+ return null;
+ }).when(mTransitionCoordinator).notifyBeforeLowLightTransition(anyBoolean(),
+ any(Runnable.class));
+
+ mLowLightDreamManager = new LowLightDreamManager(mDreamManager, mTransitionCoordinator,
+ mDreamComponent);
}
@Test
public void setAmbientLightMode_lowLight_setSystemDream() {
- final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
- mDreamComponent);
-
- lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+ mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+ verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(true), any());
verify(mDreamManager).setSystemDreamComponent(mDreamComponent);
}
@Test
public void setAmbientLightMode_regularLight_clearSystemDream() {
- final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
- mDreamComponent);
-
- lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
+ mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
+ verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(false), any());
verify(mDreamManager).setSystemDreamComponent(null);
}
@Test
public void setAmbientLightMode_defaultUnknownMode_clearSystemDream() {
- final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
- mDreamComponent);
-
// Set to low light first.
- lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
+ mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
clearInvocations(mDreamManager);
// Return to default unknown mode.
- lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
+ mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
verify(mDreamManager).setSystemDreamComponent(null);
}
@@ -89,7 +100,7 @@ public class LowLightDreamManagerTest {
@Test
public void setAmbientLightMode_dreamComponentNotSet_doNothing() {
final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
- null /*dream component*/);
+ mTransitionCoordinator, null /*dream component*/);
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java
new file mode 100644
index 000000000000..81e1e33d6220
--- /dev/null
+++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.dream.lowlight;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.animation.Animator;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class LowLightTransitionCoordinatorTest {
+ @Mock
+ private LowLightTransitionCoordinator.LowLightEnterListener mEnterListener;
+
+ @Mock
+ private LowLightTransitionCoordinator.LowLightExitListener mExitListener;
+
+ @Mock
+ private Animator mAnimator;
+
+ @Captor
+ private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor;
+
+ @Mock
+ private Runnable mRunnable;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void onEnterCalledOnListeners() {
+ LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+ coordinator.setLowLightEnterListener(mEnterListener);
+
+ coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+ verify(mEnterListener).onBeforeEnterLowLight();
+ verify(mRunnable).run();
+ }
+
+ @Test
+ public void onExitCalledOnListeners() {
+ LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+ coordinator.setLowLightExitListener(mExitListener);
+
+ coordinator.notifyBeforeLowLightTransition(false, mRunnable);
+
+ verify(mExitListener).onBeforeExitLowLight();
+ verify(mRunnable).run();
+ }
+
+ @Test
+ public void listenerNotCalledAfterRemoval() {
+ LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+
+ coordinator.setLowLightEnterListener(mEnterListener);
+ coordinator.setLowLightEnterListener(null);
+
+ coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+ verifyZeroInteractions(mEnterListener);
+ verify(mRunnable).run();
+ }
+
+ @Test
+ public void runnableCalledAfterAnimationEnds() {
+ when(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator);
+
+ LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
+ coordinator.setLowLightEnterListener(mEnterListener);
+
+ coordinator.notifyBeforeLowLightTransition(true, mRunnable);
+
+ // Animator listener is added and the runnable is not run yet.
+ verify(mAnimator).addListener(mAnimatorListenerCaptor.capture());
+ verifyZeroInteractions(mRunnable);
+
+ // Runnable is run once the animation ends.
+ mAnimatorListenerCaptor.getValue().onAnimationEnd(null);
+ verify(mRunnable).run();
+ }
+}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index f85bdee18967..5f5e214357ea 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -252,10 +252,10 @@ public class AudioMix {
if (o == null || getClass() != o.getClass()) return false;
final AudioMix that = (AudioMix) o;
- return (this.mRouteFlags == that.mRouteFlags)
- && (this.mRule == that.mRule)
- && (this.mMixType == that.mMixType)
- && (this.mFormat == that.mFormat);
+ return (mRouteFlags == that.mRouteFlags)
+ && (mMixType == that.mMixType)
+ && Objects.equals(mRule, that.mRule)
+ && Objects.equals(mFormat, that.mFormat);
}
/** @hide */
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 440447e5ec1d..ce9773312a10 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -24,6 +24,7 @@ import android.os.Parcelable;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Objects;
@@ -50,7 +51,8 @@ public class AudioPolicyConfig implements Parcelable {
mMixes = conf.mMixes;
}
- AudioPolicyConfig(ArrayList<AudioMix> mixes) {
+ @VisibleForTesting
+ public AudioPolicyConfig(ArrayList<AudioMix> mixes) {
mMixes = mixes;
}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index d70e8b36afdb..178a6d97dff8 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -191,20 +191,13 @@ public final class MediaProjection {
} else {
session = ContentRecordingSession.createTaskSession(launchCookie);
}
+ // Pass in the current session details, so they are guaranteed to only be set in WMS
+ // AFTER a VirtualDisplay is constructed (assuming there are no errors during set-up).
+ virtualDisplayConfig.setContentRecordingSession(session);
virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
virtualDisplayConfig.build(), callback, handler, windowContext);
- if (virtualDisplay == null) {
- // Since WM handling a new display and DM creating a new VirtualDisplay is async,
- // WM may have tried to start task recording and encountered an error that required
- // stopping recording entirely. The VirtualDisplay would then be null when the
- // MediaProjection is no longer active.
- return null;
- }
- session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
- // Successfully set up, so save the current session details.
- getProjectionService().setContentRecordingSession(session, mImpl);
return virtualDisplay;
} catch (RemoteException e) {
// Can not capture if WMS is not accessible, so bail out.
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
index 63292ce766f8..4624dfe70756 100644
--- a/media/tests/AudioPolicyTest/Android.bp
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -14,6 +14,7 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.rules",
"guava",
+ "guava-android-testlib",
"hamcrest-library",
"platform-test-annotations",
],
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
new file mode 100644
index 000000000000..bbca8823dde4
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioMixUnitTests.java
@@ -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.
+ */
+
+package com.android.audiopolicytest;
+
+import static android.media.AudioFormat.CHANNEL_OUT_MONO;
+import static android.media.AudioFormat.CHANNEL_OUT_STEREO;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_INJECTOR;
+import static android.media.audiopolicy.AudioMixingRule.MIX_ROLE_PLAYERS;
+import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_AUDIO_SESSION_ID;
+import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_UID;
+
+import android.media.AudioFormat;
+import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioMixingRule;
+import android.media.audiopolicy.AudioPolicyConfig;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for AudioMix.
+ *
+ * Run with "atest AudioMixUnitTests".
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AudioMixUnitTests {
+ private static final AudioFormat OUTPUT_FORMAT_STEREO_44KHZ_PCM =
+ new AudioFormat.Builder()
+ .setSampleRate(44000)
+ .setChannelMask(CHANNEL_OUT_STEREO)
+ .setEncoding(ENCODING_PCM_16BIT).build();
+ private static final AudioFormat OUTPUT_FORMAT_MONO_16KHZ_PCM =
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setChannelMask(CHANNEL_OUT_MONO)
+ .setEncoding(ENCODING_PCM_16BIT).build();
+ private static final AudioFormat INPUT_FORMAT_MONO_16KHZ_PCM =
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .setEncoding(ENCODING_PCM_16BIT).build();
+
+ @Test
+ public void testEquals() {
+ final EqualsTester equalsTester = new EqualsTester();
+
+ // --- Equality group 1
+ final AudioMix playbackAudioMixWithSessionId42AndUid123 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
+ .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+ .addMixRule(RULE_MATCH_UID, 123).build())
+ .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ final AudioMix playbackAudioMixWithUid123AndSessionId42 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
+ .addMixRule(RULE_MATCH_UID, 123)
+ .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42).build())
+ .setFormat(OUTPUT_FORMAT_STEREO_44KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ equalsTester.addEqualityGroup(
+ playbackAudioMixWithSessionId42AndUid123,
+ playbackAudioMixWithUid123AndSessionId42,
+ writeToAndFromParcel(playbackAudioMixWithSessionId42AndUid123),
+ writeToAndFromParcel(playbackAudioMixWithUid123AndSessionId42));
+
+ // --- Equality group 2
+ final AudioMix recordingAudioMixWithSessionId42AndUid123 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+ .addMixRule(RULE_MATCH_UID, 123).build())
+ .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ final AudioMix recordingAudioMixWithUid123AndSessionId42 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+ .addMixRule(RULE_MATCH_UID, 123).build())
+ .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123,
+ recordingAudioMixWithUid123AndSessionId42,
+ writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123),
+ writeToAndFromParcel(recordingAudioMixWithUid123AndSessionId42));
+
+ // --- Equality group 3
+ final AudioMix recordingAudioMixWithSessionId42AndUid123Render =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_INJECTOR)
+ .addMixRule(RULE_MATCH_AUDIO_SESSION_ID, 42)
+ .addMixRule(RULE_MATCH_UID, 123).build())
+ .setFormat(INPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(
+ AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER).build();
+ equalsTester.addEqualityGroup(recordingAudioMixWithSessionId42AndUid123Render,
+ writeToAndFromParcel(recordingAudioMixWithSessionId42AndUid123Render));
+
+ // --- Equality group 4
+ final AudioMix playbackAudioMixWithUid123 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
+ .addMixRule(RULE_MATCH_UID, 123).build())
+ .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ equalsTester.addEqualityGroup(playbackAudioMixWithUid123,
+ writeToAndFromParcel(playbackAudioMixWithUid123));
+
+ // --- Equality group 5
+ final AudioMix playbackAudioMixWithUid42 =
+ new AudioMix.Builder(new AudioMixingRule.Builder()
+ .setTargetMixRole(MIX_ROLE_PLAYERS)
+ .addMixRule(RULE_MATCH_UID, 42).build())
+ .setFormat(OUTPUT_FORMAT_MONO_16KHZ_PCM)
+ .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK).build();
+ equalsTester.addEqualityGroup(playbackAudioMixWithUid42,
+ writeToAndFromParcel(playbackAudioMixWithUid42));
+
+ equalsTester.testEquals();
+ }
+
+ private static AudioMix writeToAndFromParcel(AudioMix audioMix) {
+ AudioPolicyConfig apc = new AudioPolicyConfig(new ArrayList<>(List.of(audioMix)));
+ Parcel parcel = Parcel.obtain();
+ apc.writeToParcel(parcel, /*flags=*/0);
+ parcel.setDataPosition(0);
+ AudioMix unmarshalledMix =
+ AudioPolicyConfig.CREATOR.createFromParcel(parcel).getMixes().get(0);
+ parcel.recycle();
+ return unmarshalledMix;
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 418011acf6f3..27c8cdfe98f3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3056,11 +3056,11 @@ public class SettingsProvider extends ContentProvider {
final int key = makeKey(type, userId);
boolean success = false;
- boolean isNewSetting = false;
+ boolean wasUnsetNonPredefinedSetting = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
- if (!settingsState.hasSetting(name)) {
- isNewSetting = true;
+ if (!isSettingPreDefined(name, type) && !settingsState.hasSetting(name)) {
+ wasUnsetNonPredefinedSetting = true;
}
success = settingsState.insertSettingLocked(name, value,
tag, makeDefault, forceNonSystemPackage, packageName,
@@ -3073,9 +3073,9 @@ public class SettingsProvider extends ContentProvider {
if (forceNotify || success) {
notifyForSettingsChange(key, name);
- if (isNewSetting && !isSettingPreDefined(name, type)) {
- // Increment the generation number for all null settings because a new
- // non-predefined setting has been inserted
+ if (wasUnsetNonPredefinedSetting) {
+ // Increment the generation number for all non-predefined, unset settings,
+ // because a new non-predefined setting has been inserted
mGenerationRegistry.incrementGenerationForUnsetSettings(key);
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 17a94b8639d0..296c2ae5cf99 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -419,7 +419,7 @@ class ActivityLaunchAnimator(
internal val delegate: AnimationDelegate
init {
- delegate = AnimationDelegate(controller, callback, launchAnimator, listener)
+ delegate = AnimationDelegate(controller, callback, listener, launchAnimator)
}
@BinderThread
@@ -446,10 +446,10 @@ class ActivityLaunchAnimator(
constructor(
private val controller: Controller,
private val callback: Callback,
- /** The animator to use to animate the window launch. */
- private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR,
/** Listener for animation lifecycle events. */
- private val listener: Listener? = null
+ private val listener: Listener? = null,
+ /** The animator to use to animate the window launch. */
+ private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR
) : RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> {
private val launchContainer = controller.launchContainer
private val context = launchContainer.context
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e65c327736e1..8f90724c09b9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -782,7 +782,7 @@
<!-- Duration in milliseconds of the dream in complications fade-in animation. -->
<integer name="config_dreamOverlayInComplicationsDurationMs">250</integer>
<!-- Duration in milliseconds of the y-translation animation when entering a dream -->
- <integer name="config_dreamOverlayInTranslationYDurationMs">917</integer>
+ <integer name="config_dreamOverlayInTranslationYDurationMs">1167</integer>
<!-- Delay in milliseconds before switching to the dock user and dreaming if a secondary user is
active when the device is locked and docked. 0 indicates disabled. Default is 1 minute. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aba3fc4615c9..0f2ce444f225 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -196,9 +196,6 @@
<!-- Increased height of a small notification in the status bar -->
<dimen name="notification_min_height_increased">146dp</dimen>
- <!-- Increased height of a collapsed media notification in the status bar -->
- <dimen name="notification_min_height_media">160dp</dimen>
-
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 2501be93d189..e049ae09b1de 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -351,9 +351,20 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
}
private void animateFromMinimized() {
- mIsMinimized = false;
- setExpandedView();
- animateIn();
+ if (mEnterAnimator != null && mEnterAnimator.isRunning()) {
+ mEnterAnimator.cancel();
+ }
+ mEnterAnimator = mView.getMinimizedFadeoutAnimation();
+ mEnterAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mIsMinimized = false;
+ setExpandedView();
+ animateIn();
+ }
+ });
+ mEnterAnimator.start();
}
private String getAccessibilityAnnouncement(ClipboardModel.Type type) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index f372bb4bc7f2..28c57d31a4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -21,6 +21,7 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
@@ -286,6 +287,20 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mActionChips.clear();
}
+ Animator getMinimizedFadeoutAnimation() {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mMinimizedPreview, "alpha", 1, 0);
+ anim.setDuration(66);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mMinimizedPreview.setVisibility(View.GONE);
+ mMinimizedPreview.setAlpha(1);
+ }
+ });
+ return anim;
+ }
+
Animator getEnterAnimation() {
if (mAccessibilityManager.isEnabled()) {
mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index ca1cef385755..d0a92f0846d0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -43,7 +43,6 @@ import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
@@ -131,9 +130,17 @@ constructor(
}
}
- /** Starts the dream content and dream overlay entry animations. */
+ /**
+ * Starts the dream content and dream overlay entry animations.
+ *
+ * @param downwards if true, the entry animation translations downwards into position rather
+ * than upwards.
+ */
@JvmOverloads
- fun startEntryAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
+ fun startEntryAnimations(
+ downwards: Boolean,
+ animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
+ ) {
cancelAnimations()
mAnimator =
@@ -153,7 +160,7 @@ constructor(
interpolator = Interpolators.LINEAR
),
translationYAnimator(
- from = mDreamInTranslationYDistance.toFloat(),
+ from = mDreamInTranslationYDistance.toFloat() * (if (downwards) -1 else 1),
to = 0f,
durationMs = mDreamInTranslationYDurationMs,
interpolator = Interpolators.EMPHASIZED_DECELERATE
@@ -167,6 +174,71 @@ constructor(
}
}
+ /**
+ * Starts the dream content and dream overlay exit animations.
+ *
+ * This should only be used when the low light dream is entering, animations to/from other SysUI
+ * views is controlled by `transitionViewModel`.
+ */
+ // TODO(b/256916668): integrate with the keyguard transition model once dream surfaces work is
+ // done.
+ @JvmOverloads
+ fun startExitAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }): Animator {
+ cancelAnimations()
+
+ mAnimator =
+ animatorBuilder().apply {
+ playTogether(
+ translationYAnimator(
+ from = 0f,
+ to = -mDreamInTranslationYDistance.toFloat(),
+ durationMs = mDreamInTranslationYDurationMs,
+ delayMs = 0,
+ interpolator = Interpolators.EMPHASIZED
+ ),
+ alphaAnimator(
+ from =
+ mCurrentAlphaAtPosition.getOrDefault(
+ key = POSITION_BOTTOM,
+ defaultValue = 1f
+ ),
+ to = 0f,
+ durationMs = mDreamInComplicationsAnimDurationMs,
+ delayMs = 0,
+ positions = POSITION_BOTTOM
+ )
+ .apply {
+ doOnEnd {
+ // The logical end of the animation is once the alpha and blur
+ // animations finish, end the animation so that any listeners are
+ // notified. The Y translation animation is much longer than all of
+ // the other animations due to how the spec is defined, but is not
+ // expected to run to completion.
+ mAnimator?.end()
+ }
+ },
+ alphaAnimator(
+ from =
+ mCurrentAlphaAtPosition.getOrDefault(
+ key = POSITION_TOP,
+ defaultValue = 1f
+ ),
+ to = 0f,
+ durationMs = mDreamInComplicationsAnimDurationMs,
+ delayMs = 0,
+ positions = POSITION_TOP
+ )
+ )
+ doOnEnd {
+ mAnimator = null
+ mOverlayStateController.setExitAnimationsRunning(false)
+ }
+ start()
+ }
+ mOverlayStateController.setExitAnimationsRunning(true)
+ return mAnimator as AnimatorSet
+ }
+
/** Starts the dream content and dream overlay exit animations. */
fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
cancelAnimations()
@@ -182,19 +254,6 @@ constructor(
}
}
- /**
- * Ends the dream content and dream overlay animations, if they're currently running.
- *
- * @see [AnimatorSet.end]
- */
- fun endAnimations() {
- mAnimator =
- mAnimator?.let {
- it.end()
- null
- }
- }
-
private fun blurAnimator(
view: View,
fromBlurRadius: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 50cfb6a905c9..4b478cdca9f9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_BOTTOM;
import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP;
+import android.animation.Animator;
import android.content.res.Resources;
import android.os.Handler;
import android.util.MathUtils;
@@ -31,6 +32,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
@@ -54,11 +56,14 @@ import javax.inject.Named;
* View controller for {@link DreamOverlayContainerView}.
*/
@DreamOverlayComponent.DreamOverlayScope
-public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
+public class DreamOverlayContainerViewController extends
+ ViewController<DreamOverlayContainerView> implements
+ LowLightTransitionCoordinator.LowLightEnterListener {
private final DreamOverlayStatusBarViewController mStatusBarViewController;
private final BlurUtils mBlurUtils;
private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
private final DreamOverlayStateController mStateController;
+ private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
private final ComplicationHostViewController mComplicationHostViewController;
@@ -143,19 +148,18 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
};
/**
- * If true, overlay entry animations should be skipped once.
- *
- * This is turned on when exiting low light and should be turned off once the entry animations
- * are skipped once.
+ * If {@code true}, the dream has just transitioned from the low light dream back to the user
+ * dream and we should play an entry animation where the overlay slides in downwards from the
+ * top instead of the typicla slide in upwards from the bottom.
*/
- private boolean mSkipEntryAnimations;
+ private boolean mExitingLowLight;
private final DreamOverlayStateController.Callback
mDreamOverlayStateCallback =
new DreamOverlayStateController.Callback() {
@Override
public void onExitLowLight() {
- mSkipEntryAnimations = true;
+ mExitingLowLight = true;
}
};
@@ -165,6 +169,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
DreamOverlayStatusBarViewController statusBarViewController,
+ LowLightTransitionCoordinator lowLightTransitionCoordinator,
BlurUtils blurUtils,
@Main Handler handler,
@Main Resources resources,
@@ -182,6 +187,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mBlurUtils = blurUtils;
mDreamOverlayAnimationsController = animationsController;
mStateController = stateController;
+ mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
mBouncerlessScrimController = bouncerlessScrimController;
mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
@@ -208,6 +214,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mStatusBarViewController.init();
mComplicationHostViewController.init();
mDreamOverlayAnimationsController.init(mView);
+ mLowLightTransitionCoordinator.setLowLightEnterListener(this);
}
@Override
@@ -219,14 +226,10 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
// Start dream entry animations. Skip animations for low light clock.
if (!mStateController.isLowLightActive()) {
- mDreamOverlayAnimationsController.startEntryAnimations();
-
- if (mSkipEntryAnimations) {
- // If we're transitioning from the low light dream back to the user dream, skip the
- // overlay animations and show immediately.
- mDreamOverlayAnimationsController.endAnimations();
- mSkipEntryAnimations = false;
- }
+ // If this is transitioning from the low light dream to the user dream, the overlay
+ // should translate in downwards instead of upwards.
+ mDreamOverlayAnimationsController.startEntryAnimations(mExitingLowLight);
+ mExitingLowLight = false;
}
}
@@ -310,4 +313,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
}
+
+ @Override
+ public Animator onBeforeEnterLowLight() {
+ // Return the animator so that the transition coordinator waits for the overlay exit
+ // animations to finish before entering low light, as otherwise the default DreamActivity
+ // animation plays immediately and there's no time for this animation to play.
+ return mDreamOverlayAnimationsController.startExitAnimations();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index a2e11b21ea59..24e90f066622 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -22,14 +22,18 @@ import static com.android.systemui.dreams.complication.dagger.ComplicationModule
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Debug;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.LifecycleOwner;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.Collection;
import java.util.HashMap;
@@ -54,6 +58,8 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
private final LifecycleOwner mLifecycleOwner;
private final ComplicationCollectionViewModel mComplicationCollectionViewModel;
private final HashMap<ComplicationId, Complication.ViewHolder> mComplications = new HashMap<>();
+ @VisibleForTesting
+ boolean mIsAnimationEnabled;
// Whether dream entry animations are finished.
private boolean mEntryAnimationsFinished = false;
@@ -64,7 +70,8 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
ComplicationLayoutEngine layoutEngine,
DreamOverlayStateController dreamOverlayStateController,
LifecycleOwner lifecycleOwner,
- @Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel) {
+ @Named(SCOPED_COMPLICATIONS_MODEL) ComplicationCollectionViewModel viewModel,
+ SecureSettings secureSettings) {
super(view);
mLayoutEngine = layoutEngine;
mLifecycleOwner = lifecycleOwner;
@@ -78,6 +85,10 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
mDreamOverlayStateController.areEntryAnimationsFinished();
}
});
+
+ // Whether animations are enabled.
+ mIsAnimationEnabled = secureSettings.getFloatForUser(
+ Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
}
@Override
@@ -148,7 +159,7 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
// Complications to be added before dream entry animations are finished are set
// to invisible and are animated in.
- if (!mEntryAnimationsFinished) {
+ if (!mEntryAnimationsFinished && mIsAnimationEnabled) {
view.setVisibility(View.INVISIBLE);
}
mComplications.put(id, viewHolder);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
index ead3b7b1de53..0b4b7c691cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DraggableConstraintLayout.java
@@ -45,6 +45,7 @@ public class DraggableConstraintLayout extends ConstraintLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
private static final float VELOCITY_DP_PER_MS = 1;
+ private static final int MAXIMUM_DISMISS_DISTANCE_DP = 400;
private final SwipeDismissHandler mSwipeDismissHandler;
private final GestureDetector mSwipeDetector;
@@ -347,14 +348,18 @@ public class DraggableConstraintLayout extends ConstraintLayout
} else {
finalX = -1 * getBackgroundRight();
}
- float distance = Math.abs(finalX - startX);
+ float distance = Math.min(Math.abs(finalX - startX),
+ FloatingWindowUtil.dpToPx(mDisplayMetrics, MAXIMUM_DISMISS_DISTANCE_DP));
+ // ensure that view dismisses in the right direction (right in LTR, left in RTL)
+ float distanceVector = Math.copySign(distance, finalX - startX);
anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
+ float translation = MathUtils.lerp(
+ startX, startX + distanceVector, animation.getAnimatedFraction());
mView.setTranslationX(translation);
mView.setAlpha(1 - animation.getAnimatedFraction());
});
- anim.setDuration((long) (distance / Math.abs(velocity)));
+ anim.setDuration((long) (Math.abs(distance / velocity)));
return anim;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index b1987c151e5f..ee9e54a22ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -4720,8 +4720,13 @@ public final class NotificationPanelViewController implements Dumpable {
gesture possible. */
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
+ if (mTrackingPointer < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ } else {
+ mShadeLog.logMotionEvent(event, "Skipping intercept of multitouch pointer");
+ return false;
+ }
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index c35c5c522798..77550038c94a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -93,6 +93,16 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
@IntDef({STATE_ICON, STATE_DOT, STATE_HIDDEN})
public @interface VisibleState { }
+ /** Returns a human-readable string of {@link VisibleState}. */
+ public static String getVisibleStateString(@VisibleState int state) {
+ switch(state) {
+ case STATE_ICON: return "ICON";
+ case STATE_DOT: return "DOT";
+ case STATE_HIDDEN: return "HIDDEN";
+ default: return "UNKNOWN";
+ }
+ }
+
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
= new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -561,7 +571,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
@Override
public String toString() {
return "StatusBarIconView("
- + "slot='" + mSlot + " alpha=" + getAlpha() + " icon=" + mIcon
+ + "slot='" + mSlot + "' alpha=" + getAlpha() + " icon=" + mIcon
+ + " visibleState=" + getVisibleStateString(getVisibleState())
+ " iconColor=#" + Integer.toHexString(mIconColor)
+ " notification=" + mNotification + ')';
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
new file mode 100644
index 000000000000..5ce1db2b6acd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArrayMap
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
+
+/** A small coordinator which finds, stores, and applies the closest notification time. */
+@CoordinatorScope
+class GroupWhenCoordinator
+@Inject
+constructor(
+ @Main private val delayableExecutor: DelayableExecutor,
+ private val systemClock: SystemClock
+) : Coordinator {
+
+ private val invalidator = object : Invalidator("GroupWhenCoordinator") {}
+ private val notificationGroupTimes = ArrayMap<GroupEntry, Long>()
+ private var cancelInvalidateListRunnable: Runnable? = null
+
+ private val invalidateListRunnable: Runnable = Runnable {
+ invalidator.invalidateList("future notification invalidation")
+ }
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener)
+ pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroupListener)
+ pipeline.addPreRenderInvalidator(invalidator)
+ }
+
+ private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ cancelListInvalidation()
+ notificationGroupTimes.clear()
+
+ val now = systemClock.currentTimeMillis()
+ var closestFutureTime = Long.MAX_VALUE
+ entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
+ val whenMillis = calculateGroupNotificationTime(groupEntry, now)
+ notificationGroupTimes[groupEntry] = whenMillis
+ if (whenMillis > now) {
+ closestFutureTime = min(closestFutureTime, whenMillis)
+ }
+ }
+
+ if (closestFutureTime != Long.MAX_VALUE) {
+ cancelInvalidateListRunnable =
+ delayableExecutor.executeDelayed(invalidateListRunnable, closestFutureTime - now)
+ }
+ }
+
+ private fun cancelListInvalidation() {
+ cancelInvalidateListRunnable?.run()
+ cancelInvalidateListRunnable = null
+ }
+
+ private fun onAfterRenderGroupListener(group: GroupEntry, controller: NotifGroupController) {
+ notificationGroupTimes[group]?.let(controller::setNotificationGroupWhen)
+ }
+
+ private fun calculateGroupNotificationTime(
+ groupEntry: GroupEntry,
+ currentTimeMillis: Long
+ ): Long {
+ var pastTime = Long.MIN_VALUE
+ var futureTime = Long.MAX_VALUE
+ groupEntry.children
+ .asSequence()
+ .mapNotNull { child -> child.sbn.notification.`when`.takeIf { it > 0 } }
+ .forEach { time ->
+ val isInThePast = currentTimeMillis - time > 0
+ if (isInThePast) {
+ pastTime = max(pastTime, time)
+ } else {
+ futureTime = min(futureTime, time)
+ }
+ }
+
+ if (pastTime == Long.MIN_VALUE && futureTime == Long.MAX_VALUE) {
+ return checkNotNull(groupEntry.summary).creationTime
+ }
+
+ return if (futureTime != Long.MAX_VALUE) futureTime else pastTime
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 8a82bcad44e4..6bb5b9218ed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -31,31 +31,32 @@ interface NotifCoordinators : Coordinator, PipelineDumpable
@CoordinatorScope
class NotifCoordinatorsImpl @Inject constructor(
- notifPipelineFlags: NotifPipelineFlags,
- dataStoreCoordinator: DataStoreCoordinator,
- hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
- hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
- keyguardCoordinator: KeyguardCoordinator,
- rankingCoordinator: RankingCoordinator,
- appOpsCoordinator: AppOpsCoordinator,
- deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
- bubbleCoordinator: BubbleCoordinator,
- headsUpCoordinator: HeadsUpCoordinator,
- gutsCoordinator: GutsCoordinator,
- conversationCoordinator: ConversationCoordinator,
- debugModeCoordinator: DebugModeCoordinator,
- groupCountCoordinator: GroupCountCoordinator,
- mediaCoordinator: MediaCoordinator,
- preparationCoordinator: PreparationCoordinator,
- remoteInputCoordinator: RemoteInputCoordinator,
- rowAppearanceCoordinator: RowAppearanceCoordinator,
- stackCoordinator: StackCoordinator,
- shadeEventCoordinator: ShadeEventCoordinator,
- smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
- viewConfigCoordinator: ViewConfigCoordinator,
- visualStabilityCoordinator: VisualStabilityCoordinator,
- sensitiveContentCoordinator: SensitiveContentCoordinator,
- dismissibilityCoordinator: DismissibilityCoordinator
+ notifPipelineFlags: NotifPipelineFlags,
+ dataStoreCoordinator: DataStoreCoordinator,
+ hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
+ hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
+ keyguardCoordinator: KeyguardCoordinator,
+ rankingCoordinator: RankingCoordinator,
+ appOpsCoordinator: AppOpsCoordinator,
+ deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
+ bubbleCoordinator: BubbleCoordinator,
+ headsUpCoordinator: HeadsUpCoordinator,
+ gutsCoordinator: GutsCoordinator,
+ conversationCoordinator: ConversationCoordinator,
+ debugModeCoordinator: DebugModeCoordinator,
+ groupCountCoordinator: GroupCountCoordinator,
+ groupWhenCoordinator: GroupWhenCoordinator,
+ mediaCoordinator: MediaCoordinator,
+ preparationCoordinator: PreparationCoordinator,
+ remoteInputCoordinator: RemoteInputCoordinator,
+ rowAppearanceCoordinator: RowAppearanceCoordinator,
+ stackCoordinator: StackCoordinator,
+ shadeEventCoordinator: ShadeEventCoordinator,
+ smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
+ viewConfigCoordinator: ViewConfigCoordinator,
+ visualStabilityCoordinator: VisualStabilityCoordinator,
+ sensitiveContentCoordinator: SensitiveContentCoordinator,
+ dismissibilityCoordinator: DismissibilityCoordinator
) : NotifCoordinators {
private val mCoordinators: MutableList<Coordinator> = ArrayList()
@@ -82,6 +83,7 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(debugModeCoordinator)
mCoordinators.add(conversationCoordinator)
mCoordinators.add(groupCountCoordinator)
+ mCoordinators.add(groupWhenCoordinator)
mCoordinators.add(mediaCoordinator)
mCoordinators.add(rowAppearanceCoordinator)
mCoordinators.add(stackCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
index e2edc01f0d7c..061ef9e9341c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
@@ -20,4 +20,7 @@ package com.android.systemui.statusbar.notification.collection.render
interface NotifGroupController {
/** Set the number of children that this group would have if not for the 8-child max */
fun setUntruncatedChildCount(untruncatedChildCount: Int)
+
+ /** Set the when value of notification group that reflects most important closest notification time */
+ fun setNotificationGroupWhen(whenMillis: Long)
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 2affa77eee04..6deaa23ca20e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -192,7 +192,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private int mMaxSmallHeightBeforeS;
private int mMaxSmallHeight;
private int mMaxSmallHeightLarge;
- private int mMaxSmallHeightMedia;
private int mMaxExpandedHeight;
private int mIncreasedPaddingBetweenElements;
private int mNotificationLaunchHeight;
@@ -853,6 +852,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
+ * @see NotificationChildrenContainer#setNotificationGroupWhen(long)
+ */
+ public void setNotificationGroupWhen(long whenMillis) {
+ if (mIsSummaryWithChildren) {
+ mChildrenContainer.setNotificationGroupWhen(whenMillis);
+ } else {
+ Log.w(TAG, "setNotificationGroupWhen( whenMillis: " + whenMillis + ")"
+ + " mIsSummaryWithChildren: false"
+ + " mChildrenContainer has not been inflated yet.");
+ }
+ }
+
+ /**
* Called after children have been attached to set the expansion states
*/
public void resetChildSystemExpandedStates() {
@@ -1774,8 +1786,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
R.dimen.notification_min_height);
mMaxSmallHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_increased);
- mMaxSmallHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
- R.dimen.notification_min_height_media);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 2dda6fd802e8..dfc80fde3cd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -349,6 +349,15 @@ public class ExpandableNotificationRowController implements NotifViewController
}
@Override
+ public void setNotificationGroupWhen(long whenMillis) {
+ if (mView.isSummaryWithChildren()) {
+ mView.setNotificationGroupWhen(whenMillis);
+ } else {
+ Log.w(TAG, "Called setNotificationTime(" + whenMillis + ") on a leaf row");
+ }
+ }
+
+ @Override
public void setSystemExpanded(boolean systemExpanded) {
mView.setSystemExpanded(systemExpanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 1f664cb16179..9a777ea6230b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -27,6 +27,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.widget.DateTimeView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -344,6 +345,21 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
mTransformationHelper.setVisible(visible);
}
+ /***
+ * Set Notification when value
+ * @param whenMillis
+ */
+ public void setNotificationWhen(long whenMillis) {
+ if (mNotificationHeader == null) {
+ return;
+ }
+
+ final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time);
+
+ if (timeView instanceof DateTimeView) {
+ ((DateTimeView) timeView).setTime(whenMillis);
+ }
+ }
protected void addTransformedViews(View... views) {
for (View view : views) {
if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 9b93d7b9e1d0..40f55bd3726c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -296,6 +296,19 @@ public class NotificationChildrenContainer extends ViewGroup
}
/**
+ * Set the notification time in the group so that the view can show the latest event in the UI
+ * appropriately.
+ */
+ public void setNotificationGroupWhen(long whenMillis) {
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.setNotificationWhen(whenMillis);
+ }
+ if (mNotificationHeaderWrapperLowPriority != null) {
+ mNotificationHeaderWrapperLowPriority.setNotificationWhen(whenMillis);
+ }
+ }
+
+ /**
* Add a child notification to this view.
*
* @param row the row to add
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index c72eb054c62c..39b5b5a4cef8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
@@ -288,10 +289,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
* @param mobileContext possibly mcc/mnc overridden mobile context
* @param subId the subscriptionId for this mobile view
*/
- public void addModernMobileView(Context mobileContext, int subId) {
+ public void addModernMobileView(
+ Context mobileContext,
+ MobileViewLogger mobileViewLogger,
+ int subId) {
Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind(
mobileContext,
+ mobileViewLogger,
"mobile",
mMobileIconsViewModel.viewModelForSub(subId, mLocation)
);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 11863627218e..04cc8ce792d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -569,7 +569,10 @@ public interface StatusBarIconController {
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
- mDemoStatusIcons.addModernMobileView(mContext, subId);
+ mDemoStatusIcons.addModernMobileView(
+ mContext,
+ mMobileIconsViewModel.getLogger(),
+ subId);
}
return view;
@@ -601,6 +604,7 @@ public interface StatusBarIconController {
return ModernStatusBarMobileView
.constructAndBind(
mobileContext,
+ mMobileIconsViewModel.getLogger(),
slot,
mMobileIconsViewModel.viewModelForSub(subId, mLocation)
);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index f6c0da8da8c0..833cb93f62e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -79,6 +79,18 @@ public class StatusBarIconHolder {
private @IconType int mType = TYPE_ICON;
private int mTag = 0;
+ /** Returns a human-readable string representing the given type. */
+ public static String getTypeString(@IconType int type) {
+ switch(type) {
+ case TYPE_ICON: return "ICON";
+ case TYPE_WIFI: return "WIFI_OLD";
+ case TYPE_MOBILE: return "MOBILE_OLD";
+ case TYPE_MOBILE_NEW: return "MOBILE_NEW";
+ case TYPE_WIFI_NEW: return "WIFI_NEW";
+ default: return "UNKNOWN";
+ }
+ }
+
private StatusBarIconHolder() {
}
@@ -230,4 +242,11 @@ public class StatusBarIconHolder {
public int getTag() {
return mTag;
}
+
+ @Override
+ public String toString() {
+ return "StatusBarIconHolder(type=" + getTypeString(mType)
+ + " tag=" + getTag()
+ + " visible=" + isVisible() + ")";
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 8800b05fadb7..565481a20d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -27,6 +27,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
/** A class holding the list of all the system icons that could be shown in the status bar. */
public class StatusBarIconList {
@@ -302,7 +303,7 @@ public class StatusBarIconList {
@Override
public String toString() {
- return String.format("(%s) %s", mName, subSlotsString());
+ return String.format("(%s) holder=%s %s", mName, mHolder, subSlotsString());
}
private String subSlotsString() {
@@ -310,7 +311,10 @@ public class StatusBarIconList {
return "";
}
- return "" + mSubSlots.size() + " subSlots";
+ return "| " + mSubSlots.size() + " subSlots: "
+ + mSubSlots.stream()
+ .map(StatusBarIconHolder::toString)
+ .collect(Collectors.joining("|"));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt
new file mode 100644
index 000000000000..e594a8a5efd9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileViewLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for changes with the new mobile views. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class MobileViewLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 44647515a6e5..adfea80715a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -148,5 +148,19 @@ abstract class StatusBarPipelineModule {
fun provideMobileInputLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("MobileInputLog", 100)
}
+
+ @Provides
+ @SysUISingleton
+ @MobileViewLog
+ fun provideMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("MobileViewLog", 100)
+ }
+
+ @Provides
+ @SysUISingleton
+ @VerboseMobileViewLog
+ fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("VerboseMobileViewLog", 100)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt
new file mode 100644
index 000000000000..b98789807dd3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseMobileViewLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Logs for **verbose** changes with the new mobile views. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class VerboseMobileViewLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 3cbd2b76c248..159f689de370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.mobile.shared
+package com.android.systemui.statusbar.pipeline.mobile.data
import android.net.Network
import android.net.NetworkCapabilities
@@ -133,24 +133,6 @@ constructor(
)
}
- fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
- buffer.log(
- TAG,
- LogLevel.INFO,
- { str1 = subs.toString() },
- { "Sub IDs in MobileUiAdapter updated internally: $str1" },
- )
- }
-
- fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
- buffer.log(
- TAG,
- LogLevel.INFO,
- { str1 = subs.toString() },
- { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
- )
- }
-
fun logCarrierConfigChanged(subId: Int) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index bb3b9b2166c3..efdce062bb37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -30,8 +30,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 96b96f14d6aa..e182bc66081a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -36,6 +36,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
@@ -47,7 +48,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameMo
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 53a208cd171e..f97e41c018f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -45,11 +45,11 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index da63ab10f733..075e6ec11ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import java.io.PrintWriter
import javax.inject.Inject
@@ -55,17 +54,14 @@ constructor(
interactor: MobileIconsInteractor,
private val iconController: StatusBarIconController,
private val iconsViewModelFactory: MobileIconsViewModel.Factory,
- private val logger: MobileInputLogger,
+ private val logger: MobileViewLogger,
@Application private val scope: CoroutineScope,
private val statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable {
private val mobileSubIds: Flow<List<Int>> =
- interactor.filteredSubscriptions
- .mapLatest { subscriptions ->
- subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
- }
- .distinctUntilChanged()
- .onEach { logger.logUiAdapterSubIdsUpdated(it) }
+ interactor.filteredSubscriptions.mapLatest { subscriptions ->
+ subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
+ }
/**
* We expose the list of tracked subscriptions as a flow of a list of ints, where each int is
@@ -75,7 +71,10 @@ constructor(
* NOTE: this should go away as the view presenter learns more about this data pipeline
*/
private val mobileSubIdsState: StateFlow<List<Int>> =
- mobileSubIds.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+ mobileSubIds
+ .distinctUntilChanged()
+ .onEach { logger.logUiAdapterSubIdsUpdated(it) }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
/** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */
val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
new file mode 100644
index 000000000000..90dff23c637c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui
+
+import android.view.View
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Logs for changes with the new mobile views. */
+@SysUISingleton
+class MobileViewLogger
+@Inject
+constructor(
+ @MobileViewLog private val buffer: LogBuffer,
+ dumpManager: DumpManager,
+) : Dumpable {
+ init {
+ dumpManager.registerNormalDumpable(this)
+ }
+
+ private val collectionStatuses = mutableMapOf<String, Boolean>()
+
+ fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = subs.toString() },
+ { "Sub IDs in MobileUiAdapter updated internally: $str1" },
+ )
+ }
+
+ fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = subs.toString() },
+ { "Sub IDs in MobileUiAdapter being sent to icon controller: $str1" },
+ )
+ }
+
+ fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = view.getIdForLogging()
+ str2 = viewModel.getIdForLogging()
+ str3 = viewModel.locationName
+ },
+ { "New view binding. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+ )
+ }
+
+ fun logCollectionStarted(view: View, viewModel: LocationBasedMobileViewModel) {
+ collectionStatuses[view.getIdForLogging()] = true
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = view.getIdForLogging()
+ str2 = viewModel.getIdForLogging()
+ str3 = viewModel.locationName
+ },
+ { "Collection started. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+ )
+ }
+
+ fun logCollectionStopped(view: View, viewModel: LocationBasedMobileViewModel) {
+ collectionStatuses[view.getIdForLogging()] = false
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = view.getIdForLogging()
+ str2 = viewModel.getIdForLogging()
+ str3 = viewModel.locationName
+ },
+ { "Collection stopped. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+ )
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("Collection statuses per view:---")
+ collectionStatuses.forEach { viewId, isCollecting ->
+ pw.println("viewId=$viewId, isCollecting=$isCollecting")
+ }
+ }
+
+ companion object {
+ fun Any.getIdForLogging(): String {
+ // The identityHashCode is guaranteed to be constant for the lifetime of the object.
+ return Integer.toHexString(System.identityHashCode(this))
+ }
+ }
+}
+
+private const val TAG = "MobileViewLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
new file mode 100644
index 000000000000..f67bc8f14447
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui
+
+import android.view.View
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import javax.inject.Inject
+
+/**
+ * Logs for **verbose** changes with the new mobile views.
+ *
+ * This is a hopefully temporary log until we resolve some open bugs (b/267236367, b/269565345,
+ * b/270300839).
+ */
+@SysUISingleton
+class VerboseMobileViewLogger
+@Inject
+constructor(
+ @VerboseMobileViewLog private val buffer: LogBuffer,
+) {
+ fun logBinderReceivedSignalIcon(parentView: View, subId: Int, icon: SignalIconModel) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = parentView.getIdForLogging()
+ int1 = subId
+ int2 = icon.level
+ bool1 = icon.showExclamationMark
+ },
+ {
+ "Binder[subId=$int1, viewId=$str1] received new signal icon: " +
+ "level=$int2 showExclamation=$bool1"
+ },
+ )
+ }
+
+ fun logBinderReceivedNetworkTypeIcon(parentView: View, subId: Int, icon: Icon.Resource?) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = parentView.getIdForLogging()
+ int1 = subId
+ bool1 = icon != null
+ int2 = icon?.res ?: -1
+ },
+ {
+ "Binder[subId=$int1, viewId=$str1] received new network type icon: " +
+ if (bool1) "resId=$int2" else "null"
+ },
+ )
+ }
+}
+
+private const val TAG = "VerboseMobileViewLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index db585e68d185..5b7d45b55c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -36,8 +36,10 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@@ -48,6 +50,7 @@ object MobileIconBinder {
fun bind(
view: ViewGroup,
viewModel: LocationBasedMobileViewModel,
+ logger: MobileViewLogger,
): ModernStatusBarViewBinding {
val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group)
val activityContainer = view.requireViewById<View>(R.id.inout_container)
@@ -70,8 +73,13 @@ object MobileIconBinder {
val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
+ var isCollecting: Boolean = false
+
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
+ logger.logCollectionStarted(view, viewModel)
+ isCollecting = true
+
launch {
visibilityState.collect { state ->
when (state) {
@@ -96,6 +104,11 @@ object MobileIconBinder {
// Set the icon for the triangle
launch {
viewModel.icon.distinctUntilChanged().collect { icon ->
+ viewModel.verboseLogger?.logBinderReceivedSignalIcon(
+ view,
+ viewModel.subscriptionId,
+ icon,
+ )
mobileDrawable.level =
SignalDrawable.getState(
icon.level,
@@ -114,6 +127,11 @@ object MobileIconBinder {
// Set the network type icon
launch {
viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
+ viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
+ view,
+ viewModel.subscriptionId,
+ dataTypeId,
+ )
dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
}
@@ -150,6 +168,13 @@ object MobileIconBinder {
}
launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+
+ try {
+ awaitCancellation()
+ } finally {
+ isCollecting = false
+ logger.logCollectionStopped(view, viewModel)
+ }
}
}
@@ -175,6 +200,10 @@ object MobileIconBinder {
}
decorTint.value = newTint
}
+
+ override fun isCollecting(): Boolean {
+ return isCollecting
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index ed9a1884a7b4..4144293d5ccd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -20,6 +20,8 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
@@ -31,6 +33,15 @@ class ModernStatusBarMobileView(
var subId: Int = -1
+ override fun toString(): String {
+ return "ModernStatusBarMobileView(" +
+ "slot='$slot', " +
+ "subId=$subId, " +
+ "isCollecting=${binding.isCollecting()}, " +
+ "visibleState=${getVisibleStateString(visibleState)}); " +
+ "viewString=${super.toString()}"
+ }
+
companion object {
/**
@@ -40,6 +51,7 @@ class ModernStatusBarMobileView(
@JvmStatic
fun constructAndBind(
context: Context,
+ logger: MobileViewLogger,
slot: String,
viewModel: LocationBasedMobileViewModel,
): ModernStatusBarMobileView {
@@ -48,7 +60,8 @@ class ModernStatusBarMobileView(
as ModernStatusBarMobileView)
.also {
it.subId = viewModel.subscriptionId
- it.initView(slot) { MobileIconBinder.bind(it, viewModel) }
+ it.initView(slot) { MobileIconBinder.bind(it, viewModel, logger) }
+ logger.logNewViewBinding(it, viewModel)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
index 8e103f7bee2f..f775940140cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import android.graphics.Color
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
/**
* A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This
@@ -26,11 +27,15 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
*
* @param commonImpl for convenience, this class wraps a base interface that can provides all of the
* common implementations between locations. See [MobileIconViewModel]
+ * @property locationName the name of the location of this VM, used for logging.
+ * @property verboseLogger an optional logger to log extremely verbose view updates.
*/
abstract class LocationBasedMobileViewModel(
val commonImpl: MobileIconViewModelCommon,
statusBarPipelineFlags: StatusBarPipelineFlags,
debugTint: Int,
+ val locationName: String,
+ val verboseLogger: VerboseMobileViewLogger?,
) : MobileIconViewModelCommon by commonImpl {
val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
@@ -45,11 +50,16 @@ abstract class LocationBasedMobileViewModel(
fun viewModelForLocation(
commonImpl: MobileIconViewModelCommon,
statusBarPipelineFlags: StatusBarPipelineFlags,
+ verboseMobileViewLogger: VerboseMobileViewLogger,
loc: StatusBarLocation,
): LocationBasedMobileViewModel =
when (loc) {
StatusBarLocation.HOME ->
- HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+ HomeMobileIconViewModel(
+ commonImpl,
+ statusBarPipelineFlags,
+ verboseMobileViewLogger,
+ )
StatusBarLocation.KEYGUARD ->
KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
@@ -60,20 +70,41 @@ abstract class LocationBasedMobileViewModel(
class HomeMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
statusBarPipelineFlags: StatusBarPipelineFlags,
+ verboseMobileViewLogger: VerboseMobileViewLogger,
) :
MobileIconViewModelCommon,
- LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.CYAN)
+ LocationBasedMobileViewModel(
+ commonImpl,
+ statusBarPipelineFlags,
+ debugTint = Color.CYAN,
+ locationName = "Home",
+ verboseMobileViewLogger,
+ )
class QsMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
statusBarPipelineFlags: StatusBarPipelineFlags,
) :
MobileIconViewModelCommon,
- LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.GREEN)
+ LocationBasedMobileViewModel(
+ commonImpl,
+ statusBarPipelineFlags,
+ debugTint = Color.GREEN,
+ locationName = "QS",
+ // Only do verbose logging for the Home location.
+ verboseLogger = null,
+ )
class KeyguardMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
statusBarPipelineFlags: StatusBarPipelineFlags,
) :
MobileIconViewModelCommon,
- LocationBasedMobileViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.MAGENTA)
+ LocationBasedMobileViewModel(
+ commonImpl,
+ statusBarPipelineFlags,
+ debugTint = Color.MAGENTA,
+ locationName = "Keyguard",
+ // Only do verbose logging for the Home location.
+ verboseLogger = null,
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 049627899eff..dbb534b24471 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -49,7 +49,7 @@ interface MobileIconViewModelCommon {
val contentDescription: Flow<ContentDescription>
val roaming: Flow<Boolean>
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
- val networkTypeIcon: Flow<Icon?>
+ val networkTypeIcon: Flow<Icon.Resource?>
val activityInVisible: Flow<Boolean>
val activityOutVisible: Flow<Boolean>
val activityContainerVisible: Flow<Boolean>
@@ -161,7 +161,7 @@ constructor(
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val networkTypeIcon: Flow<Icon?> =
+ override val networkTypeIcon: Flow<Icon.Resource?> =
combine(
iconInteractor.networkTypeIconGroup,
showNetworkTypeIcon,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 8cb52af336da..2b90065284d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -23,6 +23,8 @@ import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import javax.inject.Inject
@@ -39,6 +41,8 @@ class MobileIconsViewModel
@Inject
constructor(
val subscriptionIdsFlow: StateFlow<List<Int>>,
+ val logger: MobileViewLogger,
+ private val verboseLogger: VerboseMobileViewLogger,
private val interactor: MobileIconsInteractor,
private val airplaneModeInteractor: AirplaneModeInteractor,
private val constants: ConnectivityConstants,
@@ -66,6 +70,7 @@ constructor(
return LocationBasedMobileViewModel.viewModelForLocation(
common,
statusBarPipelineFlags,
+ verboseLogger,
location,
)
}
@@ -79,6 +84,8 @@ constructor(
class Factory
@Inject
constructor(
+ private val logger: MobileViewLogger,
+ private val verboseLogger: VerboseMobileViewLogger,
private val interactor: MobileIconsInteractor,
private val airplaneModeInteractor: AirplaneModeInteractor,
private val constants: ConnectivityConstants,
@@ -88,6 +95,8 @@ constructor(
fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
return MobileIconsViewModel(
subscriptionIdsFlow,
+ logger,
+ verboseLogger,
interactor,
airplaneModeInteractor,
constants,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
index f67876b50233..81f8683411ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
@@ -37,4 +37,7 @@ interface ModernStatusBarViewBinding {
/** Notifies that the decor tint has been updated (used only for the dot). */
fun onDecorTintChanged(newTint: Int)
+
+ /** Returns true if the binding between the view and view-model is currently collecting. */
+ fun isCollecting(): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
index b1e28129a690..1a1340484bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
@@ -36,7 +36,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) :
BaseStatusBarFrameLayout(context, attrs) {
private lateinit var slot: String
- private lateinit var binding: ModernStatusBarViewBinding
+ internal lateinit var binding: ModernStatusBarViewBinding
@StatusBarIconView.VisibleState
private var iconVisibleState: Int = STATE_HIDDEN
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 2aff12c8721d..9e8c814ca2a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarV
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -74,8 +75,12 @@ object WifiViewBinder {
val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
+ var isCollecting: Boolean = false
+
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
+ isCollecting = true
+
launch {
visibilityState.collect { visibilityState ->
groupView.isVisible = visibilityState == STATE_ICON
@@ -127,6 +132,12 @@ object WifiViewBinder {
airplaneSpacer.isVisible = visible
}
}
+
+ try {
+ awaitCancellation()
+ } finally {
+ isCollecting = false
+ }
}
}
@@ -152,6 +163,10 @@ object WifiViewBinder {
}
decorTint.value = newTint
}
+
+ override fun isCollecting(): Boolean {
+ return isCollecting
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
index 7a734862fe1b..f23e10287164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
@@ -33,6 +34,15 @@ class ModernStatusBarWifiView(
context: Context,
attrs: AttributeSet?,
) : ModernStatusBarView(context, attrs) {
+
+ override fun toString(): String {
+ return "ModernStatusBarWifiView(" +
+ "slot='$slot', " +
+ "isCollecting=${binding.isCollecting()}, " +
+ "visibleState=${StatusBarIconView.getVisibleStateString(visibleState)}); " +
+ "viewString=${super.toString()}"
+ }
+
companion object {
/**
* Inflates a new instance of [ModernStatusBarWifiView], binds it to a view model, and
@@ -45,12 +55,9 @@ class ModernStatusBarWifiView(
slot: String,
wifiViewModel: LocationBasedWifiViewModel,
): ModernStatusBarWifiView {
- return (
- LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
- as ModernStatusBarWifiView
- ).also {
- it.initView(slot) { WifiViewBinder.bind(it, wifiViewModel) }
- }
+ return (LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
+ as ModernStatusBarWifiView)
+ .also { it.initView(slot) { WifiViewBinder.bind(it, wifiViewModel) } }
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index c2fb904f64ad..ffd75fb9bbc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.RemoteAction;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -101,6 +102,9 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor;
private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks;
+ @Captor
+ private ArgumentCaptor<AnimatorListenerAdapter> mAnimatorArgumentCaptor;
+
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
@@ -478,12 +482,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
when(mClipboardOverlayWindow.getWindowInsets()).thenReturn(
getImeInsets(new Rect(0, 0, 0, 1)));
mOverlayController.setClipData(mSampleClipData, "");
+ Animator mockFadeoutAnimator = Mockito.mock(Animator.class);
+ when(mClipboardOverlayView.getMinimizedFadeoutAnimation()).thenReturn(mockFadeoutAnimator);
verify(mClipboardOverlayView).setMinimized(true);
verify(mClipboardOverlayView, never()).setMinimized(false);
verify(mClipboardOverlayView, never()).showTextPreview(any(), anyBoolean());
mCallbacks.onMinimizedViewTapped();
+ verify(mockFadeoutAnimator).addListener(mAnimatorArgumentCaptor.capture());
+ mAnimatorArgumentCaptor.getValue().onAnimationEnd(mockFadeoutAnimator);
verify(mClipboardOverlayView).setMinimized(false);
verify(mClipboardOverlayView).showTextPreview("Test Item", false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 6c23254941a8..0a9470617a5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -1,6 +1,8 @@
package com.android.systemui.dreams
+import android.animation.Animator
import android.animation.AnimatorSet
+import android.animation.ValueAnimator
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
@@ -10,13 +12,16 @@ import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransition
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.eq
@@ -71,6 +76,19 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
}
@Test
+ fun testExitAnimationUpdatesState() {
+ controller.startExitAnimations(animatorBuilder = { mockAnimator })
+
+ verify(stateController).setExitAnimationsRunning(true)
+
+ val captor = argumentCaptor<Animator.AnimatorListener>()
+ verify(mockAnimator).addListener(captor.capture())
+
+ captor.value.onAnimationEnd(mockAnimator)
+ verify(stateController).setExitAnimationsRunning(false)
+ }
+
+ @Test
fun testWakeUpCallsExecutor() {
val mockExecutor: DelayableExecutor = mock()
val mockCallback: Runnable = mock()
@@ -87,7 +105,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
fun testWakeUpAfterStartWillCancel() {
val mockStartAnimator: AnimatorSet = mock()
- controller.startEntryAnimations(animatorBuilder = { mockStartAnimator })
+ controller.startEntryAnimations(false, animatorBuilder = { mockStartAnimator })
verify(mockStartAnimator, never()).cancel()
@@ -100,4 +118,50 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
// animator.
verify(mockStartAnimator, times(1)).cancel()
}
+
+ @Test
+ fun testEntryAnimations_translatesUpwards() {
+ val mockStartAnimator: AnimatorSet = mock()
+
+ controller.startEntryAnimations(
+ /* downwards= */ false,
+ animatorBuilder = { mockStartAnimator }
+ )
+
+ val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
+ verify(mockStartAnimator).playTogether(animatorCaptor.capture())
+
+ // Check if there's a ValueAnimator starting at the expected Y distance.
+ val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
+ assertTrue(
+ animators.any {
+ // Call setCurrentFraction so the animated value jumps to the initial value.
+ it.setCurrentFraction(0f)
+ it.animatedValue == DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
+ }
+ )
+ }
+
+ @Test
+ fun testEntryAnimations_translatesDownwards() {
+ val mockStartAnimator: AnimatorSet = mock()
+
+ controller.startEntryAnimations(
+ /* downwards= */ true,
+ animatorBuilder = { mockStartAnimator }
+ )
+
+ val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
+ verify(mockStartAnimator).playTogether(animatorCaptor.capture())
+
+ // Check if there's a ValueAnimator starting at the expected Y distance.
+ val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
+ assertTrue(
+ animators.any {
+ // Call setCurrentFraction so the animated value jumps to the initial value.
+ it.setCurrentFraction(0f)
+ it.animatedValue == -DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
+ }
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 6b095ffd3977..2a72e7d85d3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -33,6 +34,7 @@ import android.view.ViewTreeObserver;
import androidx.test.filters.SmallTest;
+import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
@@ -65,6 +67,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
@Mock
+ LowLightTransitionCoordinator mLowLightTransitionCoordinator;
+
+ @Mock
DreamOverlayContainerView mDreamOverlayContainerView;
@Mock
@@ -109,6 +114,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mComplicationHostViewController,
mDreamOverlayContentView,
mDreamOverlayStatusBarViewController,
+ mLowLightTransitionCoordinator,
mBlurUtils,
mHandler,
mResources,
@@ -200,7 +206,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mController.onViewAttached();
- verify(mAnimationsController).startEntryAnimations();
+ verify(mAnimationsController).startEntryAnimations(false);
verify(mAnimationsController, never()).cancelAnimations();
}
@@ -210,11 +216,11 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mController.onViewAttached();
- verify(mAnimationsController, never()).startEntryAnimations();
+ verify(mAnimationsController, never()).startEntryAnimations(anyBoolean());
}
@Test
- public void testSkipEntryAnimationsWhenExitingLowLight() {
+ public void testDownwardEntryAnimationsWhenExitingLowLight() {
ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
when(mStateController.isLowLightActive()).thenReturn(false);
@@ -230,8 +236,14 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mController.onViewAttached();
// Entry animations should be started then immediately ended to skip to the end.
- verify(mAnimationsController).startEntryAnimations();
- verify(mAnimationsController).endAnimations();
+ verify(mAnimationsController).startEntryAnimations(true);
+ }
+
+ @Test
+ public void testStartsExitAnimationsBeforeEnteringLowLight() {
+ mController.onBeforeEnterLowLight();
+
+ verify(mAnimationsController).startExitAnimations();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index dcd8736711f6..068852de7a43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -22,6 +22,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.view.View;
@@ -33,6 +35,8 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -96,6 +100,10 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
private ComplicationHostViewController mController;
+ private SecureSettings mSecureSettings;
+
+ private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -108,12 +116,17 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
when(mViewHolder.getLayoutParams()).thenReturn(mComplicationLayoutParams);
when(mComplicationView.getParent()).thenReturn(mComplicationHostView);
+ mSecureSettings = new FakeSettings();
+ mSecureSettings.putFloatForUser(
+ Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, CURRENT_USER_ID);
+
mController = new ComplicationHostViewController(
mComplicationHostView,
mLayoutEngine,
mDreamOverlayStateController,
mLifecycleOwner,
- mViewModel);
+ mViewModel,
+ mSecureSettings);
mController.init();
}
@@ -188,6 +201,23 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
}
+ @Test
+ public void testAnimationsDisabled_ComplicationsNeverSetToInvisible() {
+ //Disable animations
+ mController.mIsAnimationEnabled = false;
+
+ final Observer<Collection<ComplicationViewModel>> observer =
+ captureComplicationViewModelsObserver();
+
+ // Add a complication before entry animations are finished.
+ final HashSet<ComplicationViewModel> complications = new HashSet<>(
+ Collections.singletonList(mComplicationViewModel));
+ observer.onChanged(complications);
+
+ // The complication view should not be set to invisible.
+ verify(mComplicationView, never()).setVisibility(View.INVISIBLE);
+ }
+
private Observer<Collection<ComplicationViewModel>> captureComplicationViewModelsObserver() {
verify(mComplicationViewModelLiveData).observe(eq(mLifecycleOwner),
mObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
new file mode 100644
index 000000000000..eac0e296c51f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.app.Notification
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.SystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.InjectMocks
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class GroupWhenCoordinatorTest : SysuiTestCase() {
+
+ private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
+ private lateinit var afterRenderGroupListener: OnAfterRenderGroupListener
+
+ @Mock private lateinit var pipeline: NotifPipeline
+
+ @Mock private lateinit var delayableExecutor: DelayableExecutor
+
+ @Mock private lateinit var groupController: NotifGroupController
+
+ @Mock private lateinit var systemClock: SystemClock
+
+ @InjectMocks private lateinit var coordinator: GroupWhenCoordinator
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ whenever(systemClock.currentTimeMillis()).thenReturn(NOW)
+ coordinator.attach(pipeline)
+
+ beforeFinalizeFilterListener = withArgCaptor {
+ verify(pipeline).addOnBeforeFinalizeFilterListener(capture())
+ }
+ afterRenderGroupListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderGroupListener(capture())
+ }
+ }
+
+ @Test
+ fun setNotificationGroupWhen_setClosestTimeByNow_whenAllNotificationsAreBeforeNow() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW - 10L)
+ val childEntry2 = buildNotificationEntry(2, NOW - 100L)
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2))
+ .build()
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(groupController).setNotificationGroupWhen(eq(NOW - 10L))
+ }
+
+ @Test
+ fun setNotificationGroupWhen_setClosestTimeByNow_whenAllNotificationsAreAfterNow() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW + 10L)
+ val childEntry2 = buildNotificationEntry(2, NOW + 100L)
+
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2))
+ .build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(groupController).setNotificationGroupWhen(eq(NOW + 10L))
+ }
+
+ @Test
+ fun setNotificationGroupWhen_setClosestFutureTimeByNow_whenThereAreBothBeforeAndAfterNow() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW + 100L)
+ val childEntry2 = buildNotificationEntry(2, NOW + 10L)
+ val childEntry3 = buildNotificationEntry(3, NOW - 100L)
+ val childEntry4 = buildNotificationEntry(4, NOW - 9L)
+
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2, childEntry3, childEntry4))
+ .build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(groupController).setNotificationGroupWhen(eq(NOW + 10L))
+ }
+
+ @Test
+ fun setNotificationGroupWhen_filterInvalidNotificationTimes() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW + 100L)
+ val childEntry2 = buildNotificationEntry(2, -20000L)
+ val childEntry3 = buildNotificationEntry(4, 0)
+
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2, childEntry3))
+ .build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(groupController).setNotificationGroupWhen(eq(NOW + 100))
+ }
+
+ @Test
+ fun setNotificationGroupWhen_setSummaryTimeWhenAllNotificationTimesAreInvalid() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, 0)
+ val childEntry2 = buildNotificationEntry(2, -1)
+
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2))
+ .build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(groupController, never()).setNotificationGroupWhen(NOW)
+ }
+
+ @Test
+ fun setNotificationGroupWhen_schedulePipelineInvalidationWhenAnyNotificationIsInTheFuture() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW + 1000L)
+ val childEntry2 = buildNotificationEntry(2, NOW + 2000L)
+ val childEntry3 = buildNotificationEntry(3, NOW - 100L)
+
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(summaryEntry)
+ .setChildren(listOf(childEntry1, childEntry2, childEntry3))
+ .build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ // THEN
+ verify(delayableExecutor).executeDelayed(any(), eq(1000))
+ }
+
+ @Test
+ fun setNotificationGroupWhen_cancelPrevPipelineInvalidation() {
+ // GIVEN
+ val summaryEntry = buildNotificationEntry(0, NOW)
+ val childEntry1 = buildNotificationEntry(1, NOW + 1L)
+ val prevInvalidation = mock<Runnable>()
+ whenever(delayableExecutor.executeDelayed(any(), any())).thenReturn(prevInvalidation)
+
+ val groupEntry =
+ GroupEntryBuilder().setSummary(summaryEntry).setChildren(listOf(childEntry1)).build()
+
+ // WHEN
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+ afterRenderGroupListener.onAfterRenderGroup(groupEntry, groupController)
+
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
+
+ // THEN
+ verify(prevInvalidation).run()
+ }
+
+ private fun buildNotificationEntry(id: Int, timeMillis: Long): NotificationEntry {
+ val notification = Notification.Builder(mContext).setWhen(timeMillis).build()
+ val sbn = SbnBuilder().setNotification(notification).build()
+ return NotificationEntryBuilder().setId(id).setSbn(sbn).build()
+ }
+
+ private companion object {
+ private const val NOW = 1000L
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt
index 86529dce948a..35dea60b1a1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/shared/MobileInputLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLoggerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.mobile.shared
+package com.android.systemui.statusbar.pipeline.mobile.data
import android.net.Network
import android.net.NetworkCapabilities
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index 0145103d55e1..dfef62e95eda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -24,8 +24,8 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 17502f28a479..07c8cee9a3d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -27,13 +27,13 @@ import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoModeMobileConnectionDataSource
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.validMobileEvent
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index b2577e349da7..bd5a4d7f7385 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -53,6 +53,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
@@ -65,7 +66,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrier
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 09b7a66c925d..68b1cda62f4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,12 +37,12 @@ import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
-import com.android.systemui.statusbar.pipeline.mobile.shared.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
new file mode 100644
index 000000000000..4aa48d6f25f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui
+
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.KeyguardMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MobileViewLoggerTest : SysuiTestCase() {
+ private val buffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
+ private val stringWriter = StringWriter()
+ private val printWriter = PrintWriter(stringWriter)
+
+ private val underTest = MobileViewLogger(buffer, mock())
+
+ @Mock private lateinit var flags: StatusBarPipelineFlags
+ @Mock private lateinit var commonViewModel: MobileIconViewModel
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun collectionStarted_dumpHasInfo() {
+ val view = TextView(context)
+ val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+
+ underTest.logCollectionStarted(view, viewModel)
+
+ val dumpString = getDumpString()
+ assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=true")
+ }
+
+ @Test
+ fun collectionStarted_multipleViews_dumpHasInfo() {
+ val view = TextView(context)
+ val view2 = TextView(context)
+ val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+ val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+
+ underTest.logCollectionStarted(view, viewModel)
+ underTest.logCollectionStarted(view2, viewModel2)
+
+ val dumpString = getDumpString()
+ assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=true")
+ assertThat(dumpString).contains("${view2.getIdForLogging()}, isCollecting=true")
+ }
+
+ @Test
+ fun collectionStopped_dumpHasInfo() {
+ val view = TextView(context)
+ val view2 = TextView(context)
+ val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+ val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+
+ underTest.logCollectionStarted(view, viewModel)
+ underTest.logCollectionStarted(view2, viewModel2)
+ underTest.logCollectionStopped(view, viewModel)
+
+ val dumpString = getDumpString()
+ assertThat(dumpString).contains("${view.getIdForLogging()}, isCollecting=false")
+ assertThat(dumpString).contains("${view2.getIdForLogging()}, isCollecting=true")
+ }
+
+ private fun getDumpString(): String {
+ underTest.dump(printWriter, args = arrayOf())
+ return stringWriter.toString()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index e68a3970ae93..7420db2e895e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
@@ -60,6 +61,7 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var viewLogger: MobileViewLogger
@Mock private lateinit var constants: ConnectivityConstants
private lateinit var interactor: FakeMobileIconInteractor
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -94,7 +96,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Test
fun setVisibleState_icon_iconShownDotHidden() {
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
view.setVisibleState(StatusBarIconView.STATE_ICON, /* animate= */ false)
@@ -109,8 +117,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Test
fun setVisibleState_dot_iconHiddenDotShown() {
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
view.setVisibleState(StatusBarIconView.STATE_DOT, /* animate= */ false)
ViewUtils.attachView(view)
@@ -124,8 +137,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Test
fun setVisibleState_hidden_iconAndDotHidden() {
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
view.setVisibleState(StatusBarIconView.STATE_HIDDEN, /* animate= */ false)
ViewUtils.attachView(view)
@@ -142,8 +160,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
whenever(constants.hasDataCapabilities).thenReturn(false)
createViewModel()
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -157,8 +180,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
whenever(constants.hasDataCapabilities).thenReturn(true)
createViewModel()
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -171,8 +199,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
fun isIconVisible_notAirplaneMode_outputsTrue() {
airplaneModeRepository.setIsAirplaneMode(false)
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -185,8 +218,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
fun isIconVisible_airplaneMode_outputsTrue() {
airplaneModeRepository.setIsAirplaneMode(true)
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
-
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -198,7 +236,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Test
fun onDarkChanged_iconHasNewColor() {
whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
@@ -214,7 +258,13 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() {
@Test
fun setStaticDrawableColor_iconHasNewColor() {
whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
- val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
ViewUtils.attachView(view)
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index f9830309252d..a6d915243f60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -84,7 +85,7 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
testScope.backgroundScope,
)
- homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+ homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags, mock())
qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
keyguardIcon = KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 4628f8410245..ddb7f4d88d30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -24,6 +24,8 @@ import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirp
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
@@ -51,6 +53,8 @@ class MobileIconsViewModelTest : SysuiTestCase() {
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
+ @Mock private lateinit var logger: MobileViewLogger
+ @Mock private lateinit var verboseLogger: VerboseMobileViewLogger
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -73,6 +77,8 @@ class MobileIconsViewModelTest : SysuiTestCase() {
underTest =
MobileIconsViewModel(
subscriptionIdsFlow,
+ logger,
+ verboseLogger,
interactor,
airplaneModeInteractor,
constants,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
index e4c8fd0cd8a1..b4039d906810 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
@@ -164,6 +164,10 @@ class ModernStatusBarViewTest : SysuiTestCase() {
override fun getShouldIconBeVisible(): Boolean {
return shouldIconBeVisibleInternal
}
+
+ override fun isCollecting(): Boolean {
+ return true
+ }
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0ee883f745ca..f236a961fcb6 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -402,6 +402,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public void systemServicesReady() {
mStats.systemServicesReady(mContext);
+ mCpuWakeupStats.systemServicesReady();
mWorker.systemServicesReady();
final INetworkManagementService nms = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 55d2921b2878..ea157c89f675 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -128,6 +128,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Spline;
+import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
@@ -250,6 +251,7 @@ public final class DisplayManagerService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
private ActivityManager mActivityManager;
private UidImportanceListener mUidImportanceListener = new UidImportanceListener();
+ @Nullable
private IMediaProjectionManager mProjectionService;
private DeviceStateManagerInternal mDeviceStateManager;
@GuardedBy("mSyncRoot")
@@ -1494,8 +1496,9 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
+ final int displayId;
synchronized (mSyncRoot) {
- final int displayId =
+ displayId =
createVirtualDisplayLocked(
callback,
projection,
@@ -1509,8 +1512,39 @@ public final class DisplayManagerService extends SystemService {
mDisplayWindowPolicyControllers.put(
displayId, Pair.create(virtualDevice, dwpc));
}
- return displayId;
}
+
+ // When calling setContentRecordingSession into the WindowManagerService, the WMS
+ // attempts to acquire a lock before executing its main body. Due to this, we need
+ // to be sure that it isn't called while the DisplayManagerService is also holding
+ // a lock, to avoid a deadlock scenario.
+ final ContentRecordingSession session =
+ virtualDisplayConfig.getContentRecordingSession();
+
+ if (displayId != Display.INVALID_DISPLAY && session != null) {
+ // Only attempt to set content recording session if there are details to set and a
+ // VirtualDisplay has been successfully constructed.
+ session.setDisplayId(displayId);
+
+ // We set the content recording session here on the server side instead of using
+ // a second AIDL call in MediaProjection. By ensuring that a virtual display has
+ // been constructed before calling setContentRecordingSession, we avoid a race
+ // condition between the DMS & WMS which could lead to the MediaProjection
+ // being pre-emptively torn down.
+ if (!mWindowManagerInternal.setContentRecordingSession(session)) {
+ // Unable to start mirroring, so tear down projection & release VirtualDisplay.
+ try {
+ getProjectionService().stopActiveProjection();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to tell MediaProjectionManagerService to stop the "
+ + "active projection", e);
+ }
+ releaseVirtualDisplayInternal(callback.asBinder());
+ return Display.INVALID_DISPLAY;
+ }
+ }
+
+ return displayId;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2804,8 +2838,7 @@ public final class DisplayManagerService extends SystemService {
private IMediaProjectionManager getProjectionService() {
if (mProjectionService == null) {
- IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
- mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
+ mProjectionService = mInjector.getProjectionService();
}
return mProjectionService;
}
@@ -2964,6 +2997,11 @@ public final class DisplayManagerService extends SystemService {
boolean getHdrOutputConversionSupport() {
return DisplayControl.getHdrOutputConversionSupport();
}
+
+ IMediaProjectionManager getProjectionService() {
+ IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
+ return IMediaProjectionManager.Stub.asInterface(b);
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index eda15ae32c8b..4f7a2ba58570 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -88,7 +88,17 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
// Called with SyncRoot lock held.
public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
Context context, Handler handler, Listener listener) {
- this(syncRoot, context, handler, listener, DisplayControl::createDisplay);
+ this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
+ @Override
+ public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
+ return DisplayControl.createDisplay(name, secure, requestedRefreshRate);
+ }
+
+ @Override
+ public void destroyDisplay(IBinder displayToken) {
+ DisplayControl.destroyDisplay(displayToken);
+ }
+ });
}
@VisibleForTesting
@@ -311,7 +321,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mSurface.release();
mSurface = null;
}
- DisplayControl.destroyDisplay(getDisplayTokenLocked());
+ mSurfaceControlDisplayFactory.destroyDisplay(getDisplayTokenLocked());
if (mProjection != null && mMediaProjectionCallback != null) {
try {
mProjection.unregisterCallback(mMediaProjectionCallback);
@@ -653,5 +663,12 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
* @return The token reference for the display in SurfaceFlinger.
*/
IBinder createDisplay(String name, boolean secure, float requestedRefreshRate);
+
+ /**
+ * Destroy a display in SurfaceFlinger.
+ *
+ * @param displayToken The display token for the display to be destroyed.
+ */
+ void destroyDisplay(IBinder displayToken);
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index 706aedcbba7b..b05b662dc1e8 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -22,7 +22,9 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
import android.content.Context;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.LongSparseArray;
@@ -40,6 +42,8 @@ import com.android.internal.util.IntPair;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -52,13 +56,13 @@ public class CpuWakeupStats {
private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
private static final String SUBSYSTEM_ALARM_WIFI = "Wifi";
@VisibleForTesting
- static final long WAKEUP_RETENTION_MS = 3 * 24 * 60 * 60_000; // 3 days.
- @VisibleForTesting
static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
- private static final long WAKEUP_WRITE_DELAY_MS = 2 * 60 * 1000; // 2 minutes.
+ private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2);
private final Handler mHandler;
private final IrqDeviceMap mIrqDeviceMap;
+ @VisibleForTesting
+ final Config mConfig = new Config();
private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
@VisibleForTesting
@@ -72,6 +76,14 @@ public class CpuWakeupStats {
mHandler = handler;
}
+ /**
+ * Called on the boot phase SYSTEM_SERVICES_READY.
+ * This ensures that DeviceConfig is ready for calls to read properties.
+ */
+ public synchronized void systemServicesReady() {
+ mConfig.register(new HandlerExecutor(mHandler));
+ }
+
private static int subsystemToStatsReason(int subsystem) {
switch (subsystem) {
case CPU_WAKEUP_SUBSYSTEM_ALARM:
@@ -136,14 +148,15 @@ public class CpuWakeupStats {
// we can delete all history that will not be useful in attributing future wakeups.
mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
- // Limit history of wakeups and their attribution to the last WAKEUP_RETENTION_MS. Note that
+ // Limit history of wakeups and their attribution to the last retentionDuration. Note that
// the last wakeup and its attribution (if computed) is always stored, even if that wakeup
- // had occurred before WAKEUP_RETENTION_MS.
- int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ // had occurred before retentionDuration.
+ final long retentionDuration = mConfig.WAKEUP_STATS_RETENTION_MS;
+ int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
for (int i = lastIdx; i >= 0; i--) {
mWakeupEvents.removeAt(i);
}
- lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - retentionDuration);
for (int i = lastIdx; i >= 0; i--) {
mWakeupAttribution.removeAt(i);
}
@@ -226,6 +239,9 @@ public class CpuWakeupStats {
pw.println("CPU wakeup stats:");
pw.increaseIndent();
+ mConfig.dump(pw);
+ pw.println();
+
mIrqDeviceMap.dump(pw);
pw.println();
@@ -296,7 +312,8 @@ public class CpuWakeupStats {
}
private static final class WakingActivityHistory {
- private static final long WAKING_ACTIVITY_RETENTION_MS = 3 * 60 * 60_000; // 3 hours.
+ private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
+
private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
new SparseArray<>();
@@ -521,4 +538,52 @@ public class CpuWakeupStats {
}
}
}
+
+ static final class Config implements DeviceConfig.OnPropertiesChangedListener {
+ static final String KEY_WAKEUP_STATS_RETENTION_MS = "wakeup_stats_retention_ms";
+
+ private static final String[] PROPERTY_NAMES = {
+ KEY_WAKEUP_STATS_RETENTION_MS,
+ };
+
+ static final long DEFAULT_WAKEUP_STATS_RETENTION_MS = TimeUnit.DAYS.toMillis(3);
+
+ /**
+ * Wakeup stats are retained only for this duration.
+ */
+ public volatile long WAKEUP_STATS_RETENTION_MS = DEFAULT_WAKEUP_STATS_RETENTION_MS;
+
+ void register(Executor executor) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_STATS,
+ executor, this);
+ onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_STATS,
+ PROPERTY_NAMES));
+ }
+
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+ switch (name) {
+ case KEY_WAKEUP_STATS_RETENTION_MS:
+ WAKEUP_STATS_RETENTION_MS = properties.getLong(
+ KEY_WAKEUP_STATS_RETENTION_MS, DEFAULT_WAKEUP_STATS_RETENTION_MS);
+ break;
+ }
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Config:");
+
+ pw.increaseIndent();
+
+ pw.print(KEY_WAKEUP_STATS_RETENTION_MS, WAKEUP_STATS_RETENTION_MS);
+ pw.println();
+
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index c8ec7c20650b..e84f0cc17d05 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -20,17 +20,18 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.credentials.ClearCredentialStateRequest;
+import android.credentials.CredentialProviderInfo;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.ArrayList;
@@ -120,22 +121,22 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
Log.i(TAG, "respondToClientWithResponseAndFinish");
if (isSessionCancelled()) {
mChosenProviderMetric.setChosenProviderStatus(
- MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+ ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onSuccess();
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_SUCCESS);
+ ApiStatus.SUCCESS);
} catch (RemoteException e) {
mChosenProviderMetric.setChosenProviderStatus(
- MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+ ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
Log.i(TAG, "Issue while propagating the response to the client");
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_FAILURE);
+ ApiStatus.FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -144,7 +145,7 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
Log.i(TAG, "respondToClientWithErrorAndFinish");
if (isSessionCancelled()) {
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -154,7 +155,7 @@ public final class ClearRequestSession extends RequestSession<ClearCredentialSta
e.printStackTrace();
}
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_FAILURE);
+ ApiStatus.FAILURE);
finishSession(/*propagateCancellation=*/false);
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 0c1133ce8793..7e1780d05d75 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -16,9 +16,6 @@
package com.android.server.credentials;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -27,17 +24,18 @@ import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialRequest;
import android.credentials.CreateCredentialResponse;
import android.credentials.CredentialManager;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ICreateCredentialCallback;
import android.credentials.ui.ProviderData;
import android.credentials.ui.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.ArrayList;
@@ -103,11 +101,11 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
setChosenMetric(componentName);
if (response != null) {
mChosenProviderMetric.setChosenProviderStatus(
- METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+ ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
respondToClientWithResponseAndFinish(response);
} else {
mChosenProviderMetric.setChosenProviderStatus(
- METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+ ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
"Invalid response");
}
@@ -144,18 +142,18 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
}
if (isSessionCancelled()) {
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onResponse(response);
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_SUCCESS);
+ ApiStatus.SUCCESS);
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client: " + e.getMessage());
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_FAILURE);
+ ApiStatus.FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -168,7 +166,7 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
}
if (isSessionCancelled()) {
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -184,10 +182,10 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
private void logFailureOrUserCancel(String errorType) {
if (CreateCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
logApiCall(ApiName.CREATE_CREDENTIAL,
- /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+ /* apiStatus */ ApiStatus.USER_CANCELED);
} else {
logApiCall(ApiName.CREATE_CREDENTIAL,
- /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+ /* apiStatus */ ApiStatus.FAILURE);
}
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 41ae9118d965..9c870050449c 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -36,15 +36,14 @@ import android.credentials.CreateCredentialRequest;
import android.credentials.CredentialDescription;
import android.credentials.CredentialManager;
import android.credentials.CredentialOption;
+import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.IClearCredentialStateCallback;
import android.credentials.ICreateCredentialCallback;
import android.credentials.ICredentialManager;
import android.credentials.IGetCredentialCallback;
-import android.credentials.IListEnabledProvidersCallback;
import android.credentials.ISetEnabledProvidersCallback;
-import android.credentials.ListEnabledProvidersResponse;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
import android.credentials.ui.IntentFactory;
@@ -56,7 +55,7 @@ import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -118,10 +117,11 @@ public final class CredentialManagerService
int resolvedUserId) {
List<CredentialManagerServiceImpl> services = new ArrayList<>();
List<CredentialProviderInfo> serviceInfos =
- CredentialProviderInfo.getAvailableSystemServices(
+ CredentialProviderInfoFactory.getAvailableSystemServices(
mContext,
resolvedUserId,
- /* disableSystemAppVerificationForTests= */ false);
+ /* disableSystemAppVerificationForTests= */ false,
+ new HashSet<>());
serviceInfos.forEach(
info -> {
services.add(
@@ -222,10 +222,16 @@ public final class CredentialManagerService
return hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- private void verifyPermission(String permission) throws SecurityException {
- if (!hasPermission(permission)) {
- throw new SecurityException("Caller is missing permission: " + permission);
+ private void verifyGetProvidersPermission() throws SecurityException {
+ if (hasPermission(android.Manifest.permission.QUERY_ALL_PACKAGES)) {
+ return;
}
+
+ if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
+ return;
+ }
+
+ throw new SecurityException("Caller is missing permission: QUERY_ALL_PACKAGES or LIST_ENABLED_CREDENTIAL_PROVIDERS");
}
private boolean hasPermission(String permission) {
@@ -550,40 +556,6 @@ public final class CredentialManagerService
providerSessions.forEach(ProviderSession::invokeSession);
}
- @SuppressWarnings("GuardedBy") // ErrorProne requires listEnabledProviders
- // to be guarded by 'service.mLock', which is the same as mLock.
- @Override
- public ICancellationSignal listEnabledProviders(IListEnabledProvidersCallback callback) {
- Log.i(TAG, "listEnabledProviders");
- ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-
- if (!hasWriteSecureSettingsPermission()) {
- try {
- callback.onError(
- PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
- } catch (RemoteException e) {
- Log.e(TAG, "Issue with invoking response: " + e.getMessage());
- }
- return cancelTransport;
- }
-
- List<String> enabledProviders = new ArrayList<>();
- runForUser(
- (service) -> {
- enabledProviders.add(service.getComponentName().flattenToString());
- });
-
- // Call the callback.
- try {
- callback.onResponse(ListEnabledProvidersResponse.create(enabledProviders));
- } catch (RemoteException e) {
- Log.i(TAG, "Issue with invoking response: " + e.getMessage());
- // TODO: Propagate failure
- }
-
- return cancelTransport;
- }
-
@Override
public void setEnabledProviders(
List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
@@ -659,7 +631,7 @@ public final class CredentialManagerService
// The component name and the package name do not match.
MetricUtilities.logApiCalled(
ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.METRICS_API_STATUS_FAILURE, callingUid);
+ ApiStatus.FAILURE, callingUid);
Log.w(
TAG,
"isEnabledCredentialProviderService: Component name does not"
@@ -667,7 +639,7 @@ public final class CredentialManagerService
return false;
}
MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.METRICS_API_STATUS_SUCCESS, callingUid);
+ ApiStatus.SUCCESS, callingUid);
return true;
}
}
@@ -677,20 +649,35 @@ public final class CredentialManagerService
}
@Override
- public List<ServiceInfo> getCredentialProviderServices(
- int userId, boolean disableSystemAppVerificationForTests, int providerFilter) {
+ public List<CredentialProviderInfo> getCredentialProviderServices(
+ int userId, int providerFilter) {
Log.i(TAG, "getCredentialProviderServices");
- verifyPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS);
-
- List<ServiceInfo> services = new ArrayList<>();
- List<CredentialProviderInfo> providers =
- CredentialProviderInfo.getCredentialProviderServices(
- mContext, userId, disableSystemAppVerificationForTests, providerFilter);
- for (CredentialProviderInfo p : providers) {
- services.add(p.getServiceInfo());
- }
+ verifyGetProvidersPermission();
- return services;
+ return CredentialProviderInfoFactory.getCredentialProviderServices(
+ mContext, userId, providerFilter, getEnabledProviders());
+ }
+
+ @Override
+ public List<CredentialProviderInfo> getCredentialProviderServicesForTesting(
+ int providerFilter) {
+ Log.i(TAG, "getCredentialProviderServicesForTesting");
+ verifyGetProvidersPermission();
+
+ final int userId = UserHandle.getCallingUserId();
+ return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting(
+ mContext, userId, providerFilter, getEnabledProviders());
+ }
+
+ private Set<ServiceInfo> getEnabledProviders() {
+ Set<ServiceInfo> enabledProviders = new HashSet<>();
+ synchronized (mLock) {
+ runForUser(
+ (service) -> {
+ enabledProviders.add(service.getCredentialProviderInfo().getServiceInfo());
+ });
+ }
+ return enabledProviders;
}
@Override
@@ -828,11 +815,11 @@ public final class CredentialManagerService
}
private List<CredentialProviderInfo> getServicesForCredentialDescription(int userId) {
- return CredentialProviderInfo.getCredentialProviderServices(
+ return CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
userId,
- /* disableSystemAppVerificationForTests= */ false,
- CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
+ CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS,
+ new HashSet<>());
}
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index 546c48fe05f4..ee55a1ccc357 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -21,7 +21,8 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.service.credentials.CredentialProviderInfo;
+import android.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
import android.util.Log;
import android.util.Slog;
@@ -82,7 +83,7 @@ public final class CredentialManagerServiceImpl extends
Log.i(TAG, "newServiceInfoLocked with null mInfo , "
+ serviceComponent.getPackageName());
}
- mInfo = new CredentialProviderInfo(
+ mInfo = CredentialProviderInfoFactory.create(
getContext(), serviceComponent,
mUserId, /*isSystemProvider=*/false);
return mInfo.getServiceInfo();
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 2c6c0d8b4018..546c37ff95af 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.credentials.CredentialManager;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ui.DisabledProviderData;
import android.credentials.ui.IntentFactory;
import android.credentials.ui.ProviderData;
@@ -31,11 +32,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ResultReceiver;
-import android.service.credentials.CredentialProviderInfo;
+import android.service.credentials.CredentialProviderInfoFactory;
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -118,11 +120,11 @@ public class CredentialManagerUi {
.map(ProviderData::getProviderFlattenedComponentName)
.collect(Collectors.toUnmodifiableSet());
Set<String> allProviders =
- CredentialProviderInfo.getCredentialProviderServices(
+ CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
mUserId,
- /* disableSystemAppVerificationForTests= */ false,
- CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)
+ CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY,
+ new HashSet<>())
.stream()
.map(CredentialProviderInfo::getServiceInfo)
.map(ServiceInfo::getComponentName)
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 13f4b542a83e..8c6e5cea3bba 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -16,12 +16,10 @@
package com.android.server.credentials;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
-
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
@@ -31,11 +29,11 @@ import android.credentials.ui.RequestInfo;
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.ArrayList;
@@ -95,11 +93,11 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
setChosenMetric(componentName);
if (response != null) {
mChosenProviderMetric.setChosenProviderStatus(
- METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
+ ProviderStatusForMetrics.FINAL_SUCCESS.getMetricCode());
respondToClientWithResponseAndFinish(response);
} else {
mChosenProviderMetric.setChosenProviderStatus(
- METRICS_PROVIDER_STATUS_FINAL_FAILURE);
+ ProviderStatusForMetrics.FINAL_FAILURE.getMetricCode());
respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
"Invalid response from provider");
}
@@ -120,18 +118,18 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
}
if (isSessionCancelled()) {
logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onResponse(response);
logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_SUCCESS);
+ ApiStatus.SUCCESS);
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client with a response : " + e.getMessage());
logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_FAILURE);
+ ApiStatus.FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -143,7 +141,7 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
}
if (isSessionCancelled()) {
logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
- ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
+ ApiStatus.CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -160,10 +158,10 @@ public final class GetRequestSession extends RequestSession<GetCredentialRequest
private void logFailureOrUserCancel(String errorType) {
if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
logApiCall(ApiName.GET_CREDENTIAL,
- /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+ /* apiStatus */ ApiStatus.USER_CANCELED);
} else {
logApiCall(ApiName.GET_CREDENTIAL,
- /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+ /* apiStatus */ ApiStatus.FAILURE);
}
}
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index e7b0a2d9f731..880ae6d4201e 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -16,12 +16,6 @@
package com.android.server.credentials;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -46,18 +40,6 @@ public class MetricUtilities {
public static final int DEFAULT_INT_32 = -1;
public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
- // Metrics constants TODO(b/269290341) migrate to enums eventually to improve
- protected static final int METRICS_PROVIDER_STATUS_FINAL_FAILURE =
- CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
- protected static final int METRICS_PROVIDER_STATUS_QUERY_FAILURE =
- CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
- protected static final int METRICS_PROVIDER_STATUS_FINAL_SUCCESS =
- CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
- protected static final int METRICS_PROVIDER_STATUS_QUERY_SUCCESS =
- CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
- protected static final int METRICS_PROVIDER_STATUS_UNKNOWN =
- CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
-
/**
* This retrieves the uid of any package name, given a context and a component name for the
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index 941d9ad26dca..b7a4cd581a1a 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -20,11 +20,11 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.credentials.ClearCredentialStateException;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ui.ProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.ClearCredentialStateRequest;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import android.util.Slog;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 17caba5831e8..640cc3331b1a 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.credentials.CreateCredentialException;
import android.credentials.CreateCredentialResponse;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ui.CreateCredentialProviderData;
import android.credentials.ui.Entry;
import android.credentials.ui.ProviderPendingIntentResponse;
@@ -33,7 +34,6 @@ import android.service.credentials.BeginCreateCredentialResponse;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.CreateCredentialRequest;
import android.service.credentials.CreateEntry;
-import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.RemoteEntry;
import android.util.Log;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index b8b11ebd13f0..07e2f877bfc0 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -23,6 +23,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.credentials.CredentialOption;
+import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialResponse;
import android.credentials.ui.AuthenticationEntry;
@@ -35,7 +36,6 @@ import android.service.credentials.BeginGetCredentialRequest;
import android.service.credentials.BeginGetCredentialResponse;
import android.service.credentials.CallingAppInfo;
import android.service.credentials.CredentialEntry;
-import android.service.credentials.CredentialProviderInfo;
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 53ed070e3e49..a85769572972 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -16,9 +16,6 @@
package com.android.server.credentials;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_SUCCESS;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,14 +24,15 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.credentials.Credential;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ui.ProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.os.ICancellationSignal;
import android.os.RemoteException;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.UUID;
@@ -205,9 +203,11 @@ public abstract class ProviderSession<T, R>
mCandidateProviderMetric
.setQueryFinishTimeNanoseconds(System.nanoTime());
if (isTerminatingStatus(status)) {
- mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_FAILURE);
+ mCandidateProviderMetric.setProviderQueryStatus(ProviderStatusForMetrics.QUERY_FAILURE
+ .getMetricCode());
} else if (isCompletionStatus(status)) {
- mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_SUCCESS);
+ mCandidateProviderMetric.setProviderQueryStatus(ProviderStatusForMetrics.QUERY_SUCCESS
+ .getMetricCode());
}
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index abfb2107c4a3..c1f35d0f8195 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialProviderInfo;
import android.credentials.ui.ProviderData;
import android.credentials.ui.UserSelectionDialogResult;
import android.os.Binder;
@@ -30,7 +31,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.service.credentials.CallingAppInfo;
-import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
import com.android.internal.R;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
index 36a1f2df6d24..22cab707387d 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
@@ -22,11 +22,11 @@ import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
public enum ApiStatus {
- METRICS_API_STATUS_SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS),
- METRICS_API_STATUS_FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE),
- METRICS_API_STATUS_CLIENT_CANCELED(
+ SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS),
+ FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE),
+ CLIENT_CANCELED(
CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED),
- METRICS_API_STATUS_USER_CANCELED(
+ USER_CANCELED(
CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED);
private final int mInnerMetricCode;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
index f49995d041aa..9f438ecc1146 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
@@ -18,6 +18,9 @@ package com.android.server.credentials.metrics;
/**
* The central candidate provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
* TODO(b/270403549) - iterate on this in V3+
*/
public class CandidateProviderMetric {
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
index 75fdc567013c..03102558d21b 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -22,6 +22,9 @@ import com.android.server.credentials.MetricUtilities;
/**
* The central chosen provider metric object that mimics our defined metric setup.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
* TODO(b/270403549) - iterate on this in V3+
*/
public class ChosenProviderMetric {
@@ -65,12 +68,12 @@ public class ChosenProviderMetric {
/**
* In order for a chosen provider to be selected, the call must have successfully begun.
- * Thus, the {@link PreCandidateMetric} can directly pass this initial latency figure into
+ * Thus, the {@link InitialPhaseMetric} can directly pass this initial latency figure into
* this chosen provider metric.
*
* @param preQueryPhaseLatencyMicroseconds the millisecond latency for the service start,
* typically passed in through the
- * {@link PreCandidateMetric}
+ * {@link InitialPhaseMetric}
*/
public void setPreQueryPhaseLatencyMicroseconds(int preQueryPhaseLatencyMicroseconds) {
mPreQueryPhaseLatencyMicroseconds = preQueryPhaseLatencyMicroseconds;
@@ -112,7 +115,7 @@ public class ChosenProviderMetric {
/**
* Returns the full (platform invoked to response) latency in microseconds. Expects the
- * start time to be provided, such as from {@link PreCandidateMetric}.
+ * start time to be provided, such as from {@link InitialPhaseMetric}.
*/
public int getEntireLatencyMicroseconds() {
return (int) ((this.mFinalFinishTimeNanoseconds
@@ -123,11 +126,11 @@ public class ChosenProviderMetric {
/**
* In order for a chosen provider to be selected, the call must have successfully begun.
- * Thus, the {@link PreCandidateMetric} can directly pass this initial timestamp into this
+ * Thus, the {@link InitialPhaseMetric} can directly pass this initial timestamp into this
* chosen provider metric.
*
* @param serviceBeganTimeNanoseconds the timestamp moment when the platform was called,
- * typically passed in through the {@link PreCandidateMetric}
+ * typically passed in through the {@link InitialPhaseMetric}
*/
public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
@@ -188,8 +191,6 @@ public class ChosenProviderMetric {
- this.mServiceBeganTimeNanoseconds) / 1000);
}
-
-
/* ----------- Provider Status -------------- */
public int getChosenProviderStatus() {
diff --git a/services/credentials/java/com/android/server/credentials/metrics/PreCandidateMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 952328f6ba3a..5f062b05df67 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/PreCandidateMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -18,19 +18,34 @@ package com.android.server.credentials.metrics;
/**
* This handles metrics collected prior to any remote calls to providers.
+ * Some types are redundant across these metric collectors, but that has debug use-cases as
+ * these data-types are available at different moments of the flow (and typically, one can feed
+ * into the next).
* TODO(b/270403549) - iterate on this in V3+
*/
-public class PreCandidateMetric {
-
+public class InitialPhaseMetric {
private static final String TAG = "PreCandidateMetric";
+ // The api being called, default set to unknown
+ private int mApiName = ApiName.UNKNOWN.getMetricCode();
+ // The caller uid of the calling application, default to -1
+ private int mCallerUid = -1;
+ // The session id to unite multiple atom emits, default to -1
+ private long mSessionId = -1;
+ // A sequence id to order united emits, default to -1
+ private int mSequenceId = -1;
+ private int mCountRequestClassType = -1;
+
// Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
// reference point.
-
private long mCredentialServiceStartedTimeNanoseconds = -1;
+
+ // A reference point to give this object utility to capture latency. Can be directly handed
+ // over to the next latency object.
private long mCredentialServiceBeginQueryTimeNanoseconds = -1;
- public PreCandidateMetric() {
+
+ public InitialPhaseMetric() {
}
/* ---------- Latencies ---------- */
@@ -62,4 +77,54 @@ public class PreCandidateMetric {
public long getCredentialServiceBeginQueryTimeNanoseconds() {
return mCredentialServiceBeginQueryTimeNanoseconds;
}
+
+ /* ------ ApiName ------ */
+
+ public void setApiName(int apiName) {
+ mApiName = apiName;
+ }
+
+ public int getApiName() {
+ return mApiName;
+ }
+
+ /* ------ CallerUid ------ */
+
+ public void setCallerUid(int callerUid) {
+ mCallerUid = callerUid;
+ }
+
+ public int getCallerUid() {
+ return mCallerUid;
+ }
+
+ /* ------ SessionId ------ */
+
+ public void setSessionId(long sessionId) {
+ mSessionId = sessionId;
+ }
+
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ /* ------ SequenceId ------ */
+
+ public void setSequenceId(int sequenceId) {
+ mSequenceId = sequenceId;
+ }
+
+ public int getSequenceId() {
+ return mSequenceId;
+ }
+
+ /* ------ Count Request Class Types ------ */
+
+ public void setCountRequestClassType(int countRequestClassType) {
+ mCountRequestClassType = countRequestClassType;
+ }
+
+ public int getCountRequestClassType() {
+ return mCountRequestClassType;
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
new file mode 100644
index 000000000000..08f1afa2f438
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
@@ -0,0 +1,52 @@
+/*
+ * 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.credentials.metrics;
+
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
+
+public enum ProviderStatusForMetrics {
+
+ UNKNOWN(
+ CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN),
+ FINAL_FAILURE(
+ CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE),
+ QUERY_FAILURE(
+ CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE),
+ FINAL_SUCCESS(
+ CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS),
+ QUERY_SUCCESS(
+ CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS);
+
+ private final int mInnerMetricCode;
+
+ ProviderStatusForMetrics(int innerMetricCode) {
+ this.mInnerMetricCode = innerMetricCode;
+ }
+
+ /**
+ * Gives the West-world version of the metric name.
+ *
+ * @return a code corresponding to the west world metric name
+ */
+ public int getMetricCode() {
+ return this.mInnerMetricCode;
+ }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 71492656913b..6861c2f049fb 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -108,6 +108,7 @@
<uses-permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" />
<uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
+ <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
<queries>
<package android:name="com.android.servicestests.apps.suspendtestapp" />
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index de1c2195fcdb..94d30bb4440b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -24,6 +24,8 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_D
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -32,8 +34,10 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -61,10 +65,13 @@ import android.hardware.display.HdrConversionMode;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
+import android.media.projection.IMediaProjectionManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
+import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -158,25 +165,40 @@ public class DisplayManagerServiceTest {
}
};
- class BasicInjector extends DisplayManagerService.Injector {
- @Override
- VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
- (String name, boolean secure, float refreshRate) -> mMockDisplayToken);
- }
-
- @Override
- LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener, new LocalDisplayAdapter.Injector() {
- @Override
- public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
- return mSurfaceControlProxy;
- }
- });
- }
+ class BasicInjector extends DisplayManagerService.Injector {
+ @Override
+ IMediaProjectionManager getProjectionService() {
+ return mMockProjectionService;
+ }
+
+ @Override
+ VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ new VirtualDisplayAdapter.SurfaceControlDisplayFactory() {
+ @Override
+ public IBinder createDisplay(String name, boolean secure,
+ float requestedRefreshRate) {
+ return mMockDisplayToken;
+ }
+
+ @Override
+ public void destroyDisplay(IBinder displayToken) {
+ }
+ });
+ }
+
+ @Override
+ LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new LocalDisplayAdapter(syncRoot, context, handler,
+ displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ @Override
+ public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
+ return mSurfaceControlProxy;
+ }
+ });
+ }
@Override
int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
@@ -198,6 +220,7 @@ public class DisplayManagerServiceTest {
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
+ @Mock IMediaProjectionManager mMockProjectionService;
@Mock IVirtualDeviceManager mIVirtualDeviceManager;
@Mock InputManagerInternal mMockInputManagerInternal;
@Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
@@ -285,6 +308,7 @@ public class DisplayManagerServiceTest {
builder.setFlags(flags);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -410,6 +434,7 @@ public class DisplayManagerServiceTest {
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -446,6 +471,7 @@ public class DisplayManagerServiceTest {
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
/* projection= */ null, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -479,6 +505,7 @@ public class DisplayManagerServiceTest {
builder.setUniqueId(uniqueId);
int displayId = bs.createVirtualDisplay(builder.build(), /* callback= */ mMockAppToken,
/* projection= */ null, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -720,6 +747,7 @@ public class DisplayManagerServiceTest {
builder.setUniqueId(uniqueId);
final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
// The second virtual display requests to mirror the first virtual display.
final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -731,6 +759,7 @@ public class DisplayManagerServiceTest {
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
mMockAppToken2 /* callback */, null /* projection */,
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
// flush the handler
@@ -768,6 +797,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
// Create a second virtual display. This should be added to the previously created display
@@ -783,6 +813,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
assertEquals(
@@ -820,6 +851,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
int displayGroupId1 = localService.getDisplayInfo(displayId1).displayGroupId;
// Create a second virtual display. With the flag VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP,
@@ -838,6 +870,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
int displayGroupId2 = localService.getDisplayInfo(displayId2).displayGroupId;
assertNotEquals(
@@ -881,6 +914,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
// Check that FLAG_ALWAYS_UNLOCKED is set.
assertNotEquals(
@@ -906,6 +940,7 @@ public class DisplayManagerServiceTest {
virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
// Check that FLAG_ALWAYS_UNLOCKED is set.
assertNotEquals(
@@ -929,6 +964,7 @@ public class DisplayManagerServiceTest {
null /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class),
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
// Check that FLAG_ALWAYS_UNLOCKED is not set.
assertEquals(
@@ -960,6 +996,7 @@ public class DisplayManagerServiceTest {
.setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
// The second virtual display requests to mirror the first virtual display.
final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
@@ -971,6 +1008,7 @@ public class DisplayManagerServiceTest {
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
mMockAppToken2 /* callback */, null /* projection */,
PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
// flush the handler
@@ -985,6 +1023,54 @@ public class DisplayManagerServiceTest {
Display.INVALID_DISPLAY);
}
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSessionSuccess() throws Exception {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockWindowManagerInternal
+ .setContentRecordingSession(any(ContentRecordingSession.class)))
+ .thenReturn(true);
+
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession true");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ assertThat(displayId).isNotEqualTo(Display.INVALID_DISPLAY);
+ }
+
+ @Test
+ public void testCreateVirtualDisplay_setContentRecordingSessionFail() throws Exception {
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ when(mMockWindowManagerInternal
+ .setContentRecordingSession(any(ContentRecordingSession.class)))
+ .thenReturn(false);
+
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, 600, 800, 320);
+ builder.setUniqueId("uniqueId --- setContentRecordingSession false");
+ builder.setContentRecordingSession(
+ ContentRecordingSession.createDisplaySession(new Binder("")));
+
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+ displayManager.windowManagerAndInputReady();
+
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ final int displayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ assertThat(displayId).isEqualTo(Display.INVALID_DISPLAY);
+ }
+
/**
* Tests that the virtual display is created with
* {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
@@ -1011,6 +1097,7 @@ public class DisplayManagerServiceTest {
builder.setUniqueId(uniqueId);
final int displayId = binderService.createVirtualDisplay(builder.build(),
mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1043,6 +1130,7 @@ public class DisplayManagerServiceTest {
int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */,
null /* projection */, PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
@@ -1093,7 +1181,6 @@ public class DisplayManagerServiceTest {
registerDefaultDisplays(displayManager);
- DisplayManagerService.BinderService bs = displayManager.new BinderService();
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
@@ -1111,6 +1198,7 @@ public class DisplayManagerServiceTest {
int displayId = localService.createVirtualDisplay(builder.build(),
mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,
mock(DisplayWindowPolicyController.class), PACKAGE_NAME);
+ verify(mMockWindowManagerInternal, never()).setContentRecordingSession(Mockito.any());
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 178670e678f0..397d7b5f2a3e 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -21,7 +21,6 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
-import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
import static com.google.common.truth.Truth.assertThat;
@@ -66,6 +65,7 @@ public class CpuWakeupStatsTest {
public void removesOldWakeups() {
// The xml resource doesn't matter for this test.
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1, mHandler);
+ final long retention = obj.mConfig.WAKEUP_STATS_RETENTION_MS;
final Set<Long> timestamps = new HashSet<>();
final long firstWakeup = 453192;
@@ -73,22 +73,21 @@ public class CpuWakeupStatsTest {
obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ);
timestamps.add(firstWakeup);
for (int i = 1; i < 1000; i++) {
- final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
+ final long delta = mRandom.nextLong(retention);
if (timestamps.add(firstWakeup + delta)) {
obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ);
}
}
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
- obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231,
+ obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231,
KERNEL_REASON_UNKNOWN_IRQ);
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
for (int i = 0; i < 100; i++) {
- final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
+ final long now = mRandom.nextLong(retention + 1, 100 * retention);
obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ);
- assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
- .isLessThan(0);
+ assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 19ecf6ab8799..05abf9fd1a8e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -53,7 +53,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
new file mode 100644
index 000000000000..46899f373fcf
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.flicker.launch
+
+import android.tools.common.NavBar
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/** Some assertions will fail because of b/264415996 */
+@FlickerServiceCompatible
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ // TAPL fails on landscape mode b/240916028
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_3BUTTON)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index da985232a1e3..b848e63c9c87 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -93,8 +93,7 @@ class OverrideTaskTransitionTest(val flicker: FlickerTest) {
.then()
// Animation starts, but the app may not be drawn yet which means the Splash
// may be visible.
- .isInvisible(testApp, isOptional = true)
- .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .isSplashScreenVisibleFor(testApp, isOptional = true)
.then()
// App shows up with the custom animation starting at alpha=1.
.isVisible(testApp)