Merge "Use Context in Notification PermissionHelper" into udc-dev
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 9ba94c8..19a4766 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -24,11 +24,9 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
-import android.app.WaitResult;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -70,7 +68,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Perf tests for user life cycle events.
@@ -1262,15 +1259,13 @@
* <p> This should always be used for profiles since profiles cannot be started in foreground.
*/
private void startUserInBackgroundAndWaitForUnlock(int userId) {
- final ProgressWaiter waiter = new ProgressWaiter();
- boolean success = false;
try {
- mIam.startUserInBackgroundWithListener(userId, waiter);
- success = waiter.waitForFinish(TIMEOUT_IN_SECOND);
- } catch (RemoteException e) {
- Log.e(TAG, "startUserInBackgroundAndWaitForUnlock failed", e);
+ attestTrue("Failed to start user " + userId + " in background.",
+ ShellHelper.runShellCommandWithTimeout("am start-user -w " + userId,
+ TIMEOUT_IN_SECOND).startsWith("Success:"));
+ } catch (TimeoutException e) {
+ fail("Could not start user " + userId + " in " + TIMEOUT_IN_SECOND + " seconds");
}
- attestTrue("Failed to start user " + userId + " in background.", success);
}
/** Starts the given user in the foreground. */
@@ -1390,14 +1385,20 @@
* Launches the given package in the given user.
* Make sure the keyguard has been dismissed prior to calling.
*/
- private void startApp(int userId, String packageName) throws RemoteException {
- final Context context = InstrumentationRegistry.getContext();
- final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
- context.getPackageName(), context.getAttributionTag(),
- context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
- null, 0, 0, null, null, userId);
- attestTrue("User " + userId + " failed to start " + packageName,
- result.result == ActivityManager.START_SUCCESS);
+ private void startApp(int userId, String packageName) {
+ final String failMessage = "User " + userId + " failed to start " + packageName;
+ final String component = InstrumentationRegistry.getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName).getComponent().flattenToShortString();
+ try {
+ final String result = ShellHelper.runShellCommandWithTimeout(
+ "am start -W -n " + component + " --user " + userId, TIMEOUT_IN_SECOND);
+ assertTrue(failMessage + ", component=" + component + ", result=" + result,
+ result.contains("Status: ok")
+ && !result.contains("Warning:")
+ && !result.contains("Error:"));
+ } catch (TimeoutException e) {
+ fail(failMessage + " in " + TIMEOUT_IN_SECOND + " seconds");
+ }
}
private class ProgressWaiter extends IProgressListener.Stub {
@@ -1472,7 +1473,8 @@
private void removeUser(int userId) throws RemoteException {
stopUserAfterWaitingForBroadcastIdle(userId, true);
try {
- runShellCommandWithTimeout("pm remove-user -w " + userId, TIMEOUT_IN_SECOND);
+ ShellHelper.runShellCommandWithTimeout("pm remove-user -w " + userId,
+ TIMEOUT_IN_SECOND);
} catch (TimeoutException e) {
Log.e(TAG, String.format("Could not remove user %d in %d seconds",
userId, TIMEOUT_IN_SECOND), e);
@@ -1539,7 +1541,7 @@
private void waitForBroadcastIdle() {
try {
- runShellCommandWithTimeout("am wait-for-broadcast-idle", TIMEOUT_IN_SECOND);
+ ShellHelper.runShellCommandWithTimeout("am wait-for-broadcast-idle", TIMEOUT_IN_SECOND);
} catch (TimeoutException e) {
Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
}
@@ -1558,41 +1560,4 @@
waitForBroadcastIdle();
sleep(tenSeconds);
}
-
- /**
- * Runs a Shell command with a timeout, returning a trimmed response.
- */
- private String runShellCommandWithTimeout(String command, long timeoutInSecond)
- throws TimeoutException {
- AtomicReference<Exception> exception = new AtomicReference<>(null);
- AtomicReference<String> result = new AtomicReference<>(null);
-
- CountDownLatch latch = new CountDownLatch(1);
-
- new Thread(() -> {
- try {
- result.set(ShellHelper.runShellCommandRaw(command));
- } catch (Exception e) {
- exception.set(e);
- } finally {
- latch.countDown();
- }
- }).start();
-
- try {
- if (!latch.await(timeoutInSecond, TimeUnit.SECONDS)) {
- throw new TimeoutException("Command: '" + command + "' could not run in "
- + timeoutInSecond + " seconds");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
-
- if (exception.get() != null) {
- Log.e(TAG, "Command: '" + command + "' failed.", exception.get());
- throw new RuntimeException(exception.get());
- }
-
- return result.get();
- }
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
index 7b52576..a35899a 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -24,6 +24,10 @@
import androidx.test.InstrumentationRegistry;
import java.io.FileInputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Provides Shell-based utilities such as running a command.
@@ -31,6 +35,43 @@
public final class ShellHelper {
/**
+ * Runs a Shell command with a timeout, returning a trimmed response.
+ */
+ @NonNull
+ public static String runShellCommandWithTimeout(@NonNull String command, long timeoutInSecond)
+ throws TimeoutException {
+ AtomicReference<Exception> exception = new AtomicReference<>(null);
+ AtomicReference<String> result = new AtomicReference<>(null);
+
+ CountDownLatch latch = new CountDownLatch(1);
+
+ new Thread(() -> {
+ try {
+ result.set(runShellCommandRaw(command));
+ } catch (Exception e) {
+ exception.set(e);
+ } finally {
+ latch.countDown();
+ }
+ }).start();
+
+ try {
+ if (!latch.await(timeoutInSecond, TimeUnit.SECONDS)) {
+ throw new TimeoutException("Command: '" + command + "' could not run in "
+ + timeoutInSecond + " seconds");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ if (exception.get() != null) {
+ throw new AndroidRuntimeException(exception.get());
+ }
+
+ return result.get();
+ }
+
+ /**
* Runs a Shell command, returning a trimmed response.
*/
@NonNull
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f779b4d..b0b3b1f 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -319,6 +319,8 @@
private SensorManager mSensorManager;
private final boolean mUseMotionSensor;
private Sensor mMotionSensor;
+ private final boolean mIsLocationPrefetchEnabled;
+ @Nullable
private LocationRequest mLocationRequest;
private Intent mIdleIntent;
private Bundle mIdleIntentOptions;
@@ -2460,6 +2462,11 @@
return null;
}
+ boolean isLocationPrefetchEnabled() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePrefetchLocation);
+ }
+
boolean useMotionSensor() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModeUseMotionSensor);
@@ -2489,6 +2496,7 @@
mAppStateTracker = mInjector.getAppStateTracker(context,
AppSchedulingModuleThread.get().getLooper());
LocalServices.addService(AppStateTracker.class, mAppStateTracker);
+ mIsLocationPrefetchEnabled = mInjector.isLocationPrefetchEnabled();
mUseMotionSensor = mInjector.useMotionSensor();
}
@@ -2602,8 +2610,7 @@
mMotionSensor = mInjector.getMotionSensor();
}
- if (getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
+ if (mIsLocationPrefetchEnabled) {
mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0)
.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setMaxUpdates(1)
@@ -3779,34 +3786,40 @@
case STATE_SENSING:
cancelSensingTimeoutAlarmLocked();
moveToStateLocked(STATE_LOCATING, reason);
- scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
- LocationManager locationManager = mInjector.getLocationManager();
- if (locationManager != null
- && locationManager.getProvider(LocationManager.FUSED_PROVIDER) != null) {
- locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
- mLocationRequest,
- AppSchedulingModuleThread.getExecutor(),
- mGenericLocationListener);
- mLocating = true;
+ if (mIsLocationPrefetchEnabled) {
+ scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
+ LocationManager locationManager = mInjector.getLocationManager();
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.FUSED_PROVIDER)
+ != null) {
+ locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
+ mLocationRequest,
+ AppSchedulingModuleThread.getExecutor(),
+ mGenericLocationListener);
+ mLocating = true;
+ } else {
+ mHasFusedLocation = false;
+ }
+ if (locationManager != null
+ && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+ mHasGps = true;
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
+ 1000, 5, mGpsLocationListener, mHandler.getLooper());
+ mLocating = true;
+ } else {
+ mHasGps = false;
+ }
+ // If we have a location provider, we're all set, the listeners will move state
+ // forward.
+ if (mLocating) {
+ break;
+ }
+ // Otherwise, we have to move from locating into idle maintenance.
} else {
- mHasFusedLocation = false;
- }
- if (locationManager != null
- && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
- mHasGps = true;
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
- mGpsLocationListener, mHandler.getLooper());
- mLocating = true;
- } else {
- mHasGps = false;
- }
- // If we have a location provider, we're all set, the listeners will move state
- // forward.
- if (mLocating) {
- break;
+ mLocating = false;
}
- // Otherwise, we have to move from locating into idle maintenance.
+ // We're not doing any locating work, so move on to the next state.
case STATE_LOCATING:
cancelAlarmLocked();
cancelLocatingLocked();
@@ -5303,15 +5316,19 @@
pw.print(" "); pw.print(mStationaryListeners.size());
pw.println(" stationary listeners registered");
}
- pw.print(" mLocating="); pw.print(mLocating);
- pw.print(" mHasGps="); pw.print(mHasGps);
- pw.print(" mHasFused="); pw.print(mHasFusedLocation);
- pw.print(" mLocated="); pw.println(mLocated);
- if (mLastGenericLocation != null) {
- pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
- }
- if (mLastGpsLocation != null) {
- pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation);
+ if (mIsLocationPrefetchEnabled) {
+ pw.print(" mLocating="); pw.print(mLocating);
+ pw.print(" mHasGps="); pw.print(mHasGps);
+ pw.print(" mHasFused="); pw.print(mHasFusedLocation);
+ pw.print(" mLocated="); pw.println(mLocated);
+ if (mLastGenericLocation != null) {
+ pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
+ }
+ if (mLastGpsLocation != null) {
+ pw.print(" mLastGpsLocation="); pw.println(mLastGpsLocation);
+ }
+ } else {
+ pw.println(" Location prefetching disabled");
}
pw.print(" mState="); pw.print(stateToString(mState));
pw.print(" mLightState=");
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 430a1e2..4d646de 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -292,6 +292,10 @@
return "permission";
case EXACT_ALLOW_REASON_POLICY_PERMISSION:
return "policy_permission";
+ case EXACT_ALLOW_REASON_LISTENER:
+ return "listener";
+ case EXACT_ALLOW_REASON_PRIORITIZED:
+ return "prioritized";
case EXACT_ALLOW_REASON_NOT_APPLICABLE:
return "N/A";
default:
diff --git a/config/dirty-image-objects b/config/dirty-image-objects
index dfd091c..2584610e 100644
--- a/config/dirty-image-objects
+++ b/config/dirty-image-objects
@@ -28,359 +28,270 @@
# Then, grep for lines containing "Private dirty object" from the output.
# This particular file was generated by dumping systemserver and systemui.
#
-Landroid/accounts/Account;
-Landroid/accounts/OnAccountsUpdateListener;
Landroid/animation/LayoutTransition;
Landroid/app/ActivityManager;
-Landroid/app/ActivityManager$OnUidImportanceListener;
Landroid/app/ActivityTaskManager;
Landroid/app/ActivityThread;
-Landroid/app/admin/DevicePolicyManager;
Landroid/app/AlarmManager;
-Landroid/app/Application;
Landroid/app/AppOpsManager;
-Landroid/app/backup/BackupManager;
Landroid/app/ContextImpl;
-Landroid/app/INotificationManager;
-Landroid/app/Notification$BigPictureStyle;
-Landroid/app/Notification$BigTextStyle;
-Landroid/app/Notification$InboxStyle;
-Landroid/app/NotificationChannel;
-Landroid/app/NotificationChannelGroup;
+Landroid/app/Notification;
Landroid/app/NotificationManager;
-Landroid/app/PendingIntent;
-Landroid/app/PendingIntent$OnFinished;
+Landroid/app/PendingIntent$FinishedDispatcher;
+Landroid/app/PropertyInvalidatedCache$NoPreloadHolder;
Landroid/app/QueuedWork;
Landroid/app/ResourcesManager;
+Landroid/app/SystemServiceRegistry;
Landroid/app/WallpaperManager;
-Landroid/app/WindowConfiguration;
-Landroid/bluetooth/BluetoothAdapter;
-Landroid/bluetooth/BluetoothDevice;
-Landroid/bluetooth/BluetoothProfile;
-Landroid/bluetooth/IBluetoothA2dp;
-Landroid/bluetooth/IBluetoothHeadsetPhone;
-Landroid/bluetooth/IBluetoothHidDevice;
-Landroid/bluetooth/IBluetoothHidHost;
-Landroid/bluetooth/IBluetoothMap;
-Landroid/bluetooth/IBluetoothPan;
-Landroid/bluetooth/IBluetoothPbap;
-Landroid/bluetooth/IBluetoothSap;
-Landroid/content/ClipboardManager$OnPrimaryClipChangedListener;
-Landroid/content/ComponentName;
-Landroid/content/ContentProvider$PipeDataWriter;
+Landroid/app/backup/BackupManager;
+Landroid/compat/Compatibility;
+Landroid/content/AsyncQueryHandler;
+Landroid/content/ContentProviderClient;
Landroid/content/ContentResolver;
Landroid/content/Context;
-Landroid/content/Intent;
-Landroid/content/pm/PackageManager$OnPermissionsChangedListener;
-Landroid/content/pm/VersionedPackage;
-Landroid/content/res/Configuration;
-Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener;
+Landroid/content/pm/PackageItemInfo;
+Landroid/content/pm/UserPackage;
+Landroid/content/res/ResourceTimer;
Landroid/database/CursorWindow;
Landroid/database/sqlite/SQLiteCompatibilityWalFlags;
-Landroid/database/sqlite/SQLiteDatabase$CursorFactory;
+Landroid/database/sqlite/SQLiteDebug$NoPreloadHolder;
Landroid/database/sqlite/SQLiteGlobal;
-Landroid/database/sqlite/SQLiteTransactionListener;
Landroid/ddm/DdmHandleAppName;
Landroid/graphics/Bitmap;
Landroid/graphics/Canvas;
-Landroid/graphics/drawable/AdaptiveIconDrawable;
-Landroid/graphics/drawable/ColorDrawable;
-Landroid/graphics/drawable/GradientDrawable;
-Landroid/graphics/drawable/Icon;
-Landroid/graphics/drawable/InsetDrawable;
-Landroid/graphics/drawable/RippleDrawable;
-Landroid/graphics/drawable/VectorDrawable$VGroup;
-Landroid/graphics/ImageDecoder;
-Landroid/graphics/Rect;
+Landroid/graphics/Compatibility;
+Landroid/graphics/HardwareRenderer;
Landroid/graphics/TemporaryBuffer;
-Landroid/hardware/biometrics/BiometricSourceType;
-Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal;
-Landroid/hardware/display/DisplayManagerGlobal;
-Landroid/hardware/display/NightDisplayListener$Callback;
-Landroid/hardware/input/InputManager;
-Landroid/hardware/input/InputManager$InputDeviceListener;
+Landroid/graphics/Typeface;
+Landroid/graphics/drawable/AdaptiveIconDrawable;
Landroid/hardware/SensorPrivacyManager;
Landroid/hardware/SystemSensorManager;
-Landroid/icu/impl/OlsonTimeZone;
-Landroid/icu/text/BreakIterator;
+Landroid/hardware/devicestate/DeviceStateManagerGlobal;
+Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal;
+Landroid/hardware/display/DisplayManagerGlobal;
+Landroid/hardware/input/InputManagerGlobal;
+Landroid/hardware/location/GeofenceHardwareImpl;
+Landroid/icu/impl/number/range/StandardPluralRanges;
Landroid/icu/text/Collator;
-Landroid/icu/text/DateFormat$BooleanAttribute;
-Landroid/icu/text/DateTimePatternGenerator$DTPGflags;
-Landroid/icu/text/PluralRules$Operand;
Landroid/icu/util/TimeZone;
-Landroid/location/GpsStatus$Listener;
-Landroid/location/LocationListener;
+Landroid/location/LocationManager;
Landroid/media/AudioManager;
+Landroid/media/AudioPlaybackConfiguration;
+Landroid/media/AudioSystem;
+Landroid/media/MediaCodec;
+Landroid/media/MediaCodecList;
+Landroid/media/MediaFrameworkPlatformInitializer;
+Landroid/media/MediaRouter2Manager;
Landroid/media/MediaRouter;
Landroid/media/PlayerBase;
-Landroid/media/session/MediaSessionManager;
-Landroid/net/apf/ApfCapabilities;
-Landroid/net/ConnectivityManager;
-Landroid/net/ConnectivityManager$OnNetworkActiveListener;
-Landroid/net/ConnectivityThread$Singleton;
-Landroid/net/IpConfiguration$IpAssignment;
-Landroid/net/IpConfiguration$ProxySettings;
-Landroid/net/IpPrefix;
-Landroid/net/LinkAddress;
-Landroid/net/LinkProperties;
-Landroid/net/Network;
-Landroid/net/NetworkCapabilities;
-Landroid/net/NetworkInfo;
-Landroid/net/NetworkInfo$State;
-Landroid/net/NetworkRequest;
-Landroid/net/NetworkRequest$Type;
-Landroid/net/RouteInfo;
-Landroid/net/StringNetworkSpecifier;
-Landroid/net/TrafficStats;
-Landroid/net/UidRange;
-Landroid/net/Uri$HierarchicalUri;
-Landroid/net/Uri$StringUri;
-Landroid/net/wifi/WifiManager;
-Landroid/net/wifi/WifiManager$SoftApCallback;
-Landroid/os/AsyncResult;
+Landroid/media/audiopolicy/AudioProductStrategy;
+Landroid/media/audiopolicy/AudioVolumeGroup;
+Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcFrameworkInitializer;
+Landroid/nfc/cardemulation/CardEmulation;
Landroid/os/AsyncTask;
+Landroid/os/BaseBundle;
+Landroid/os/Binder;
Landroid/os/BinderProxy;
-Landroid/os/Bundle;
-Landroid/os/DeadObjectException;
Landroid/os/Environment;
Landroid/os/FileObserver;
Landroid/os/Handler;
-Landroid/os/IDeviceIdleController;
Landroid/os/LocaleList;
Landroid/os/Looper;
Landroid/os/Message;
-Landroid/os/ParcelUuid;
+Landroid/os/NullVibrator;
+Landroid/os/Parcel;
Landroid/os/Process;
-Landroid/os/RecoverySystem;
Landroid/os/ServiceManager;
-Landroid/os/storage/StorageManager;
Landroid/os/StrictMode;
-Landroid/os/Trace;
+Landroid/os/UEventObserver;
+Landroid/os/UserManager;
Landroid/os/WorkSource;
-Landroid/os/WorkSource$WorkChain;
+Landroid/os/storage/StorageManager;
Landroid/permission/PermissionManager;
+Landroid/provider/DeviceConfigInitializer;
Landroid/provider/FontsContract;
-Landroid/provider/Settings$SettingNotFoundException;
+Landroid/provider/Settings;
+Landroid/renderscript/RenderScript;
Landroid/renderscript/RenderScriptCacheDir;
-Landroid/security/IKeyChainService;
-Landroid/security/keystore/AndroidKeyStoreProvider;
+Landroid/security/keystore2/KeyStoreCryptoOperationUtils;
Landroid/security/net/config/ApplicationConfig;
Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder;
-Landroid/telecom/PhoneAccountHandle;
+Landroid/security/net/config/UserCertificateSource$NoPreloadHolder;
+Landroid/telecom/Log;
+Landroid/telecom/TelecomManager;
Landroid/telephony/AnomalyReporter;
-Landroid/telephony/CellSignalStrengthCdma;
-Landroid/telephony/CellSignalStrengthGsm;
-Landroid/telephony/CellSignalStrengthLte;
-Landroid/telephony/CellSignalStrengthNr;
-Landroid/telephony/CellSignalStrengthTdscdma;
-Landroid/telephony/CellSignalStrengthWcdma;
-Landroid/telephony/DataSpecificRegistrationInfo;
-Landroid/telephony/emergency/EmergencyNumber;
-Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder;
-Landroid/telephony/ims/ImsMmTelManager$RegistrationCallback$RegistrationBinder;
-Landroid/telephony/ims/ImsReasonInfo;
-Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder;
-Landroid/telephony/ModemActivityInfo;
-Landroid/telephony/ModemInfo;
-Landroid/telephony/NetworkRegistrationInfo;
-Landroid/telephony/NetworkService;
+Landroid/telephony/TelephonyFrameworkInitializer;
+Landroid/telephony/TelephonyLocalConnection;
Landroid/telephony/TelephonyManager;
-Landroid/telephony/VoiceSpecificRegistrationInfo;
-Landroid/text/format/DateFormat;
-Landroid/text/method/SingleLineTransformationMethod;
-Landroid/text/Selection$MemoryTextWatcher;
-Landroid/text/SpanWatcher;
-Landroid/text/style/AlignmentSpan;
-Landroid/text/style/CharacterStyle;
-Landroid/text/style/LeadingMarginSpan;
-Landroid/text/style/LineBackgroundSpan;
-Landroid/text/style/LineHeightSpan;
-Landroid/text/style/MetricAffectingSpan;
-Landroid/text/style/ReplacementSpan;
-Landroid/text/style/SuggestionSpan;
-Landroid/text/style/TabStopSpan;
+Landroid/telephony/TelephonyRegistryManager;
+Landroid/text/DynamicLayout;
Landroid/text/TextUtils;
-Landroid/text/TextWatcher;
-Landroid/transition/ChangeClipBounds;
-Landroid/transition/ChangeImageTransform;
-Landroid/transition/ChangeTransform;
+Landroid/text/format/DateFormat;
+Landroid/text/format/DateUtils;
+Landroid/text/method/ArrowKeyMovementMethod;
+Landroid/text/method/LinkMovementMethod;
+Landroid/text/method/SingleLineTransformationMethod;
+Landroid/text/style/ClickableSpan;
+Landroid/timezone/TelephonyLookup;
+Landroid/timezone/TimeZoneFinder;
Landroid/util/ArrayMap;
Landroid/util/ArraySet;
-Landroid/util/DisplayMetrics;
Landroid/util/EventLog;
-Landroid/util/Log;
-Landroid/util/Patterns;
-Landroid/view/AbsSavedState$1;
-Landroid/view/accessibility/AccessibilityManager;
-Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener;
-Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener;
-Landroid/view/accessibility/AccessibilityNodeIdManager;
-Landroid/view/autofill/AutofillManager;
-Landroid/view/autofill/Helper;
+Landroid/util/NtpTrustedTime;
Landroid/view/Choreographer;
-Landroid/view/inputmethod/InputMethodManager;
-Landroid/view/IWindowManager;
+Landroid/view/CrossWindowBlurListeners;
+Landroid/view/DisplayCutout;
+Landroid/view/KeyEvent;
+Landroid/view/MotionEvent;
Landroid/view/PointerIcon;
-Landroid/view/RemoteAnimationAdapter;
-Landroid/view/ThreadedRenderer;
+Landroid/view/RoundedCorners;
+Landroid/view/SurfaceControl;
Landroid/view/View;
-Landroid/view/View$OnHoverListener;
+Landroid/view/ViewGroup$TouchTarget;
Landroid/view/ViewRootImpl;
-Landroid/view/ViewStub;
-Landroid/view/ViewStub$OnInflateListener;
Landroid/view/ViewTreeObserver;
-Landroid/view/WindowManager$LayoutParams;
Landroid/view/WindowManagerGlobal;
-Landroid/widget/ActionMenuPresenter$OverflowMenuButton;
-Landroid/widget/ActionMenuView;
-Landroid/widget/Button;
-Landroid/widget/CheckBox;
-Landroid/widget/FrameLayout;
-Landroid/widget/ImageButton;
+Landroid/view/accessibility/AccessibilityManager;
+Landroid/view/accessibility/AccessibilityNodeIdManager;
+Landroid/view/autofill/Helper;
+Landroid/view/inputmethod/IInputMethodManagerGlobalInvoker;
+Landroid/view/inputmethod/InputMethodManager;
+Landroid/webkit/CookieSyncManager;
+Landroid/webkit/WebView;
+Landroid/webkit/WebViewFactory;
+Landroid/webkit/WebViewZygote;
+Landroid/widget/AbsListView;
Landroid/widget/ImageView;
Landroid/widget/LinearLayout;
-Landroid/widget/RelativeLayout;
-Landroid/widget/SeekBar;
-Landroid/widget/Space;
-Landroid/widget/TextView;
-Landroid/widget/Toolbar;
-[B
-Lcom/android/ims/ImsManager;
+Landroid/widget/Toast;
+Landroid/window/SurfaceSyncGroup;
+Lcom/android/i18n/timezone/TelephonyLookup;
+Lcom/android/i18n/timezone/TimeZoneFinder;
+Lcom/android/internal/config/appcloning/AppCloningDeviceConfigHelper;
+Lcom/android/internal/content/om/OverlayConfig;
+Lcom/android/internal/display/BrightnessSynchronizer;
+Lcom/android/internal/infra/AndroidFuture;
+Lcom/android/internal/inputmethod/ImeTracing;
+Lcom/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry;
+Lcom/android/internal/jank/InteractionJankMonitor$InstanceHolder;
+Lcom/android/internal/jank/InteractionJankMonitor;
Lcom/android/internal/logging/MetricsLogger;
Lcom/android/internal/os/BackgroundThread;
Lcom/android/internal/os/BinderInternal;
-Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener;
+Lcom/android/internal/os/KernelCpuBpfTracking;
Lcom/android/internal/os/RuntimeInit;
Lcom/android/internal/os/SomeArgs;
-Lcom/android/internal/policy/DecorView;
-Lcom/android/internal/statusbar/IStatusBarService;
-Lcom/android/internal/telephony/AppSmsManager;
-Landroid/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;
-Lcom/android/internal/telephony/CarrierActionAgent;
-Lcom/android/internal/telephony/cat/CatService;
-Lcom/android/internal/telephony/cat/IconLoader;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;
-Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;
-Lcom/android/internal/telephony/cdma/EriManager;
-Lcom/android/internal/telephony/CellularNetworkValidator;
-Lcom/android/internal/telephony/CommandException;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataEnabledSettings;
-Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/euicc/EuiccCardController;
-Lcom/android/internal/telephony/euicc/EuiccController;
-Lcom/android/internal/telephony/GsmAlphabet;
-Lcom/android/internal/telephony/GsmCdmaCallTracker;
-Lcom/android/internal/telephony/GsmCdmaPhone;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;
-Lcom/android/internal/telephony/IccSmsInterfaceManager;
-Lcom/android/internal/telephony/ims/ImsResolver;
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;
-Lcom/android/internal/telephony/imsphone/ImsPhone;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;
-Lcom/android/internal/telephony/ims/RcsMessageStoreController;
+Lcom/android/internal/os/ZygoteInit;
+Lcom/android/internal/policy/AttributeCache;
+Lcom/android/internal/protolog/BaseProtoLogImpl;
+Lcom/android/internal/protolog/ProtoLogImpl;
+Lcom/android/internal/statusbar/NotificationVisibility;
+Lcom/android/internal/telephony/CellBroadcastServiceManager;
Lcom/android/internal/telephony/IntentBroadcaster;
-Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;
-Lcom/android/internal/telephony/metrics/TelephonyMetrics;
+Lcom/android/internal/telephony/MccTable;
Lcom/android/internal/telephony/MultiSimSettingController;
-Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierAttribute;
-Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierId;
-Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall;
-Lcom/android/internal/telephony/nano/TelephonyProto$SmsSession$Event;
-Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall;
-Lcom/android/internal/telephony/NitzStateMachine;
+Lcom/android/internal/telephony/PackageChangeReceiver;
Lcom/android/internal/telephony/PhoneConfigurationManager;
Lcom/android/internal/telephony/PhoneFactory;
-Lcom/android/internal/telephony/PhoneSwitcher;
Lcom/android/internal/telephony/ProxyController;
-Lcom/android/internal/telephony/RadioConfig;
-Lcom/android/internal/telephony/RIL;
Lcom/android/internal/telephony/RILRequest;
-Lcom/android/internal/telephony/RilWakelockInfo;
-Lcom/android/internal/telephony/ServiceStateTracker;
-Lcom/android/internal/telephony/SimActivationTracker;
+Lcom/android/internal/telephony/RadioConfig;
+Lcom/android/internal/telephony/RadioInterfaceCapabilityController;
Lcom/android/internal/telephony/SmsApplication;
Lcom/android/internal/telephony/SmsBroadcastUndelivered;
-Lcom/android/internal/telephony/SmsStorageMonitor;
-Lcom/android/internal/telephony/SmsUsageMonitor;
-Lcom/android/internal/telephony/SubscriptionController;
-Lcom/android/internal/telephony/SubscriptionInfoUpdater;
+Lcom/android/internal/telephony/SomeArgs;
Lcom/android/internal/telephony/TelephonyComponentFactory;
Lcom/android/internal/telephony/TelephonyDevController;
-Lcom/android/internal/telephony/TelephonyTester;
-Lcom/android/internal/telephony/uicc/AdnRecordCache;
-Lcom/android/internal/telephony/uicc/UiccCardApplication;
+Lcom/android/internal/telephony/cat/CatService;
+Lcom/android/internal/telephony/cdma/CdmaInboundSmsHandler;
+Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager;
+Lcom/android/internal/telephony/euicc/EuiccCardController;
+Lcom/android/internal/telephony/euicc/EuiccController;
+Lcom/android/internal/telephony/ims/ImsResolver;
+Lcom/android/internal/telephony/metrics/TelephonyMetrics;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CarrierIdMismatch;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularDataServiceSwitch;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$CellularServiceState;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$DataCallSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$EmergencyNumbersInfo;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$GbaEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsDedicatedBearerListenerEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationFeatureTagStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationServiceDescStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$ImsRegistrationTermination;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$IncomingSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequests;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$NetworkRequestsV2;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingShortCodeSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$OutgoingSms;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$PresenceNotifyEvent;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsAcsProvisioningStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$RcsClientProvisioningStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteController;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteIncomingDatagram;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteOutgoingDatagram;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteProvision;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SatelliteSosMessageRecommender;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipDelegateStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipMessageResponse;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportFeatureTagStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$SipTransportSession;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UceEventStats;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$UnmeteredNetworks;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallRatUsage;
+Lcom/android/internal/telephony/nano/PersistAtomsProto$VoiceCallSession;
+Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall;
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall;
+Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyServiceState$NetworkRegistrationInfo;
+Lcom/android/internal/telephony/satellite/PointingAppController;
+Lcom/android/internal/telephony/satellite/SatelliteModemInterface;
Lcom/android/internal/telephony/uicc/UiccController;
-Lcom/android/internal/telephony/uicc/UiccProfile;
Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher;
-Lcom/android/internal/telephony/uicc/UsimFileHandler;
-Lcom/android/internal/telephony/uicc/VoiceMailConstants;
-Lcom/android/internal/util/LatencyTracker;
-Lcom/android/internal/util/StateMachine$SmHandler;
-Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/okio/AsyncTimeout;
-Lcom/android/okhttp/okio/SegmentPool;
+Lcom/android/internal/util/ContrastColorUtil;
+Lcom/android/internal/view/WindowManagerPolicyThread;
+Lcom/android/org/bouncycastle/crypto/CryptoServicesRegistrar;
Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo;
Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo;
-Lcom/android/server/sip/SipWakeupTimer;
-Lcom/android/server/SystemConfig;
+Lcom/android/server/AppWidgetBackupBridge;
Ldalvik/system/BaseDexClassLoader;
Ldalvik/system/BlockGuard;
Ldalvik/system/CloseGuard;
Ldalvik/system/RuntimeHooks;
Ldalvik/system/SocketTagger;
-Ljava/io/BufferedReader;
-Ljava/lang/AssertionError;
-Ljava/lang/Boolean;
-Ljava/lang/Byte;
-Ljava/lang/Character;
-Ljava/lang/CharSequence;
-Ljava/lang/Class;
-Ljava/lang/IllegalAccessException;
-Ljava/lang/IllegalStateException;
-Ljava/lang/NoSuchMethodException;
-Ljava/lang/NullPointerException;
-Ljava/lang/Object;
-[Ljava/lang/Object;
-Ljava/lang/ref/FinalizerReference;
-Ljava/lang/Runnable;
-Ljava/lang/SecurityException;
-Ljava/lang/Short;
-[Ljava/lang/String;
+Ldalvik/system/VMRuntime;
+Ldalvik/system/ZipPathValidator;
+Ldalvik/system/ZygoteHooks;
Ljava/lang/System;
Ljava/lang/Thread;
Ljava/lang/Throwable;
-Ljava/lang/UnsatisfiedLinkError;
-Ljava/net/Inet6Address;
-Ljava/net/Socket;
-Ljava/net/SocketException;
+Ljava/lang/ref/FinalizerReference;
+Ljava/lang/ref/ReferenceQueue;
+Ljava/net/ResponseCache;
Ljava/nio/Bits;
Ljava/nio/charset/Charset;
-Ljava/security/interfaces/RSAPrivateKey;
Ljava/security/Provider;
Ljava/util/Collections;
-Ljava/util/concurrent/Executor;
Ljava/util/GregorianCalendar;
-Ljava/util/Locale;
Ljava/util/Locale$NoImagePreloadHolder;
+Ljava/util/Locale;
Ljava/util/Scanner;
-Ljava/util/Set;
Ljava/util/TimeZone;
+Ljava/util/concurrent/ForkJoinPool;
+Ljava/util/concurrent/ThreadLocalRandom;
+Ljavax/net/ServerSocketFactory;
Ljavax/net/SocketFactory;
-Ljavax/net/ssl/HttpsURLConnection;
Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder;
+Ljavax/net/ssl/HttpsURLConnection;
Ljavax/net/ssl/SSLContext;
-Ljavax/net/ssl/SSLSessionContext;
+Ljavax/net/ssl/SSLServerSocketFactory;
Ljavax/net/ssl/SSLSocketFactory;
Llibcore/io/Libcore;
-Llibcore/io/Memory;
Llibcore/net/NetworkSecurityPolicy;
-Llibcore/timezone/TimeZoneFinder;
-Lorg/apache/http/params/HttpParams;
Lsun/misc/Cleaner;
-Lsun/nio/ch/FileChannelImpl;
Lsun/nio/ch/FileChannelImpl$Unmapper;
-Lsun/nio/fs/UnixChannelFactory;
+Lsun/nio/ch/FileChannelImpl;
Lsun/security/jca/Providers;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ae63816..332c53c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -536,6 +536,12 @@
field @NonNull public static final android.app.admin.DeviceAdminAuthority DEVICE_ADMIN_AUTHORITY;
}
+ public final class DevicePolicyIdentifiers {
+ field public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
+ field public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
+ field public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
+ }
+
public class DevicePolicyManager {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void calculateHasIncompatibleAccounts();
@@ -3783,6 +3789,7 @@
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
+ method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 61d6787..2ae7216 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -403,8 +403,11 @@
private static final String KEY_SPLASH_SCREEN_STYLE =
"android.activity.splashScreenStyle";
- /** See {@link #setTransientLaunch()}. */
- private static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
+ /**
+ * See {@link #setTransientLaunch()}.
+ * @hide
+ */
+ public static final String KEY_TRANSIENT_LAUNCH = "android.activity.transientLaunch";
/** see {@link #makeLaunchIntoPip(PictureInPictureParams)}. */
private static final String KEY_LAUNCH_INTO_PIP_PARAMS =
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 36e5762..3c6ff28 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -137,7 +137,7 @@
* activities inside it belong to a managed profile user, and that user has just
* been locked.
*/
- void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo);
+ void onTaskProfileLocked(in ActivityManager.RunningTaskInfo taskInfo, int userId);
/**
* Called when a task snapshot got updated.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 99a7fa2..705b5ee 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -407,7 +407,7 @@
}
private static void checkPendingIntent(int flags, @NonNull Intent intent,
- @NonNull Context context) {
+ @NonNull Context context, boolean isActivityResultType) {
final boolean isFlagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
final boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
final String packageName = context.getPackageName();
@@ -428,11 +428,12 @@
throw new IllegalArgumentException(msg);
}
- // For apps with target SDK < U, warn that creation or retrieval of a mutable
- // implicit PendingIntent will be blocked from target SDK U onwards for security
- // reasons. The block itself happens on the server side, but this warning has to
- // stay here to preserve the client side stack trace for app developers.
- if (isNewMutableDisallowedImplicitPendingIntent(flags, intent)
+ // For apps with target SDK < U, warn that creation or retrieval of a mutable implicit
+ // PendingIntent that is not of type {@link ActivityManager#INTENT_SENDER_ACTIVITY_RESULT}
+ // will be blocked from target SDK U onwards for security reasons. The block itself
+ // happens on the server side, but this warning has to stay here to preserve the client
+ // side stack trace for app developers.
+ if (isNewMutableDisallowedImplicitPendingIntent(flags, intent, isActivityResultType)
&& !Compatibility.isChangeEnabled(BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT)) {
String msg = "New mutable implicit PendingIntent: pkg=" + packageName
+ ", action=" + intent.getAction()
@@ -445,7 +446,13 @@
/** @hide */
public static boolean isNewMutableDisallowedImplicitPendingIntent(int flags,
- @NonNull Intent intent) {
+ @NonNull Intent intent, boolean isActivityResultType) {
+ if (isActivityResultType) {
+ // Pending intents of type {@link ActivityManager#INTENT_SENDER_ACTIVITY_RESULT}
+ // should be ignored as they are intrinsically tied to a target which means they
+ // are already explicit.
+ return false;
+ }
boolean isFlagNoCreateSet = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
boolean isFlagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
boolean isImplicit = (intent.getComponent() == null) && (intent.getPackage() == null);
@@ -534,7 +541,7 @@
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkPendingIntent(flags, intent, context);
+ checkPendingIntent(flags, intent, context, /* isActivityResultType */ false);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -668,7 +675,7 @@
intents[i].migrateExtraStreamToClipData(context);
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
- checkPendingIntent(flags, intents[i], context);
+ checkPendingIntent(flags, intents[i], context, /* isActivityResultType */ false);
}
try {
IIntentSender target =
@@ -721,7 +728,7 @@
Intent intent, int flags, UserHandle userHandle) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkPendingIntent(flags, intent, context);
+ checkPendingIntent(flags, intent, context, /* isActivityResultType */ false);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
@@ -800,7 +807,7 @@
Intent intent, int flags, int serviceKind) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
- checkPendingIntent(flags, intent, context);
+ checkPendingIntent(flags, intent, context, /* isActivityResultType */ false);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 774bc06..0290cee 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -154,8 +154,18 @@
}
@Override
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId)
+ throws RemoteException {
+ onTaskProfileLocked(taskInfo);
+ }
+
+ /**
+ * @deprecated see {@link #onTaskProfileLocked(RunningTaskInfo, int)}
+ */
+ @Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void onTaskProfileLocked(RunningTaskInfo taskInfo) throws RemoteException {
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo)
+ throws RemoteException {
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index b6e83c8..29f657e 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -19,8 +19,8 @@
import com.android.server.LocalServices;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collections;
+import java.util.Map;
/**
* Stores a copy of the set of device policies maintained by {@link DevicePolicyManager} that
@@ -64,10 +64,11 @@
public abstract boolean canAdminGrantSensorsPermissions();
/**
- * Returns a list of package names for which all launcher shortcuts should be modified to be
- * launched in the managed profile and badged accordingly.
+ * Returns a map of package names to package names, for which all launcher shortcuts which
+ * match a key package name should be modified to launch the corresponding value package
+ * name in the managed profile. The overridden shortcut should be badged accordingly.
*/
- public abstract List<String> getLauncherShortcutOverrides();
+ public abstract Map<String, String> getLauncherShortcutOverrides();
/**
* Empty implementation.
@@ -95,8 +96,8 @@
return false;
}
@Override
- public List<String> getLauncherShortcutOverrides() {
- return new ArrayList<>();
+ public Map<String, String> getLauncherShortcutOverrides() {
+ return Collections.EMPTY_MAP;
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 9b0a70d..aeac59b 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -17,6 +17,7 @@
package android.app.admin;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.os.UserManager;
import java.util.Objects;
@@ -118,6 +119,7 @@
*
* @hide
*/
+ @TestApi
public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
/**
@@ -125,6 +127,7 @@
*
* @hide
*/
+ @TestApi
public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
/**
@@ -132,6 +135,7 @@
*
* @hide
*/
+ @TestApi
public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 27f5545..e0364cb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13401,11 +13401,10 @@
/**
* Called by a device admin or holder of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE} to set the short
- * support message. This will be displayed to the user
- * in settings screens where funtionality has been disabled by the admin. The message should be
- * limited to a short statement such as "This setting is disabled by your administrator. Contact
- * someone@example.com for support." If the message is longer than 200 characters it may be
- * truncated.
+ * support message. This will be displayed to the user in settings screens where functionality
+ * has been disabled by the admin. The message should be limited to a short statement such as
+ * "This setting is disabled by your administrator. Contact someone@example.com for support."
+ * If the message is longer than 200 characters it may be truncated.
* <p>
* If the short support message needs to be localized, it is the responsibility of the
* {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
@@ -13460,7 +13459,8 @@
/**
* Called by a device admin to set the long support message. This will be displayed to the user
- * in the device administators settings screen.
+ * in the device administrators settings screen. If the message is longer than 20000 characters
+ * it may be truncated.
* <p>
* If the long support message needs to be localized, it is the responsibility of the
* {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/ui/RequestInfo.java
index 09d2db8..9ebb058 100644
--- a/core/java/android/credentials/ui/RequestInfo.java
+++ b/core/java/android/credentials/ui/RequestInfo.java
@@ -30,6 +30,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Contains information about the request that initiated this UX flow.
@@ -64,6 +66,9 @@
@Nullable
private final CreateCredentialRequest mCreateCredentialRequest;
+ @NonNull
+ private final List<String> mDefaultProviderIds;
+
@Nullable
private final GetCredentialRequest mGetCredentialRequest;
@@ -83,7 +88,8 @@
@NonNull String appPackageName) {
return new RequestInfo(
token, TYPE_CREATE, appPackageName, createCredentialRequest, null,
- /*hasPermissionToOverrideDefault=*/ false);
+ /*hasPermissionToOverrideDefault=*/ false,
+ /*defaultProviderIds=*/ new ArrayList<>());
}
/**
@@ -94,10 +100,11 @@
@NonNull
public static RequestInfo newCreateRequestInfo(
@NonNull IBinder token, @NonNull CreateCredentialRequest createCredentialRequest,
- @NonNull String appPackageName, boolean hasPermissionToOverrideDefault) {
+ @NonNull String appPackageName, boolean hasPermissionToOverrideDefault,
+ @NonNull List<String> defaultProviderIds) {
return new RequestInfo(
token, TYPE_CREATE, appPackageName, createCredentialRequest, null,
- hasPermissionToOverrideDefault);
+ hasPermissionToOverrideDefault, defaultProviderIds);
}
/** Creates new {@code RequestInfo} for a get-credential flow. */
@@ -107,7 +114,8 @@
@NonNull String appPackageName) {
return new RequestInfo(
token, TYPE_GET, appPackageName, null, getCredentialRequest,
- /*hasPermissionToOverrideDefault=*/ false);
+ /*hasPermissionToOverrideDefault=*/ false,
+ /*defaultProviderIds=*/ new ArrayList<>());
}
@@ -149,6 +157,20 @@
}
/**
+ * Returns default provider identifier (flattened component name) configured from the user
+ * settings.
+ *
+ * Will only be possibly non-empty for the create use case. Not meaningful for the sign-in use
+ * case.
+ *
+ * @hide
+ */
+ @NonNull
+ public List<String> getDefaultProviderIds() {
+ return mDefaultProviderIds;
+ }
+
+ /**
* Returns the non-null GetCredentialRequest when the type of the request is {@link
* #TYPE_GET}, or null otherwise.
*/
@@ -161,13 +183,15 @@
@NonNull String appPackageName,
@Nullable CreateCredentialRequest createCredentialRequest,
@Nullable GetCredentialRequest getCredentialRequest,
- boolean hasPermissionToOverrideDefault) {
+ boolean hasPermissionToOverrideDefault,
+ @NonNull List<String> defaultProviderIds) {
mToken = token;
mType = type;
mAppPackageName = appPackageName;
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault;
+ mDefaultProviderIds = defaultProviderIds == null ? new ArrayList<>() : defaultProviderIds;
}
private RequestInfo(@NonNull Parcel in) {
@@ -188,6 +212,7 @@
mCreateCredentialRequest = createCredentialRequest;
mGetCredentialRequest = getCredentialRequest;
mHasPermissionToOverrideDefault = in.readBoolean();
+ mDefaultProviderIds = in.createStringArrayList();
}
@Override
@@ -198,6 +223,7 @@
dest.writeTypedObject(mCreateCredentialRequest, flags);
dest.writeTypedObject(mGetCredentialRequest, flags);
dest.writeBoolean(mHasPermissionToOverrideDefault);
+ dest.writeStringList(mDefaultProviderIds);
}
@Override
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index a1d74df..37c5213 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -61,36 +61,42 @@
* This variant is intended for use when the caller is acting an originator, rather than on
* behalf of a different entity, as far as authorization goes.
*/
- SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
+ public SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper,
- @NonNull Identity originatorIdentity)
- throws RemoteException {
+ @NonNull Identity originatorIdentity) {
mId = moduleId;
mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
-
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- mService = service.attachAsOriginator(moduleId, originatorIdentity,
- mEventHandlerDelegate);
+ try {
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mService = service.attachAsOriginator(moduleId, originatorIdentity,
+ mEventHandlerDelegate);
+ }
+ mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
}
/**
* This variant is intended for use when the caller is acting as a middleman, i.e. on behalf of
* a different entity, as far as authorization goes.
*/
- SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
+ public SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper,
- @NonNull Identity middlemanIdentity, @NonNull Identity originatorIdentity)
- throws RemoteException {
+ @NonNull Identity middlemanIdentity, @NonNull Identity originatorIdentity) {
mId = moduleId;
mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- mService = service.attachAsMiddleman(moduleId, middlemanIdentity, originatorIdentity,
- mEventHandlerDelegate);
+ try {
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mService = service.attachAsMiddleman(moduleId, middlemanIdentity,
+ originatorIdentity,
+ mEventHandlerDelegate);
+ }
+ mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
}
@Override
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3487b01..4a46beb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4677,16 +4677,22 @@
"display_color_mode_vendor_hint";
/**
- * Whether or not the peak refresh rate should be forced. 0=no, 1=yes
+ * The user selected min refresh rate in frames per second.
+ *
+ * If this isn't set, 0 will be used.
* @hide
*/
- public static final String FORCE_PEAK_REFRESH_RATE = "force_peak_refresh_rate";
+ @Readable
+ public static final String MIN_REFRESH_RATE = "min_refresh_rate";
/**
- * Whether or not the peak refresh rate should be used for some content. 0=no, 1=yes
+ * The user selected peak refresh rate in frames per second.
+ *
+ * If this isn't set, the system falls back to a device specific default.
* @hide
*/
- public static final String SMOOTH_DISPLAY = "smooth_display";
+ @Readable
+ public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
/**
* The amount of time in milliseconds before the device goes to sleep or begins
@@ -11496,6 +11502,8 @@
public static final int DEVICE_STATE_ROTATION_KEY_HALF_FOLDED = 1;
/** @hide */
public static final int DEVICE_STATE_ROTATION_KEY_UNFOLDED = 2;
+ /** @hide */
+ public static final int DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY = 3;
/**
* The different postures that can be used as keys with
@@ -11507,6 +11515,7 @@
DEVICE_STATE_ROTATION_KEY_FOLDED,
DEVICE_STATE_ROTATION_KEY_HALF_FOLDED,
DEVICE_STATE_ROTATION_KEY_UNFOLDED,
+ DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateRotationLockKey {
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 1a1df6f..751c675 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -75,12 +75,13 @@
/**
* Constructs an information instance of the credential provider.
*
- * @param context the context object
+ * @param context the context object
* @param serviceComponent the serviceComponent of the provider service
- * @param userId the android userId for which the current process is running
+ * @param userId the android userId for which the current process is running
* @param isSystemProvider whether this provider is a system provider
* @throws PackageManager.NameNotFoundException If provider service is not found
- * @throws SecurityException If provider does not require the relevant permission
+ * @throws SecurityException If provider does not require the relevant
+ * permission
*/
public static CredentialProviderInfo create(
@NonNull Context context,
@@ -99,13 +100,15 @@
/**
* Constructs an information instance of the credential provider.
*
- * @param context the context object
- * @param serviceInfo the service info for the provider app. This must be retrieved from the
- * {@code PackageManager}
- * @param isSystemProvider whether the provider app is a system provider
+ * @param context the context object
+ * @param serviceInfo the service info for the provider app. This must
+ * be retrieved from the
+ * {@code PackageManager}
+ * @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
+ * 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 static CredentialProviderInfo create(
@@ -374,7 +377,6 @@
if (appInfo == null || serviceInfo == null) {
continue;
}
-
services.add(serviceInfo);
} catch (SecurityException | PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Error getting info for " + serviceInfo, e);
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index 53a5fd5..cf2e6a6 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -18,7 +18,6 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-import android.Manifest;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
@@ -35,7 +34,7 @@
import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
-import android.util.Log;
+import android.util.Slog;
import java.util.Objects;
@@ -226,7 +225,7 @@
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
- Log.d(TAG, "Failed to bind with intent: " + intent);
+ Slog.w(TAG, "Failed to bind with intent: " + intent);
return null;
}
@@ -252,11 +251,6 @@
GetCredentialException>() {
@Override
public void onResult(BeginGetCredentialResponse result) {
- // If provider service does not possess the HYBRID permission, this
- // check will throw an exception in the provider process.
- if (result.getRemoteCredentialEntry() != null) {
- enforceRemoteEntryPermission();
- }
try {
callback.onSuccess(result);
} catch (RemoteException e) {
@@ -274,15 +268,6 @@
}
));
}
- private void enforceRemoteEntryPermission() {
- String permission =
- Manifest.permission.PROVIDE_REMOTE_CREDENTIALS;
- getApplicationContext().enforceCallingOrSelfPermission(
- permission,
- String.format("Provider must have %s, in order to set a "
- + "remote entry", permission)
- );
- }
@Override
public void onBeginCreateCredential(BeginCreateCredentialRequest request,
@@ -305,11 +290,6 @@
BeginCreateCredentialResponse, CreateCredentialException>() {
@Override
public void onResult(BeginCreateCredentialResponse result) {
- // If provider service does not possess the HYBRID permission, this
- // check will throw an exception in the provider process.
- if (result.getRemoteCreateEntry() != null) {
- enforceRemoteEntryPermission();
- }
try {
callback.onSuccess(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 79a4f54..7822dde 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -913,8 +913,6 @@
* sandboxed process.
* @param callback The callback to notify of detection events.
* @return An instanece of {@link VisualQueryDetector}.
- * @throws UnsupportedOperationException if only single detector is supported. Multiple detector
- * is only available for apps targeting {@link Build.VERSION_CODES#TIRAMISU} and above.
* @throws IllegalStateException when there is an existing {@link VisualQueryDetector}, or when
* there is a non-trusted hotword detector running.
*
@@ -935,21 +933,16 @@
throw new IllegalStateException("Not available until onReady() is called");
}
synchronized (mLock) {
- if (!CompatChanges.isChangeEnabled(MULTIPLE_ACTIVE_HOTWORD_DETECTORS)) {
- throw new UnsupportedOperationException("VisualQueryDetector is only available if "
- + "multiple detectors are allowed");
- } else {
- if (mActiveVisualQueryDetector != null) {
+ if (mActiveVisualQueryDetector != null) {
+ throw new IllegalStateException(
+ "There is already an active VisualQueryDetector. "
+ + "It must be destroyed to create a new one.");
+ }
+ for (HotwordDetector detector : mActiveDetectors) {
+ if (!detector.isUsingSandboxedDetectionService()) {
throw new IllegalStateException(
- "There is already an active VisualQueryDetector. "
- + "It must be destroyed to create a new one.");
- }
- for (HotwordDetector detector : mActiveDetectors) {
- if (!detector.isUsingSandboxedDetectionService()) {
- throw new IllegalStateException(
- "It disallows to create trusted and non-trusted detectors "
- + "at the same time.");
- }
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
}
}
@@ -1070,21 +1063,6 @@
mActiveVisualQueryDetector = null;
}
mActiveDetectors.remove(detector);
- shutdownHotwordDetectionServiceIfRequiredLocked();
- }
- }
-
- private void shutdownHotwordDetectionServiceIfRequiredLocked() {
- for (HotwordDetector detector : mActiveDetectors) {
- if (detector.isUsingSandboxedDetectionService()) {
- return;
- }
- }
-
- try {
- mSystemService.shutdownHotwordDetectionService();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index f2373fb..d8fa533 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -360,6 +360,14 @@
public SparseArray<SurfaceControl.RefreshRateRange> thermalRefreshRateThrottling =
new SparseArray<>();
+ /**
+ * The ID of the brightness throttling data that should be used. This can change e.g. in
+ * concurrent displays mode in which a stricter brightness throttling policy might need to be
+ * used.
+ */
+ @Nullable
+ public String thermalBrightnessThrottlingDataId;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -437,7 +445,9 @@
&& Objects.equals(displayShape, other.displayShape)
&& Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate)
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
- && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling);
+ && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling)
+ && Objects.equals(
+ thermalBrightnessThrottlingDataId, other.thermalBrightnessThrottlingDataId);
}
@Override
@@ -495,6 +505,7 @@
layoutLimitedRefreshRate = other.layoutLimitedRefreshRate;
hdrSdrRatio = other.hdrSdrRatio;
thermalRefreshRateThrottling = other.thermalRefreshRateThrottling;
+ thermalBrightnessThrottlingDataId = other.thermalBrightnessThrottlingDataId;
}
public void readFromParcel(Parcel source) {
@@ -559,6 +570,7 @@
hdrSdrRatio = source.readFloat();
thermalRefreshRateThrottling = source.readSparseArray(null,
SurfaceControl.RefreshRateRange.class);
+ thermalBrightnessThrottlingDataId = source.readString8();
}
@Override
@@ -620,6 +632,7 @@
dest.writeTypedObject(layoutLimitedRefreshRate, flags);
dest.writeFloat(hdrSdrRatio);
dest.writeSparseArray(thermalRefreshRateThrottling);
+ dest.writeString8(thermalBrightnessThrottlingDataId);
}
@Override
@@ -889,6 +902,8 @@
}
sb.append(", thermalRefreshRateThrottling ");
sb.append(thermalRefreshRateThrottling);
+ sb.append(", thermalBrightnessThrottlingDataId ");
+ sb.append(thermalBrightnessThrottlingDataId);
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 43828d5..9714896 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -86,9 +86,12 @@
void onPreWindowFocus(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) {
mHasImeFocus = WindowManager.LayoutParams.mayUseInputMethod(windowAttribute.flags);
if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
- return;
+ if (!hasWindowFocus) {
+ getImmDelegate().onWindowLostFocus(mViewRootImpl);
+ }
+ } else {
+ getImmDelegate().onPreWindowGainedFocus(mViewRootImpl);
}
- getImmDelegate().onPreWindowGainedFocus(mViewRootImpl);
}
@UiThread
@@ -163,6 +166,7 @@
void onPreWindowGainedFocus(ViewRootImpl viewRootImpl);
void onPostWindowGainedFocus(View viewForWindowFocus,
@NonNull WindowManager.LayoutParams windowAttribute);
+ void onWindowLostFocus(@NonNull ViewRootImpl viewRootImpl);
void onViewFocusChanged(@NonNull View view, boolean hasFocus);
void onScheduledCheckFocus(@NonNull ViewRootImpl viewRootImpl);
void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c0ac04c..8f20e2d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -961,12 +961,20 @@
}
sAnrReported = true;
+ // If we're making an in-process call to ActivityManagerService
+ // and the previous binder call on this thread was oneway, the
+ // calling PID will be 0. Clearing the calling identity fixes
+ // this and ensures ActivityManager gets the correct calling
+ // pid.
+ final long identityToken = Binder.clearCallingIdentity();
try {
ActivityManager.getService().appNotResponding(reason);
} catch (RemoteException e) {
// We asked the system to crash us, but the system
// already crashed. Unfortunately things may be
// out of control.
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
}
}
};
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 8f270f5..62f3c90 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -412,4 +412,32 @@
public boolean setImeConsumesInput(boolean imeConsumesInput) {
return mTarget.setImeConsumesInput(imeConsumesInput);
}
+
+ /**
+ * Called by the system when it needs to take a snapshot of multiple text-related data in an
+ * atomic manner.
+ *
+ * <p><strong>Editor authors</strong>: Supporting this method is strongly encouraged. Atomically
+ * taken {@link TextSnapshot} is going to be really helpful for the system when optimizing IPCs
+ * in a safe and deterministic manner. Return {@code null} if an atomically taken
+ * {@link TextSnapshot} is unavailable. The system continues supporting such a scenario
+ * gracefully.</p>
+ *
+ * <p><strong>IME authors</strong>: Currently IMEs cannot call this method directly and always
+ * receive {@code null} as the result.</p>
+ *
+ * <p>Beware that there is a bug that this method was not overridden in
+ * {@link InputConnectionWrapper}, which ended up always returning {@code null} when gets
+ * called even if the wrapped {@link InputConnection} implements this method. The bug was
+ * fixed in {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}.</p>
+ *
+ * @return {@code null} if {@link TextSnapshot} is unavailable and/or this API is called from
+ * IMEs. Beware the bug in older devices mentioned above.
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Nullable
+ @Override
+ public TextSnapshot takeSnapshot() {
+ return mTarget.takeSnapshot();
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 82cf073..41ef44e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -482,13 +482,21 @@
private View mNextServedView;
/**
- * This is the root view of the overall window that currently has input
- * method focus.
+ * The latest {@link ViewRootImpl} that has, or most recently had, input method focus.
+ *
+ * <p>This value will be cleared when it becomes inactive and no longer has window focus.
*/
+ @Nullable
@GuardedBy("mH")
ViewRootImpl mCurRootView;
/**
+ * Whether the {@link #mCurRootView} currently has window focus.
+ */
+ @GuardedBy("mH")
+ boolean mCurRootViewWindowFocused;
+
+ /**
* This is set when we are in the process of connecting, to determine
* when we have actually finished.
*/
@@ -745,6 +753,7 @@
public void onPreWindowGainedFocus(ViewRootImpl viewRootImpl) {
synchronized (mH) {
setCurrentRootViewLocked(viewRootImpl);
+ mCurRootViewWindowFocused = true;
}
}
@@ -822,6 +831,17 @@
}
@Override
+ public void onWindowLostFocus(@NonNull ViewRootImpl viewRootImpl) {
+ synchronized (mH) {
+ if (mCurRootView == viewRootImpl) {
+ mCurRootViewWindowFocused = false;
+
+ clearCurRootViewIfNeeded();
+ }
+ }
+ }
+
+ @Override
public void onViewFocusChanged(@Nullable View view, boolean hasFocus) {
onViewFocusChangedInternal(view, hasFocus);
}
@@ -1114,6 +1134,10 @@
// Note that finishComposingText() is allowed to run
// even when we are not active.
mFallbackInputConnection.finishComposingTextFromImm();
+
+ if (clearCurRootViewIfNeeded()) {
+ return;
+ }
}
// Check focus again in case that "onWindowFocus" is called before
// handling this message.
@@ -1756,8 +1780,7 @@
}
/**
- * Return true if the given view is the currently active view for the
- * input method.
+ * Return {@code true} if the given view is the currently active view for the input method.
*/
public boolean isActive(View view) {
// Re-dispatch if there is a context mismatch.
@@ -1773,7 +1796,7 @@
}
/**
- * Return true if any view is currently active in the input method.
+ * Return {@code true} if any view is currently active for the input method.
*/
public boolean isActive() {
checkFocus();
@@ -1783,6 +1806,20 @@
}
/**
+ * Returns {@code true} if the given view's {@link ViewRootImpl} is the currently active one
+ * for the {@code InputMethodManager}.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
+ public boolean isCurrentRootView(@NonNull View attachedView) {
+ synchronized (mH) {
+ return mCurRootView == attachedView.getViewRootImpl();
+ }
+ }
+
+ /**
* Return {@code true} if the currently served view is accepting full text edits.
* If {@code false}, it has no input connection, so it can only handle raw key events.
*/
@@ -1908,6 +1945,24 @@
mImeDispatcher.clear();
}
+ /**
+ * Clears the {@link #mCurRootView} if it's no longer window focused and the connection is
+ * no longer active.
+ *
+ * @return {@code} true iff it was cleared.
+ */
+ @GuardedBy("mH")
+ private boolean clearCurRootViewIfNeeded() {
+ if (!mActive && !mCurRootViewWindowFocused) {
+ finishInputLocked();
+ mDelegate.setCurrentRootViewLocked(null);
+
+ return true;
+ }
+
+ return false;
+ }
+
public void displayCompletions(View view, CompletionInfo[] completions) {
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
diff --git a/core/java/android/view/inputmethod/TextBoundsInfo.java b/core/java/android/view/inputmethod/TextBoundsInfo.java
index d42d94e..d9f59d6 100644
--- a/core/java/android/view/inputmethod/TextBoundsInfo.java
+++ b/core/java/android/view/inputmethod/TextBoundsInfo.java
@@ -1025,6 +1025,7 @@
mEnd = -1;
mCharacterBounds = null;
mCharacterFlags = null;
+ mCharacterBidiLevels = null;
mLineSegmentFinder = null;
mWordSegmentFinder = null;
mGraphemeSegmentFinder = null;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7931d1a..2dbff58 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -724,7 +724,10 @@
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
- makeBlink();
+ // Call resumeBlink here instead of makeBlink to ensure that if mBlink is not null the
+ // Blink object is uncancelled. This ensures when a view is removed and added back the
+ // cursor will resume blinking.
+ resumeBlink();
}
void onDetachedFromWindow() {
@@ -1094,8 +1097,10 @@
private void resumeBlink() {
if (mBlink != null) {
mBlink.uncancel();
- makeBlink();
}
+ // Moving makeBlink outside of the null check block ensures that mBlink object gets
+ // instantiated when the view is added to the window if mBlink is still null.
+ makeBlink();
}
void adjustInputType(boolean password, boolean passwordInputType,
@@ -2921,6 +2926,9 @@
if (shouldBlink()) {
mShowCursor = SystemClock.uptimeMillis();
if (mBlink == null) mBlink = new Blink();
+ // Call uncancel as mBlink could have previously been cancelled and cursor will not
+ // resume blinking unless uncancelled.
+ mBlink.uncancel();
mTextView.removeCallbacks(mBlink);
mTextView.postDelayed(mBlink, BLINK);
} else {
diff --git a/core/java/android/window/TaskConstants.java b/core/java/android/window/TaskConstants.java
index e18fd50..69d79b4 100644
--- a/core/java/android/window/TaskConstants.java
+++ b/core/java/android/window/TaskConstants.java
@@ -73,6 +73,13 @@
*/
public static final int TASK_CHILD_LAYER_TASK_OVERLAY = 4 * TASK_CHILD_LAYER_REGION_SIZE;
+
+ /**
+ * Veil to cover task surface and other window decorations during resizes.
+ * @hide
+ */
+ public static final int TASK_CHILD_LAYER_RESIZE_VEIL = 6 * TASK_CHILD_LAYER_REGION_SIZE;
+
/**
* Z-orders of task child layers other than activities, task fragments and layers interleaved
* with them, e.g. IME windows. [-10000, 10000) is reserved for these layers.
@@ -84,7 +91,8 @@
TASK_CHILD_LAYER_COMPAT_UI,
TASK_CHILD_LAYER_WINDOW_DECORATIONS,
TASK_CHILD_LAYER_RECENTS_ANIMATION_PIP_OVERLAY,
- TASK_CHILD_LAYER_TASK_OVERLAY
+ TASK_CHILD_LAYER_TASK_OVERLAY,
+ TASK_CHILD_LAYER_RESIZE_VEIL
})
public @interface TaskChildLayer {}
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 51382a4..4d0132e 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -21,6 +21,8 @@
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -421,36 +423,45 @@
return false;
}
- boolean requestsPredictiveBack;
+ boolean requestsPredictiveBack = false;
// Check if the context is from an activity.
while ((context instanceof ContextWrapper) && !(context instanceof Activity)) {
context = ((ContextWrapper) context).getBaseContext();
}
+ boolean shouldCheckActivity = false;
+
if (context instanceof Activity) {
final Activity activity = (Activity) context;
- if (activity.getActivityInfo().hasOnBackInvokedCallbackEnabled()) {
- requestsPredictiveBack =
- activity.getActivityInfo().isOnBackInvokedCallbackEnabled();
- } else {
- requestsPredictiveBack =
- context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
- }
+ final ActivityInfo activityInfo = activity.getActivityInfo();
+ if (activityInfo != null) {
+ if (activityInfo.hasOnBackInvokedCallbackEnabled()) {
+ shouldCheckActivity = true;
+ requestsPredictiveBack = activityInfo.isOnBackInvokedCallbackEnabled();
- if (DEBUG) {
- Log.d(TAG, TextUtils.formatSimple("Activity: %s isPredictiveBackEnabled=%s",
- activity.getComponentName(),
- requestsPredictiveBack));
+ if (DEBUG) {
+ Log.d(TAG, TextUtils.formatSimple(
+ "Activity: %s isPredictiveBackEnabled=%s",
+ activity.getComponentName(),
+ requestsPredictiveBack));
+ }
+ }
+ } else {
+ Log.w(TAG, "The ActivityInfo is null, so we cannot verify if this Activity"
+ + " has the 'android:enableOnBackInvokedCallback' attribute."
+ + " The application attribute will be used as a fallback.");
}
- } else {
- requestsPredictiveBack =
- context.getApplicationInfo().isOnBackInvokedCallbackEnabled();
+ }
+
+ if (!shouldCheckActivity) {
+ final ApplicationInfo applicationInfo = context.getApplicationInfo();
+ requestsPredictiveBack = applicationInfo.isOnBackInvokedCallbackEnabled();
if (DEBUG) {
Log.d(TAG, TextUtils.formatSimple("App: %s requestsPredictiveBack=%s",
- context.getApplicationInfo().packageName,
+ applicationInfo.packageName,
requestsPredictiveBack));
}
}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 3a8f427..4f9fc39 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -32,6 +32,9 @@
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.telephony.Annotation;
+import android.telephony.TelephonyManager;
import android.text.ParcelableSpan;
import android.text.Spanned;
import android.text.TextUtils;
@@ -204,6 +207,32 @@
}
/**
+ * Intercepts the {@link AccessibilityService#GLOBAL_ACTION_KEYCODE_HEADSETHOOK} action
+ * by directly interacting with TelecomManager if a call is incoming or in progress.
+ *
+ * <p>
+ * Provided here in shared utils to be used by both the legacy and modern (SysUI)
+ * system action implementations.
+ * </p>
+ *
+ * @return True if the action was propagated to TelecomManager, otherwise false.
+ */
+ public static boolean interceptHeadsetHookForActiveCall(Context context) {
+ final TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ @Annotation.CallState final int callState =
+ telecomManager != null ? telecomManager.getCallState()
+ : TelephonyManager.CALL_STATE_IDLE;
+ if (callState == TelephonyManager.CALL_STATE_RINGING) {
+ telecomManager.acceptRingingCall();
+ return true;
+ } else if (callState == TelephonyManager.CALL_STATE_OFFHOOK) {
+ telecomManager.endCall();
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Indicates whether the current user has completed setup via the setup wizard.
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE}
*
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 499d38c..50f393b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1725,16 +1725,17 @@
if (inWorkProfile) {
((TextView) findViewById(R.id.open_cross_profile)).setText(
- devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK,
- () -> getString(R.string.miniresolver_open_in_work, targetDisplayLabel),
+ devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL,
+ () -> getString(R.string.miniresolver_open_in_personal,
+ targetDisplayLabel),
targetDisplayLabel));
((Button) findViewById(R.id.use_same_profile_browser)).setText(
devicePolicyResourcesManager.getString(MINIRESOLVER_USE_WORK_BROWSER,
() -> getString(R.string.miniresolver_use_work_browser)));
} else {
((TextView) findViewById(R.id.open_cross_profile)).setText(
- devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_PERSONAL,
- () -> getString(R.string.miniresolver_open_in_personal,
+ devicePolicyResourcesManager.getString(MINIRESOLVER_OPEN_IN_WORK,
+ () -> getString(R.string.miniresolver_open_in_work,
targetDisplayLabel),
targetDisplayLabel));
((Button) findViewById(R.id.use_same_profile_browser)).setText(
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
deleted file mode 100644
index 39d8380..0000000
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.display;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.Display;
-
-/**
- * Constants and utility methods for refresh rate settings.
- */
-public class RefreshRateSettingsUtils {
-
- private static final String TAG = "RefreshRateSettingsUtils";
-
- public static final float DEFAULT_REFRESH_RATE = 60f;
-
- /**
- * Find the highest refresh rate among all the modes of the default display.
- * @param context The context
- * @return The highest refresh rate
- */
- public static float findHighestRefreshRateForDefaultDisplay(Context context) {
- final DisplayManager dm = context.getSystemService(DisplayManager.class);
- final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
-
- if (display == null) {
- Log.w(TAG, "No valid default display device");
- return DEFAULT_REFRESH_RATE;
- }
-
- float maxRefreshRate = DEFAULT_REFRESH_RATE;
- for (Display.Mode mode : display.getSupportedModes()) {
- if (Math.round(mode.getRefreshRate()) > maxRefreshRate) {
- maxRefreshRate = mode.getRefreshRate();
- }
- }
- return maxRefreshRate;
- }
-
- /**
- * Get the min refresh rate which is determined by
- * {@link Settings.System.FORCE_PEAK_REFRESH_RATE}.
- * @param context The context
- * @return The min refresh rate
- */
- public static float getMinRefreshRate(Context context) {
- final ContentResolver cr = context.getContentResolver();
- int forcePeakRefreshRateSetting = Settings.System.getIntForUser(cr,
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1, cr.getUserId());
- return forcePeakRefreshRateSetting == 1
- ? findHighestRefreshRateForDefaultDisplay(context)
- : 0;
- }
-
- /**
- * Get the peak refresh rate which is determined by {@link Settings.System.SMOOTH_DISPLAY}.
- * @param context The context
- * @param defaultPeakRefreshRate The refresh rate to return if the setting doesn't have a value
- * @return The peak refresh rate
- */
- public static float getPeakRefreshRate(Context context, float defaultPeakRefreshRate) {
- final ContentResolver cr = context.getContentResolver();
- int smoothDisplaySetting = Settings.System.getIntForUser(cr,
- Settings.System.SMOOTH_DISPLAY, -1, cr.getUserId());
- switch (smoothDisplaySetting) {
- case 0:
- return DEFAULT_REFRESH_RATE;
- case 1:
- return findHighestRefreshRateForDefaultDisplay(context);
- default:
- return defaultPeakRefreshRate;
- }
- }
-}
diff --git a/core/java/com/android/internal/util/TraceBuffer.java b/core/java/com/android/internal/util/TraceBuffer.java
index bfcd65d..fcc77bd4 100644
--- a/core/java/com/android/internal/util/TraceBuffer.java
+++ b/core/java/com/android/internal/util/TraceBuffer.java
@@ -103,6 +103,10 @@
this(bufferCapacity, new ProtoOutputStreamProvider(), null);
}
+ public TraceBuffer(int bufferCapacity, Consumer<T> protoDequeuedCallback) {
+ this(bufferCapacity, new ProtoOutputStreamProvider(), protoDequeuedCallback);
+ }
+
public TraceBuffer(int bufferCapacity, ProtoProvider protoProvider,
Consumer<T> protoDequeuedCallback) {
mBufferCapacity = bufferCapacity;
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 302d2e7..a7a69c9 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -50,6 +50,8 @@
private boolean mEmphasizedMode;
private int mDefaultPaddingBottom;
private int mDefaultPaddingTop;
+ private int mEmphasizedPaddingTop;
+ private int mEmphasizedPaddingBottom;
private int mEmphasizedHeight;
private int mRegularHeight;
@DimenRes private int mCollapsibleIndentDimen = R.dimen.notification_actions_padding_start;
@@ -322,13 +324,16 @@
}
private void updateHeights() {
- int paddingTop = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin);
+ int inset = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.button_inset_vertical_material);
+ mEmphasizedPaddingTop = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin) - inset;
// same padding on bottom and at end
- int paddingBottom = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin_end);
- mEmphasizedHeight = paddingBottom + paddingTop + getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_action_emphasized_height);
+ mEmphasizedPaddingBottom = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_end) - inset;
+ mEmphasizedHeight = mEmphasizedPaddingTop + mEmphasizedPaddingBottom
+ + getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_action_emphasized_height);
mRegularHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_action_list_height);
}
@@ -356,18 +361,10 @@
mEmphasizedMode = emphasizedMode;
int height;
if (emphasizedMode) {
- int paddingTop = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin);
- // same padding on bottom and at end
- int paddingBottom = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin_end);
- int buttonPaddingInternal = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.button_inset_vertical_material);
setPaddingRelative(getPaddingStart(),
- paddingTop - buttonPaddingInternal,
+ mEmphasizedPaddingTop,
getPaddingEnd(),
- paddingBottom - buttonPaddingInternal);
-
+ mEmphasizedPaddingBottom);
setMinimumHeight(mEmphasizedHeight);
height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto
index 9ccadbf..3a9e5ee 100644
--- a/core/proto/android/companion/telecom.proto
+++ b/core/proto/android/companion/telecom.proto
@@ -20,9 +20,9 @@
option java_multiple_files = true;
-// Next index: 2
+// Next index: 4
message Telecom {
- // Next index: 5
+ // Next index: 6
message Call {
// UUID representing this call
int64 id = 1;
@@ -34,6 +34,8 @@
// Human-readable name of the app processing this call
string app_name = 2;
bytes app_icon = 3;
+ // Unique identifier for this app, such as a package name.
+ string app_identifier = 4;
}
Origin origin = 2;
@@ -59,9 +61,11 @@
REJECT_AND_BLOCK = 9;
IGNORE = 10;
}
- repeated Control controls_available = 4;
+ repeated Control controls = 4;
}
// The list of active calls.
repeated Call calls = 1;
+ // The list of requested calls or call changes.
+ repeated Call requests = 2;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 31220b4..fefa79f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8067,16 +8067,6 @@
</intent-filter>
</receiver>
- <!-- Broadcast Receiver listen to sufficient verifier requests from Package Manager
- when install new SDK, to verifier SDK code during installation time
- and terminate install if SDK not compatible with privacy sandbox restrictions. -->
- <receiver android:name="com.android.server.sdksandbox.SdkSandboxVerifierReceiver"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION"/>
- </intent-filter>
- </receiver>
-
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
@@ -8204,6 +8194,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.server.healthconnect.backuprestore.BackupRestore$BackupRestoreJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
android:exported="false">
<intent-filter>
@@ -8228,6 +8222,8 @@
<service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="true">
+ <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
+ android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
diff --git a/core/res/res/layout-television/user_switching_dialog.xml b/core/res/res/layout-television/user_switching_dialog.xml
deleted file mode 100644
index 72150e3..0000000
--- a/core/res/res/layout-television/user_switching_dialog.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 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.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/message"
- style="?attr/textAppearanceListItem"
- android:layout_width="match_parent"
- android:background="@color/background_leanback_dark"
- android:textColor="@color/primary_text_leanback_dark"
- android:layout_height="match_parent"
- android:gravity="center"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="24dp"
- android:paddingBottom="24dp" />
diff --git a/core/res/res/values-mcc310/config.xml b/core/res/res/values-mcc310/config.xml
index df398f9..76abcee 100644
--- a/core/res/res/values-mcc310/config.xml
+++ b/core/res/res/values-mcc310/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_mcc_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc311/config.xml b/core/res/res/values-mcc311/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc311/config.xml
+++ b/core/res/res/values-mcc311/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc312/config.xml b/core/res/res/values-mcc312/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc312/config.xml
+++ b/core/res/res/values-mcc312/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc313/config.xml b/core/res/res/values-mcc313/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc313/config.xml
+++ b/core/res/res/values-mcc313/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc314/config.xml b/core/res/res/values-mcc314/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc314/config.xml
+++ b/core/res/res/values-mcc314/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc315/config.xml b/core/res/res/values-mcc315/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc315/config.xml
+++ b/core/res/res/values-mcc315/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values-mcc316/config.xml b/core/res/res/values-mcc316/config.xml
index df398f9..6e0b678 100644
--- a/core/res/res/values-mcc316/config.xml
+++ b/core/res/res/values-mcc316/config.xml
@@ -22,4 +22,7 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">false</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">false</bool>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c5f7ea6..7bc3ab8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -558,6 +558,9 @@
docked if the dock is configured to enable the accelerometer. -->
<bool name="config_supportAutoRotation">true</bool>
+ <!-- If true, allows rotation resolver service to help resolve screen rotation. -->
+ <bool name="config_allowRotationResolver">true</bool>
+
<!-- If true, the screen can be rotated via the accelerometer in all 4
rotations as the default behavior. -->
<bool name="config_allowAllRotations">false</bool>
@@ -2102,9 +2105,6 @@
<!-- The default volume for the ring stream -->
<integer name="config_audio_ring_vol_default">5</integer>
- <!-- Enable sound dose computation and warnings -->
- <bool name="config_audio_csd_enabled_default">true</bool>
-
<!-- The default value for whether head tracking for
spatial audio is enabled for a newly connected audio device -->
<bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -2936,6 +2936,9 @@
<!-- Whether safe headphone volume is enabled or not (country specific). -->
<bool name="config_safe_media_volume_enabled">true</bool>
+ <!-- Whether safe headphone sound dosage warning is enabled or not (country specific). -->
+ <bool name="config_safe_sound_dosage_enabled">true</bool>
+
<!-- Whether safe headphone volume warning dialog is disabled on Vol+ (operator specific). -->
<bool name="config_safe_media_disable_on_volume_up">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b1b1edf..ad864b1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -96,12 +96,12 @@
<!-- Width of the navigation bar when it is placed vertically on the screen in car mode -->
<dimen name="navigation_bar_width_car_mode">96dp</dimen>
<!-- Height of notification icons in the status bar -->
- <dimen name="status_bar_icon_size">22dip</dimen>
+ <dimen name="status_bar_icon_size">18sp</dimen>
<!-- Desired size of system icons in status bar. -->
- <dimen name="status_bar_system_icon_size">15dp</dimen>
+ <dimen name="status_bar_system_icon_size">15sp</dimen>
<!-- Intrinsic size of most system icons in status bar. This is the default value that
is used if a Drawable reports an intrinsic size of 0. -->
- <dimen name="status_bar_system_icon_intrinsic_size">17dp</dimen>
+ <dimen name="status_bar_system_icon_intrinsic_size">17sp</dimen>
<!-- Size of the giant number (unread count) in the notifications -->
<dimen name="status_bar_content_number_size">48sp</dimen>
<!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
@@ -257,8 +257,8 @@
<!-- The margin of the notification action list at the top -->
<dimen name="notification_action_list_margin_top">0dp</dimen>
- <!-- The visual height of the emphasized notification action -->
- <dimen name="notification_action_emphasized_height">36dp</dimen>
+ <!-- The overall height of the emphasized notification action -->
+ <dimen name="notification_action_emphasized_height">48dp</dimen>
<!-- The padding of the actions in non-conversation layout. For conversations, the analogous
value is calculated in ConversationLayout#updateActionListPadding() -->
@@ -330,7 +330,7 @@
<dimen name="notification_icon_circle_start">16dp</dimen>
<!-- size (width and height) of the icon in the notification header -->
- <dimen name="notification_header_icon_size_ambient">18dp</dimen>
+ <dimen name="notification_header_icon_size_ambient">18sp</dimen>
<!-- The margin before the start of the app name in the header. -->
<dimen name="notification_header_app_name_margin_start">3dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ae107fd..e25425d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -283,7 +283,6 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
- <java-symbol type="bool" name="config_audio_csd_enabled_default" />
<java-symbol type="integer" name="config_audio_notif_vol_default" />
<java-symbol type="integer" name="config_audio_notif_vol_steps" />
<java-symbol type="integer" name="config_audio_ring_vol_default" />
@@ -349,6 +348,7 @@
<java-symbol type="bool" name="config_useDevInputEventForAudioJack" />
<java-symbol type="bool" name="config_safe_media_volume_enabled" />
<java-symbol type="bool" name="config_safe_media_disable_on_volume_up" />
+ <java-symbol type="bool" name="config_safe_sound_dosage_enabled" />
<java-symbol type="bool" name="config_camera_sound_forced" />
<java-symbol type="bool" name="config_dontPreferApn" />
<java-symbol type="bool" name="config_restartRadioAfterProvisioning" />
@@ -1715,6 +1715,7 @@
<java-symbol type="integer" name="config_motionPredictionOffsetNanos" />
<java-symbol type="bool" name="config_showNavigationBar" />
<java-symbol type="bool" name="config_supportAutoRotation" />
+ <java-symbol type="bool" name="config_allowRotationResolver" />
<java-symbol type="bool" name="config_dockedStackDividerFreeSnapMode" />
<java-symbol type="dimen" name="docked_stack_divider_thickness" />
<java-symbol type="dimen" name="docked_stack_divider_insets" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
index 3e9e992..516253b 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
@@ -58,7 +58,7 @@
}
@Test
- public void isCurrentUser_forCurrentUser_returnsFalse() {
+ public void isCurrentOrSystemUser_forCurrentUser_returnsFalse() {
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
assertWithMessage("Current user")
@@ -66,7 +66,7 @@
}
@Test
- public void isCurrentUser_forNonCurrentUser_returnsFalse() {
+ public void isCurrentOrSystemUser_forNonCurrentUser_returnsFalse() {
when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_2);
assertWithMessage("Non-current user")
@@ -74,7 +74,8 @@
}
@Test
- public void isCurrentUser_forSystemUser_returnsTrue() {
+ public void isCurrentOrSystemUser_forSystemUser_returnsTrue() {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
assertWithMessage("System user")
@@ -82,10 +83,26 @@
}
@Test
- public void isCurrentUser_withActivityManagerFails_returnsFalse() {
- doThrow(new RuntimeException()).when(() -> ActivityManager.getCurrentUser());
+ public void isCurrentOrSystemUser_withActivityManagerFailure_returnsFalse() {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+ doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
assertWithMessage("User when activity manager fails")
.that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
}
+
+ @Test
+ public void getCurrentUser() {
+ assertWithMessage("Current user")
+ .that(RadioServiceUserController.getCurrentUser()).isEqualTo(USER_ID_1);
+ }
+
+ @Test
+ public void getCurrentUser_withActivityManagerFailure_returnsUserNull() {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+ doThrow(new RuntimeException()).when(ActivityManager::getCurrentUser);
+
+ assertWithMessage("Current user when activity manager fails")
+ .that(RadioServiceUserController.getCurrentUser()).isEqualTo(UserHandle.USER_NULL);
+ }
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index cce1b2b..6c70192 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -80,7 +80,7 @@
/* vendorIds= */ null);
}
- static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(int freq) {
+ static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(long freq) {
ProgramIdentifier halId = makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ, freq);
return makeHalSelector(halId, /* secondaryIds= */ new ProgramIdentifier[0]);
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java
index df3ddfd..b54c156 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java
@@ -52,6 +52,7 @@
{Result.INVALID_STATE, RadioTuner.TUNER_RESULT_INVALID_STATE},
{Result.NOT_SUPPORTED, RadioTuner.TUNER_RESULT_NOT_SUPPORTED},
{Result.TIMEOUT, RadioTuner.TUNER_RESULT_TIMEOUT},
+ {Result.CANCELED, RadioTuner.TUNER_RESULT_CANCELED},
{Result.UNKNOWN_ERROR, RadioTuner.TUNER_RESULT_UNKNOWN_ERROR}
});
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index aea0178..2ef923d 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -27,11 +27,13 @@
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.ProgramListChunk;
import android.hardware.broadcastradio.Properties;
+import android.hardware.broadcastradio.Result;
import android.hardware.broadcastradio.VendorKeyValue;
import android.hardware.radio.Announcement;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
+import android.os.ServiceSpecificException;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
@@ -153,6 +155,16 @@
}
@Test
+ public void throwOnError_withCancelException() {
+ ServiceSpecificException halException = new ServiceSpecificException(Result.CANCELED);
+
+ RuntimeException thrown = ConversionUtils.throwOnError(halException, "tune");
+
+ expect.withMessage("Exception thrown for canceling error").that(thrown)
+ .hasMessageThat().contains("tune: CANCELED");
+ }
+
+ @Test
public void propertiesFromHalProperties_idsMatch() {
expect.withMessage("Properties id")
.that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 78b5a4a..84aa864 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -47,9 +47,11 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
+import android.os.Binder;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -73,6 +75,8 @@
*/
public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
+ private static final int USER_ID_1 = 11;
+ private static final int USER_ID_2 = 12;
private static final VerificationWithTimeout CALLBACK_TIMEOUT =
timeout(/* millis= */ 200);
private static final int SIGNAL_QUALITY = 90;
@@ -109,7 +113,10 @@
SIGNAL_QUALITY);
// Mocks
- @Mock private IBroadcastRadio mBroadcastRadioMock;
+ @Mock
+ private UserHandle mUserHandleMock;
+ @Mock
+ private IBroadcastRadio mBroadcastRadioMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
// RadioModule under test
@@ -124,14 +131,18 @@
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioServiceUserController.class).spyStatic(CompatChanges.class);
+ builder.spyStatic(RadioServiceUserController.class).spyStatic(CompatChanges.class)
+ .spyStatic(Binder.class);
}
@Before
public void setup() throws Exception {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
doReturn(true).when(() -> CompatChanges.isChangeEnabled(
eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
+ doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
mRadioModule = new RadioModule(mBroadcastRadioMock,
AidlTestUtils.makeDefaultModuleProperties());
@@ -421,6 +432,21 @@
}
@Test
+ public void tune_forSystemUser() throws Exception {
+ when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
+ doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
+ doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo =
+ AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].tune(initialSel);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
+ @Test
public void tune_withUnknownErrorFromHal_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
@@ -1136,6 +1162,35 @@
}
@Test
+ public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+ throws Exception {
+ openAidlClients(1);
+ doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+
+ mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo(
+ AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY));
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ .onCurrentProgramInfoChanged(any());
+ }
+
+ @Test
+ public void onTuneFailed_forTunerCallback() throws Exception {
+ int numSessions = 3;
+ openAidlClients(numSessions);
+ android.hardware.broadcastradio.ProgramSelector halSel = AidlTestUtils.makeHalFmSelector(
+ AM_FM_FREQUENCY_LIST[1]);
+ ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+
+ mHalTunerCallback.onTuneFailed(Result.CANCELED, halSel);
+
+ for (int index = 0; index < numSessions; index++) {
+ verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT)
+ .onTuneFailed(RadioTuner.TUNER_RESULT_CANCELED, sel);
+ }
+ }
+
+ @Test
public void onAntennaStateChange_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 3815008..fac9eaa 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -46,8 +46,10 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
+import android.os.Binder;
import android.os.ParcelableException;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -74,6 +76,8 @@
@RunWith(MockitoJUnitRunner.class)
public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
+ private static final int USER_ID_1 = 11;
+ private static final int USER_ID_2 = 12;
private static final VerificationWithTimeout CALLBACK_TIMEOUT =
timeout(/* millis= */ 200);
private static final int SIGNAL_QUALITY = 1;
@@ -94,18 +98,25 @@
private ProgramInfo mHalCurrentInfo;
private TunerSession[] mTunerSessions;
- @Mock private IBroadcastRadio mBroadcastRadioMock;
- @Mock ITunerSession mHalTunerSessionMock;
+ @Mock
+ private UserHandle mUserHandleMock;
+ @Mock
+ private IBroadcastRadio mBroadcastRadioMock;
+ @Mock
+ ITunerSession mHalTunerSessionMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
- builder.spyStatic(RadioServiceUserController.class);
+ builder.spyStatic(RadioServiceUserController.class).spyStatic(Binder.class);
}
@Before
public void setup() throws Exception {
+ when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+ doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
mRadioModule = new RadioModule(mBroadcastRadioMock,
TestUtils.makeDefaultModuleProperties());
@@ -387,6 +398,20 @@
}
@Test
+ public void tune_forSystemUser() throws Exception {
+ when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
+ doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
+ doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+ openAidlClients(/* numClients= */ 1);
+
+ mTunerSessions[0].tune(initialSel);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
+ @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -858,6 +883,19 @@
}
@Test
+ public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+ throws Exception {
+ openAidlClients(1);
+ doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
+
+ mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo(
+ TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY));
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ .onCurrentProgramInfoChanged(any());
+ }
+
+ @Test
public void onConfigFlagUpdated_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index bf8c7c6..72969f7 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -112,6 +112,7 @@
Paths.get("/data/misc/wmtrace/layers_trace.winscope"),
Paths.get("/data/misc/wmtrace/transactions_trace.winscope"),
Paths.get("/data/misc/wmtrace/transition_trace.winscope"),
+ Paths.get("/data/misc/wmtrace/shell_transition_trace.winscope"),
};
private static final Path[] UI_TRACES_GENERATED_DURING_BUGREPORT = {
Paths.get("/data/misc/wmtrace/layers_trace_from_transactions.winscope"),
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 67842f0..ab500f7 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -59,6 +59,7 @@
"print-test-util-lib",
"testng",
"servicestests-utils",
+ "device-time-shell-utils",
],
libs: [
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index fb0f3d4..af81957 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -62,7 +62,6 @@
import android.view.Display;
import android.view.View;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -616,7 +615,6 @@
}
@Test
- @FlakyTest(bugId = 176134235)
public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/app/time/TimeManagerTest.java b/core/tests/coretests/src/android/app/time/TimeManagerTest.java
new file mode 100644
index 0000000..6370f3b
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeManagerTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
+import static android.app.time.cts.shell.DeviceConfigKeys.NAMESPACE_SYSTEM_TIME;
+import static android.app.time.cts.shell.DeviceConfigKeys.TimeZoneDetector.KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT;
+import static android.app.time.cts.shell.DeviceConfigShellHelper.SYNC_DISABLED_MODE_UNTIL_REBOOT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Instrumentation;
+import android.app.time.cts.shell.DeviceConfigShellHelper;
+import android.app.time.cts.shell.DeviceShellCommandExecutor;
+import android.app.time.cts.shell.TimeZoneDetectorShellHelper;
+import android.app.time.cts.shell.device.InstrumentationShellCommandExecutor;
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Tests for non-SDK methods / internal behavior related to {@link TimeManager}.
+ * Also see {@link android.app.time.cts.TimeManagerTest}
+ */
+public class TimeManagerTest {
+
+ /**
+ * This rule adopts the Shell process permissions, needed because MANAGE_TIME_AND_ZONE_DETECTION
+ * and SUGGEST_EXTERNAL_TIME required by {@link TimeManager} are privileged permissions.
+ */
+ @Rule
+ public final AdoptShellPermissionsRule shellPermRule = new AdoptShellPermissionsRule();
+
+ private TimeZoneDetectorShellHelper mTimeZoneDetectorShellHelper;
+ private DeviceConfigShellHelper mDeviceConfigShellHelper;
+ private DeviceConfigShellHelper.PreTestState mDeviceConfigPreTestState;
+
+ private Context mContext;
+ private TimeManager mTimeManager;
+
+ @Before
+ public void before() throws Exception {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ DeviceShellCommandExecutor shellCommandExecutor = new InstrumentationShellCommandExecutor(
+ instrumentation.getUiAutomation());
+ mTimeZoneDetectorShellHelper = new TimeZoneDetectorShellHelper(shellCommandExecutor);
+ mDeviceConfigShellHelper = new DeviceConfigShellHelper(shellCommandExecutor);
+
+ // This anticipates a future state where a generally applied target preparer may disable
+ // device_config sync for all CTS tests: only suspend syncing if it isn't already suspended,
+ // and only resume it if this test suspended it.
+ mDeviceConfigPreTestState = mDeviceConfigShellHelper.setSyncModeForTest(
+ SYNC_DISABLED_MODE_UNTIL_REBOOT, NAMESPACE_SYSTEM_TIME);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mTimeManager = mContext.getSystemService(TimeManager.class);
+ assertNotNull(mTimeManager);
+
+ // Avoid running tests when device policy doesn't allow user configuration. If this needs to
+ // pass then tests will become more complicated or separate cases broken out.
+ int configureAutoDetectionEnabledCapability = mTimeManager.getTimeCapabilitiesAndConfig()
+ .getCapabilities().getConfigureAutoDetectionEnabledCapability();
+ boolean userRestricted = configureAutoDetectionEnabledCapability == CAPABILITY_NOT_ALLOWED;
+ assertFalse(userRestricted);
+ }
+
+ @After
+ public void after() throws Exception {
+ mDeviceConfigShellHelper.restoreDeviceConfigStateForTest(mDeviceConfigPreTestState);
+ }
+
+ /**
+ * Tests a server flag that can be used to change the "automatic time zone enabled" value
+ * for devices where the user hasn't yet expressed a preference. The flag is only intended for
+ * use during internal testing and therefore has not been included in CTS; it could be removed
+ * in later releases. This test takes ~35s to run because the asynchronous operations involved
+ * require sleeps to allow them to complete.
+ */
+ @Test
+ public void testTimeZoneEnabledDefaultFlagBehavior() throws Exception {
+ TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+ mTimeManager.getTimeZoneCapabilitiesAndConfig();
+
+ TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+
+ // Skip this test if the current user is not allowed to alter time detection settings via
+ // the TimeManager APIs.
+ assumeTrue(capabilities.getConfigureAutoDetectionEnabledCapability()
+ == CAPABILITY_POSSESSED);
+
+ // Start with the auto_time_zone_explicit setting empty, but record the value if there is
+ // one so that it can be restored.
+ boolean isAutoDetectionEnabledExplicit =
+ mTimeZoneDetectorShellHelper.isAutoTimeZoneEnabledExplicitly();
+
+ mTimeZoneDetectorShellHelper.clearAutoTimeZoneEnabledExplicitly();
+ sleepForAsyncOperation();
+
+ // Record the current time zone and auto detection setting so that it can be restored by the
+ // test afterwards.
+ boolean initialAutoTzEnabled =
+ capabilitiesAndConfig.getConfiguration().isAutoDetectionEnabled();
+ TimeZoneState initialTimeZoneState = mTimeManager.getTimeZoneState();
+
+ try {
+ // The server flag should be used to control the device's behavior initially because
+ // auto_time_zone_explicit is not set.
+ boolean newAutoTzEnabled = !initialAutoTzEnabled;
+ mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME,
+ KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT,
+ Boolean.toString(newAutoTzEnabled));
+ sleepForAsyncOperation();
+ assertEquals(newAutoTzEnabled, mTimeZoneDetectorShellHelper.isAutoDetectionEnabled());
+
+ mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME,
+ KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT,
+ Boolean.toString(initialAutoTzEnabled));
+ sleepForAsyncOperation();
+ assertEquals(initialAutoTzEnabled,
+ mTimeZoneDetectorShellHelper.isAutoDetectionEnabled());
+
+ // Now simulate the user toggling the auto tz setting twice, which should cause the
+ // system to recognize the user has expressed an explicit preference.
+ TimeZoneConfiguration config1 = new TimeZoneConfiguration.Builder()
+ .setAutoDetectionEnabled(newAutoTzEnabled)
+ .build();
+ mTimeManager.updateTimeZoneConfiguration(config1);
+ sleepForAsyncOperation();
+ assertEquals(newAutoTzEnabled,
+ mTimeZoneDetectorShellHelper.isAutoDetectionEnabled());
+
+ TimeZoneConfiguration config2 = new TimeZoneConfiguration.Builder()
+ .setAutoDetectionEnabled(initialAutoTzEnabled)
+ .build();
+ mTimeManager.updateTimeZoneConfiguration(config2);
+ sleepForAsyncOperation();
+ assertEquals(initialAutoTzEnabled,
+ mTimeZoneDetectorShellHelper.isAutoDetectionEnabled());
+
+ // Auto tz enabled is now back to initialAutoTzEnabled.
+
+ // Repeat the flag check: Now the server flag should have no effect because they have
+ // expressed a preference.
+ mDeviceConfigShellHelper.put(NAMESPACE_SYSTEM_TIME,
+ KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT,
+ Boolean.toString(newAutoTzEnabled));
+ sleepForAsyncOperation();
+ assertEquals(initialAutoTzEnabled,
+ mTimeZoneDetectorShellHelper.isAutoDetectionEnabled());
+ } finally {
+ // Restore the device's state (as much as possible).
+ if (isAutoDetectionEnabledExplicit) {
+ mTimeZoneDetectorShellHelper.setAutoTimeZoneEnabledExplicitly();
+ } else {
+ mTimeZoneDetectorShellHelper.clearAutoTimeZoneEnabledExplicitly();
+ }
+
+ // Restore auto tz and the time zone (if the device started in manual).
+ mTimeZoneDetectorShellHelper.setAutoDetectionEnabled(initialAutoTzEnabled);
+ if (!initialAutoTzEnabled) {
+ // If the device started in "manual" we can restore the time zone to its original
+ // state, maybe not confidence exactly.
+ mTimeZoneDetectorShellHelper.setTimeZoneState(
+ initialTimeZoneState.getId(),
+ initialTimeZoneState.getUserShouldConfirmId());
+ }
+ sleepForAsyncOperation();
+ }
+ }
+
+ /**
+ * Sleeps for a length of time sufficient to allow async operations to complete. Many time
+ * manager APIs are or could be asynchronous and deal with time, so there are no practical
+ * alternatives.
+ */
+ private static void sleepForAsyncOperation() throws Exception {
+ Thread.sleep(5_000);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index a3eda8d..f45db23 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -39,7 +39,6 @@
import android.widget.TextView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
@@ -55,7 +54,6 @@
* atest FrameworksCoreTests:ImeInsetsSourceConsumerTest
*/
@Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
@RunWith(AndroidJUnit4.class)
public class ImeInsetsSourceConsumerTest {
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 596f351..7c2759a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1183,6 +1183,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-1005167552": {
+ "message": "Playing #%d in parallel on track #%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-1003678883": {
"message": "Cleaning splash screen token=%s",
"level": "VERBOSE",
@@ -1447,6 +1453,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-774908272": {
+ "message": "Marking #%d animation as SYNC.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-771177730": {
"message": "Removing focused app token:%s displayId=%d",
"level": "VERBOSE",
@@ -1495,12 +1507,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-741766551": {
- "message": "Content Recording: Ignoring session on invalid virtual display",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONTENT_RECORDING",
- "at": "com\/android\/server\/wm\/ContentRecordingController.java"
- },
"-732715767": {
"message": "Unable to retrieve window container to start recording for display %d",
"level": "VERBOSE",
@@ -2017,6 +2023,12 @@
"group": "WM_DEBUG_WALLPAPER",
"at": "com\/android\/server\/wm\/WallpaperController.java"
},
+ "-266707683": {
+ "message": "Moving #%d from collecting to waiting.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -2089,6 +2101,12 @@
"group": "WM_DEBUG_WINDOW_MOVEMENT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-186693085": {
+ "message": "Starting a Recents transition which can be parallel.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-182877285": {
"message": "Wallpaper layer changed: assigning layers + relayout",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 54978bd..6a79bc1 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -125,6 +125,34 @@
// End ProtoLog
+gensrcs {
+ name: "wm-shell-protos",
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+
+ tool_files: [
+ ":libprotobuf-internal-protos",
+ ],
+
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-javastream) " +
+ " --javastream_out=$(genDir)/$(in) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in) " +
+ "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+ srcs: [
+ "proto/**/*.proto",
+ ],
+ output_extension: "srcjar",
+}
+
java_library {
name: "WindowManager-Shell-proto",
@@ -142,6 +170,7 @@
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
":wm_shell-sources-kt",
":wm_shell-aidls",
+ ":wm-shell-protos",
],
resource_dirs: [
"res",
diff --git a/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto
new file mode 100644
index 0000000..6e01101
--- /dev/null
+++ b/libs/WindowManager/Shell/proto/wm_shell_transition_trace.proto
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.wm.shell;
+
+option java_multiple_files = true;
+
+/* Represents a file full of transition entries.
+ Encoded, it should start with 0x09 0x57 0x4D 0x53 0x54 0x52 0x41 0x43 0x45 (.WMSTRACE), such
+ that it can be easily identified. */
+message WmShellTransitionTraceProto {
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x54534D57; /* WMST (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ // Must be the first field, set to value in MagicNumber
+ required fixed64 magic_number = 1;
+ repeated Transition transitions = 2;
+ repeated HandlerMapping handlerMappings = 3;
+}
+
+message Transition {
+ required int32 id = 1;
+ optional int64 dispatch_time_ns = 2;
+ optional int32 handler = 3;
+ optional int64 merge_time_ns = 4;
+ optional int64 merge_request_time_ns = 5;
+ optional int32 merged_into = 6;
+ optional int64 abort_time_ns = 7;
+}
+
+message HandlerMapping {
+ required int32 id = 1;
+ required string name = 2;
+}
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
index 96d2d7c..d2360e9 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
@@ -16,9 +16,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid
- android:color="@android:color/system_neutral1_800"
+ android:color="?androidprv:attr/materialColorSurfaceContainerHigh"
/>
<corners android:radius="20dp" />
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
index 4bd2f13..8fd2e68 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_bg.xml
@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceBright" />
<corners
android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="?android:attr/dialogCornerRadius"
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml
new file mode 100644
index 0000000..1f3e3a4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_flyout.xml b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
index 7fdf290..65a07a7 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_flyout.xml
@@ -13,7 +13,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<LinearLayout
android:id="@+id/bubble_flyout_text_container"
@@ -48,6 +49,7 @@
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:maxLines="1"
android:ellipsize="end"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
<TextView
@@ -57,6 +59,7 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:maxLines="2"
android:ellipsize="end"
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
index 0cf6d73..f88d63d 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
@@ -16,6 +16,7 @@
-->
<com.android.wm.shell.common.AlphaOptimizedButton
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
style="@android:style/Widget.DeviceDefault.Button.Borderless"
android:id="@+id/manage_button"
android:layout_gravity="start"
@@ -27,6 +28,6 @@
android:focusable="true"
android:text="@string/manage_bubbles_text"
android:textSize="@*android:dimen/text_size_body_2_material"
- android:textColor="@*android:color/system_neutral1_50"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:background="@drawable/bubble_manage_btn_bg"
/>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 298ad30..10c9562 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -15,6 +15,7 @@
~ limitations under the License
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bubble_manage_menu_bg"
@@ -41,6 +42,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
android:text="@string/bubble_dismiss_text" />
@@ -66,6 +68,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
android:text="@string/bubbles_dont_bubble_conversation" />
@@ -92,6 +95,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml
new file mode 100644
index 0000000..a4bbd89
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/desktop_mode_resize_veil_background">
+
+ <ImageView
+ android:id="@+id/veil_application_icon"
+ android:layout_width="96dp"
+ android:layout_height="96dp"
+ android:layout_gravity="center"
+ android:contentDescription="@string/app_icon_text" />
+</FrameLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
index 5aff415..7f1aac3 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
@@ -73,11 +73,13 @@
android:textAlignment="center"/>
<LinearLayout
+ android:id="@+id/letterbox_restart_dialog_checkbox_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingVertical="14dp"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
- android:layout_marginVertical="32dp">
+ android:layout_marginVertical="18dp">
<CheckBox
android:id="@+id/letterbox_restart_dialog_checkbox"
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 76eb094..a3916b7 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -125,4 +125,7 @@
<!-- Whether the additional education about reachability is enabled -->
<bool name="config_letterboxIsReachabilityEducationEnabled">false</bool>
+
+ <!-- Whether DragAndDrop capability is enabled -->
+ <bool name="config_enableShellDragDrop">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index d3f3958..24fd86b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -297,7 +297,7 @@
public BubbleInfo asBubbleBarBubble() {
return new BubbleInfo(getKey(),
getFlags(),
- getShortcutInfo().getId(),
+ getShortcutId(),
getIcon(),
getUser().getIdentifier(),
getPackageName());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 21f02b1..3eb9fa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -260,7 +260,7 @@
/** One handed mode controller to register transition listener. */
private Optional<OneHandedController> mOneHandedOptional;
/** Drag and drop controller to register listener for onDragStarted. */
- private DragAndDropController mDragAndDropController;
+ private Optional<DragAndDropController> mDragAndDropController;
/** Used to send bubble events to launcher. */
private Bubbles.BubbleStateListener mBubbleStateListener;
@@ -286,7 +286,7 @@
BubblePositioner positioner,
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
@@ -469,7 +469,7 @@
});
mOneHandedOptional.ifPresent(this::registerOneHandedState);
- mDragAndDropController.addListener(this::collapseStack);
+ mDragAndDropController.ifPresent(controller -> controller.addListener(this::collapseStack));
// Clear out any persisted bubbles on disk that no longer have a valid user.
List<UserInfo> users = mUserManager.getAliveUsers();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index f878a46..6a5f785 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -177,7 +177,7 @@
final TypedArray ta = mContext.obtainStyledAttributes(
new int[] {
- com.android.internal.R.attr.colorSurface,
+ com.android.internal.R.attr.materialColorSurfaceContainer,
android.R.attr.dialogCornerRadius});
mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
mCornerRadius = ta.getDimensionPixelSize(1, 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index c49451d..df7f5b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -19,23 +19,21 @@
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.Context
import android.graphics.Bitmap
+import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Path
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.InsetDrawable
import android.util.PathParser
-import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
-class BubbleOverflow(
- private val context: Context,
- private val positioner: BubblePositioner
-) : BubbleViewProvider {
+class BubbleOverflow(private val context: Context, private val positioner: BubblePositioner) :
+ BubbleViewProvider {
private lateinit var bitmap: Bitmap
private lateinit var dotPath: Path
@@ -54,7 +52,7 @@
overflowBtn = null
}
- /** Call before use and again if cleanUpExpandedState was called. */
+ /** Call before use and again if cleanUpExpandedState was called. */
fun initialize(controller: BubbleController) {
createExpandedView()
getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
@@ -74,10 +72,10 @@
}
fun updateResources() {
- overflowIconInset = context.resources.getDimensionPixelSize(
- R.dimen.bubble_overflow_icon_inset)
- overflowBtn?.layoutParams = FrameLayout.LayoutParams(positioner.bubbleSize,
- positioner.bubbleSize)
+ overflowIconInset =
+ context.resources.getDimensionPixelSize(R.dimen.bubble_overflow_icon_inset)
+ overflowBtn?.layoutParams =
+ FrameLayout.LayoutParams(positioner.bubbleSize, positioner.bubbleSize)
expandedView?.updateDimensions()
}
@@ -85,36 +83,58 @@
val res = context.resources
// Set overflow button accent color, dot color
- val typedValue = TypedValue()
- context.theme.resolveAttribute(com.android.internal.R.attr.colorAccentPrimary,
- typedValue, true)
- val colorAccent = res.getColor(typedValue.resourceId, null)
- dotColor = colorAccent
- val shapeColor = res.getColor(android.R.color.system_accent1_1000)
+ val typedArray =
+ context.obtainStyledAttributes(
+ intArrayOf(
+ com.android.internal.R.attr.materialColorPrimaryFixed,
+ com.android.internal.R.attr.materialColorOnPrimaryFixed
+ )
+ )
+
+ val colorAccent = typedArray.getColor(0, Color.WHITE)
+ val shapeColor = typedArray.getColor(1, Color.BLACK)
+ typedArray.recycle()
+
+ dotColor = colorAccent
overflowBtn?.iconDrawable?.setTint(shapeColor)
- val iconFactory = BubbleIconFactory(context,
- context.getResources().getDimensionPixelSize(R.dimen.bubble_size),
- context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size),
- context.getResources().getColor(R.color.important_conversation),
- context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.importance_ring_stroke_width))
+ val iconFactory =
+ BubbleIconFactory(
+ context,
+ res.getDimensionPixelSize(R.dimen.bubble_size),
+ res.getDimensionPixelSize(R.dimen.bubble_badge_size),
+ res.getColor(R.color.important_conversation),
+ res.getDimensionPixelSize(com.android.internal.R.dimen.importance_ring_stroke_width)
+ )
// Update bitmap
val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
- bitmap = iconFactory.createBadgedIconBitmap(
- AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
+ bitmap =
+ iconFactory
+ .createBadgedIconBitmap(AdaptiveIconDrawable(ColorDrawable(colorAccent), fg))
+ .icon
// Update dot path
- dotPath = PathParser.createPathFromPathData(
- res.getString(com.android.internal.R.string.config_icon_mask))
- val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable,
- null /* outBounds */, null /* path */, null /* outMaskShape */)
+ dotPath =
+ PathParser.createPathFromPathData(
+ res.getString(com.android.internal.R.string.config_icon_mask)
+ )
+ val scale =
+ iconFactory.normalizer.getScale(
+ iconView!!.iconDrawable,
+ null /* outBounds */,
+ null /* path */,
+ null /* outMaskShape */
+ )
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */)
+ matrix.setScale(
+ scale /* x scale */,
+ scale /* y scale */,
+ radius /* pivot x */,
+ radius /* pivot y */
+ )
dotPath.transform(matrix)
// Attach BubbleOverflow to BadgedImageView
@@ -132,8 +152,12 @@
}
fun createExpandedView(): BubbleExpandedView? {
- expandedView = inflater.inflate(R.layout.bubble_expanded_view,
- null /* root */, false /* attachToRoot */) as BubbleExpandedView
+ expandedView =
+ inflater.inflate(
+ R.layout.bubble_expanded_view,
+ null /* root */,
+ false /* attachToRoot */
+ ) as BubbleExpandedView
expandedView?.applyThemeAttrs()
updateResources()
return expandedView
@@ -177,11 +201,15 @@
override fun getIconView(): BadgedImageView? {
if (overflowBtn == null) {
- overflowBtn = inflater.inflate(R.layout.bubble_overflow_button,
- null /* root */, false /* attachToRoot */) as BadgedImageView
+ overflowBtn =
+ inflater.inflate(
+ R.layout.bubble_overflow_button,
+ null /* root */,
+ false /* attachToRoot */
+ ) as BadgedImageView
overflowBtn?.initialize(positioner)
- overflowBtn?.contentDescription = context.resources.getString(
- R.string.bubble_overflow_button_content_description)
+ overflowBtn?.contentDescription =
+ context.resources.getString(R.string.bubble_overflow_button_content_description)
val bubbleSize = positioner.bubbleSize
overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize)
updateBtnTheme()
@@ -200,4 +228,4 @@
companion object {
const val KEY = "Overflow"
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 9aa285f..9655470 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -220,8 +220,8 @@
: res.getColor(R.color.bubbles_light));
final TypedArray typedArray = getContext().obtainStyledAttributes(new int[] {
- android.R.attr.colorBackgroundFloating,
- android.R.attr.textColorSecondary});
+ com.android.internal.R.attr.materialColorSurfaceBright,
+ com.android.internal.R.attr.materialColorOnSurface});
int bgColor = typedArray.getColor(0, isNightMode ? Color.BLACK : Color.WHITE);
int textColor = typedArray.getColor(1, isNightMode ? Color.WHITE : Color.BLACK);
textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, isNightMode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2afe8ee..b9ff5f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -282,6 +282,11 @@
/** Whether the expanded view has been hidden, because we are dragging out a bubble. */
private boolean mExpandedViewTemporarilyHidden = false;
+ /**
+ * Whether the last bubble is being removed when expanded, which impacts the collapse animation.
+ */
+ private boolean mRemovingLastBubbleWhileExpanded = false;
+
/** Animator for animating the expanded view's alpha (including the TaskView inside it). */
private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
@@ -765,7 +770,7 @@
// Update scrim
if (!mScrimAnimating) {
- showScrim(true);
+ showScrim(true, null /* runnable */);
}
}
}
@@ -880,6 +885,7 @@
final Runnable onBubbleAnimatedOut = () -> {
if (getBubbleCount() == 0) {
+ mExpandedViewTemporarilyHidden = false;
mBubbleController.onAllBubblesAnimatedOut();
}
};
@@ -1773,6 +1779,20 @@
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "removeBubble: " + bubble);
}
+ if (isExpanded() && getBubbleCount() == 1) {
+ mRemovingLastBubbleWhileExpanded = true;
+ // We're expanded while the last bubble is being removed. Let the scrim animate away
+ // and then remove our views (removing the icon view triggers the removal of the
+ // bubble window so do that at the end of the animation so we see the scrim animate).
+ showScrim(false, () -> {
+ mRemovingLastBubbleWhileExpanded = false;
+ bubble.cleanupExpandedView();
+ mBubbleContainer.removeView(bubble.getIconView());
+ bubble.cleanupViews(); // cleans up the icon view
+ updateExpandedView(); // resets state for no expanded bubble
+ });
+ return;
+ }
// Remove it from the views
for (int i = 0; i < getBubbleCount(); i++) {
View v = mBubbleContainer.getChildAt(i);
@@ -2143,7 +2163,7 @@
mExpandedViewAlphaAnimator.start();
}
- private void showScrim(boolean show) {
+ private void showScrim(boolean show, Runnable after) {
AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -2153,6 +2173,9 @@
@Override
public void onAnimationEnd(Animator animation) {
mScrimAnimating = false;
+ if (after != null) {
+ after.run();
+ }
}
};
if (show) {
@@ -2179,7 +2202,7 @@
}
beforeExpandedViewAnimation();
- showScrim(true);
+ showScrim(true, null /* runnable */);
updateZOrder();
updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -2303,7 +2326,10 @@
mIsExpanded = false;
mIsExpansionAnimating = true;
- showScrim(false);
+ if (!mRemovingLastBubbleWhileExpanded) {
+ // When we remove the last bubble it animates the scrim.
+ showScrim(false, null /* runnable */);
+ }
mBubbleContainer.cancelAllAnimations();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 0f9260c..9abf0f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -38,7 +38,7 @@
default void onTaskStackChanged() { }
- default void onTaskProfileLocked(RunningTaskInfo taskInfo) { }
+ default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { }
default void onTaskDisplayChanged(int taskId, int newDisplayId) { }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
index e2106e4..d8859ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java
@@ -150,8 +150,8 @@
}
@Override
- public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) {
- mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget();
+ public void onTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo, int userId) {
+ mMainHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
}
@Override
@@ -348,8 +348,9 @@
case ON_TASK_PROFILE_LOCKED: {
final ActivityManager.RunningTaskInfo
info = (ActivityManager.RunningTaskInfo) msg.obj;
+ final int userId = msg.arg1;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(info);
+ mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
}
break;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
index b0dea72..d27d05b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
@@ -35,6 +35,7 @@
private String mKey; // Same key as the Notification
private int mFlags; // Flags from BubbleMetadata
+ @Nullable
private String mShortcutId;
private int mUserId;
private String mPackageName;
@@ -46,7 +47,7 @@
@Nullable
private Icon mIcon;
- public BubbleInfo(String key, int flags, String shortcutId, @Nullable Icon icon,
+ public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName) {
mKey = key;
mFlags = flags;
@@ -69,10 +70,12 @@
return mKey;
}
+ @Nullable
public String getShortcutId() {
return mShortcutId;
}
+ @Nullable
public Icon getIcon() {
return mIcon;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 9eba5ec..e7dede7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -738,10 +738,6 @@
getRefBounds2(mTempRect);
t.setPosition(leash2, mTempRect.left, mTempRect.top)
.setWindowCrop(leash2, mTempRect.width(), mTempRect.height());
- // Make right or bottom side surface always higher than left or top side to avoid weird
- // animation when dismiss split. e.g. App surface fling above on decor surface.
- t.setLayer(leash1, 1);
- t.setLayer(leash2, 2);
if (mImePositionProcessor.adjustSurfaceLayoutForIme(
t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
index c53e638..05fd5f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogLayout.java
@@ -95,6 +95,9 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ final View checkboxContainer = findViewById(
+ R.id.letterbox_restart_dialog_checkbox_container);
+ final CheckBox checkbox = findViewById(R.id.letterbox_restart_dialog_checkbox);
mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container);
mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title);
mBackgroundDim = getBackground().mutate();
@@ -103,5 +106,6 @@
// We add a no-op on-click listener to the dialog container so that clicks on it won't
// propagate to the listener of the layout (which represents the background dim).
mDialogContainer.setOnClickListener(view -> {});
+ checkboxContainer.setOnClickListener(view -> checkbox.performClick());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index e9957fd..12d51f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -41,11 +41,11 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
-import java.util.Optional;
-
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -81,7 +81,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9808c59..80e920f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -186,15 +186,15 @@
@WMSingleton
@Provides
- static DragAndDropController provideDragAndDropController(Context context,
+ static Optional<DragAndDropController> provideDragAndDropController(Context context,
ShellInit shellInit,
ShellController shellController,
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
@ShellMainThread ShellExecutor mainExecutor) {
- return new DragAndDropController(context, shellInit, shellController, displayController,
- uiEventLogger, iconProvider, mainExecutor);
+ return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController,
+ displayController, uiEventLogger, iconProvider, mainExecutor));
}
@WMSingleton
@@ -544,13 +544,14 @@
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
- @ShellAnimationThread ShellExecutor animExecutor) {
+ @ShellAnimationThread ShellExecutor animExecutor,
+ ShellCommandHandler shellCommandHandler) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
return new Transitions(context, shellInit, shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor);
+ displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler);
}
@WMSingleton
@@ -796,7 +797,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropControllerOptional,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 2f0f56c..f3130d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -171,7 +171,7 @@
BubblePositioner positioner,
DisplayController displayController,
@DynamicOverride Optional<OneHandedController> oneHandedOptional,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
@@ -320,7 +320,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 86ea725..b9d2be2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -264,10 +264,10 @@
/**
* Show apps on desktop
*/
- void showDesktopApps() {
+ void showDesktopApps(int displayId) {
// Bring apps to front, ignoring their visibility status to always ensure they are on top.
WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(wct);
+ bringDesktopAppsToFront(displayId, wct);
if (!wct.isEmpty()) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -280,12 +280,12 @@
}
/** Get number of tasks that are marked as visible */
- int getVisibleTaskCount() {
- return mDesktopModeTaskRepository.getVisibleTaskCount();
+ int getVisibleTaskCount(int displayId) {
+ return mDesktopModeTaskRepository.getVisibleTaskCount(displayId);
}
- private void bringDesktopAppsToFront(WindowContainerTransaction wct) {
- final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
+ private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) {
+ final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId);
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
final List<RunningTaskInfo> taskInfos = new ArrayList<>();
@@ -386,6 +386,7 @@
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
+ RunningTaskInfo triggerTask = request.getTriggerTask();
// Only do anything if we are in desktop mode and opening/moving-to-front a task/app in
// freeform
if (!DesktopModeStatus.isActive(mContext)) {
@@ -399,16 +400,15 @@
WindowManager.transitTypeToString(request.getType()));
return null;
}
- if (request.getTriggerTask() == null
- || request.getTriggerTask().getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task");
return null;
}
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request);
WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(wct);
- wct.reorder(request.getTriggerTask().token, true /* onTop */);
+ bringDesktopAppsToFront(triggerTask.displayId, wct);
+ wct.reorder(triggerTask.token, true /* onTop */);
return wct;
}
@@ -493,16 +493,17 @@
mController = null;
}
- public void showDesktopApps() {
+ @Override
+ public void showDesktopApps(int displayId) {
executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
- DesktopModeController::showDesktopApps);
+ controller -> controller.showDesktopApps(displayId));
}
@Override
- public int getVisibleTaskCount() throws RemoteException {
+ public int getVisibleTaskCount(int displayId) throws RemoteException {
int[] result = new int[1];
executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
- controller -> result[0] = controller.getVisibleTaskCount(),
+ controller -> result[0] = controller.getVisibleTaskCount(displayId),
true /* blocking */
);
return result[0];
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 055949f..d1760ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -43,6 +43,12 @@
"persist.wm.debug.desktop_mode_2", false);
/**
+ * Flag to indicate whether task resizing is veiled.
+ */
+ private static final boolean IS_VEILED_RESIZE_ENABLED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_veiled_resizing", true);
+
+ /**
* Return {@code true} if desktop mode support is enabled
*/
public static boolean isProto1Enabled() {
@@ -65,6 +71,13 @@
}
/**
+ * Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
+ */
+ public static boolean isVeiledResizeEnabled() {
+ return IS_VEILED_RESIZE_ENABLED;
+ }
+
+ /**
* Check if desktop mode is active
*
* @return {@code true} if active
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 12f8ea2..00cc57f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -20,6 +20,8 @@
import android.util.ArrayMap
import android.util.ArraySet
import android.util.SparseArray
+import androidx.core.util.forEach
+import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -29,14 +31,18 @@
*/
class DesktopModeTaskRepository {
- /**
- * Set of task ids that are marked as active in desktop mode.
- * Active tasks in desktop mode are freeform tasks that are visible or have been visible after
- * desktop mode was activated.
- * Task gets removed from this list when it vanishes. Or when desktop mode is turned off.
- */
- private val activeTasks = ArraySet<Int>()
- private val visibleTasks = ArraySet<Int>()
+ /** Task data that is tracked per display */
+ private data class DisplayData(
+ /**
+ * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode
+ * are freeform tasks that are visible or have been visible after desktop mode was
+ * activated. Task gets removed from this list when it vanishes. Or when desktop mode is
+ * turned off.
+ */
+ val activeTasks: ArraySet<Int> = ArraySet(),
+ val visibleTasks: ArraySet<Int> = ArraySet(),
+ )
+
// Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
private val freeformTasksInZOrder = mutableListOf<Int>()
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
@@ -47,9 +53,22 @@
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
- /**
- * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository.
- */
+ private val displayData =
+ object : SparseArray<DisplayData>() {
+ /**
+ * Get the [DisplayData] associated with this [displayId]
+ *
+ * Creates a new instance if one does not exist
+ */
+ fun getOrCreate(displayId: Int): DisplayData {
+ if (!contains(displayId)) {
+ put(displayId, DisplayData())
+ }
+ return get(displayId)
+ }
+ }
+
+ /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */
fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) {
activeTasksListeners.add(activeTasksListener)
}
@@ -57,10 +76,17 @@
/**
* Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not.
*/
- fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
- visibleTasksListeners.put(visibleTasksListener, executor)
- executor.execute(
- Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) })
+ fun addVisibleTasksListener(
+ visibleTasksListener: VisibleTasksListener,
+ executor: Executor
+ ) {
+ visibleTasksListeners[visibleTasksListener] = executor
+ displayData.keyIterator().forEach { displayId ->
+ val visibleTasks = getVisibleTaskCount(displayId)
+ executor.execute {
+ visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0)
+ }
+ }
}
/**
@@ -100,14 +126,21 @@
}
/**
- * Mark a task with given [taskId] as active.
+ * Mark a task with given [taskId] as active on given [displayId]
*
- * @return `true` if the task was not active
+ * @return `true` if the task was not active on given [displayId]
*/
- fun addActiveTask(taskId: Int): Boolean {
- val added = activeTasks.add(taskId)
+ fun addActiveTask(displayId: Int, taskId: Int): Boolean {
+ // Check if task is active on another display, if so, remove it
+ displayData.forEach { id, data ->
+ if (id != displayId && data.activeTasks.remove(taskId)) {
+ activeTasksListeners.onEach { it.onActiveTasksChanged(id) }
+ }
+ }
+
+ val added = displayData.getOrCreate(displayId).activeTasks.add(taskId)
if (added) {
- activeTasksListeners.onEach { it.onActiveTasksChanged() }
+ activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
}
return added
}
@@ -118,65 +151,93 @@
* @return `true` if the task was active
*/
fun removeActiveTask(taskId: Int): Boolean {
- val removed = activeTasks.remove(taskId)
- if (removed) {
- activeTasksListeners.onEach { it.onActiveTasksChanged() }
+ var result = false
+ displayData.forEach { displayId, data ->
+ if (data.activeTasks.remove(taskId)) {
+ activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
+ result = true
+ }
}
- return removed
+ return result
}
/**
* Check if a task with the given [taskId] was marked as an active task
*/
fun isActiveTask(taskId: Int): Boolean {
- return activeTasks.contains(taskId)
+ return displayData.valueIterator().asSequence().any { data ->
+ data.activeTasks.contains(taskId)
+ }
}
/**
* Whether a task is visible.
*/
fun isVisibleTask(taskId: Int): Boolean {
- return visibleTasks.contains(taskId)
+ return displayData.valueIterator().asSequence().any { data ->
+ data.visibleTasks.contains(taskId)
+ }
}
/**
- * Get a set of the active tasks
+ * Get a set of the active tasks for given [displayId]
*/
- fun getActiveTasks(): ArraySet<Int> {
- return ArraySet(activeTasks)
+ fun getActiveTasks(displayId: Int): ArraySet<Int> {
+ return ArraySet(displayData[displayId]?.activeTasks)
}
/**
* Get a list of freeform tasks, ordered from top-bottom (top at index 0).
*/
+ // TODO(b/278084491): pass in display id
fun getFreeformTasksInZOrder(): List<Int> {
return freeformTasksInZOrder
}
/**
* Updates whether a freeform task with this id is visible or not and notifies listeners.
+ *
+ * If the task was visible on a different display with a different displayId, it is removed from
+ * the set of visible tasks on that display. Listeners will be notified.
*/
- fun updateVisibleFreeformTasks(taskId: Int, visible: Boolean) {
- val prevCount: Int = visibleTasks.size
+ fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) {
if (visible) {
- visibleTasks.add(taskId)
- } else {
- visibleTasks.remove(taskId)
- }
- if (prevCount == 0 && visibleTasks.size == 1 ||
- prevCount > 0 && visibleTasks.size == 0) {
- for ((listener, executor) in visibleTasksListeners) {
- executor.execute(
- Runnable { listener.onVisibilityChanged(visibleTasks.size > 0) })
+ // Task is visible. Check if we need to remove it from any other display.
+ val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId }
+ for (otherDisplayId in otherDisplays) {
+ if (displayData[otherDisplayId].visibleTasks.remove(taskId)) {
+ // Task removed from other display, check if we should notify listeners
+ if (displayData[otherDisplayId].visibleTasks.isEmpty()) {
+ notifyVisibleTaskListeners(otherDisplayId, hasVisibleFreeformTasks = false)
+ }
+ }
}
}
+
+ val prevCount = getVisibleTaskCount(displayId)
+ if (visible) {
+ displayData.getOrCreate(displayId).visibleTasks.add(taskId)
+ } else {
+ displayData[displayId]?.visibleTasks?.remove(taskId)
+ }
+ val newCount = getVisibleTaskCount(displayId)
+ // Check if count changed and if there was no tasks or this is the first task
+ if (prevCount != newCount && (prevCount == 0 || newCount == 0)) {
+ notifyVisibleTaskListeners(displayId, newCount > 0)
+ }
+ }
+
+ private fun notifyVisibleTaskListeners(displayId: Int, hasVisibleFreeformTasks: Boolean) {
+ visibleTasksListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onVisibilityChanged(displayId, hasVisibleFreeformTasks) }
+ }
}
/**
- * Get number of tasks that are marked as visible
+ * Get number of tasks that are marked as visible on given [displayId]
*/
- fun getVisibleTaskCount(): Int {
- return visibleTasks.size
+ fun getVisibleTaskCount(displayId: Int): Int {
+ return displayData[displayId]?.visibleTasks?.size ?: 0
}
/**
@@ -226,7 +287,7 @@
* Called when the active tasks change in desktop mode.
*/
@JvmDefault
- fun onActiveTasksChanged() {}
+ fun onActiveTasksChanged(displayId: Int) {}
}
/**
@@ -237,6 +298,6 @@
* Called when the desktop starts or stops showing freeform tasks.
*/
@JvmDefault
- fun onVisibilityChanged(hasVisibleFreeformTasks: Boolean) {}
+ fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 0d56023..c814fe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -97,10 +97,11 @@
}
/** Show all tasks, that are part of the desktop, on top of launcher */
- fun showDesktopApps() {
+ fun showDesktopApps(displayId: Int) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps")
val wct = WindowContainerTransaction()
- bringDesktopAppsToFront(wct)
+ // TODO(b/278084491): pass in display id
+ bringDesktopAppsToFront(displayId, wct)
// Execute transaction if there are pending operations
if (!wct.isEmpty) {
@@ -114,8 +115,8 @@
}
/** Get number of tasks that are marked as visible */
- fun getVisibleTaskCount(): Int {
- return desktopModeTaskRepository.getVisibleTaskCount()
+ fun getVisibleTaskCount(displayId: Int): Int {
+ return desktopModeTaskRepository.getVisibleTaskCount(displayId)
}
/** Move a task with given `taskId` to desktop */
@@ -129,7 +130,7 @@
val wct = WindowContainerTransaction()
// Bring other apps to front first
- bringDesktopAppsToFront(wct)
+ bringDesktopAppsToFront(task.displayId, wct)
addMoveToDesktopChanges(wct, task.token)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
@@ -165,7 +166,7 @@
freeformBounds: Rect
) {
val wct = WindowContainerTransaction()
- bringDesktopAppsToFront(wct)
+ bringDesktopAppsToFront(taskInfo.displayId, wct)
addMoveToDesktopChanges(wct, taskInfo.getToken())
wct.setBounds(taskInfo.token, freeformBounds)
@@ -244,9 +245,9 @@
?: WINDOWING_MODE_UNDEFINED
}
- private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) {
+ private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront")
- val activeTasks = desktopModeTaskRepository.getActiveTasks()
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)
// First move home to front and then other tasks on top of it
moveHomeTaskToFront(wct)
@@ -290,18 +291,17 @@
request: TransitionRequestInfo
): WindowContainerTransaction? {
// Check if we should skip handling this transition
- val task: RunningTaskInfo? = request.triggerTask
val shouldHandleRequest =
when {
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
// Only handle when it is a task transition
- task == null -> false
+ request.triggerTask == null -> false
// Only handle standard type tasks
- task.activityType != ACTIVITY_TYPE_STANDARD -> false
+ request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false
// Only handle fullscreen or freeform tasks
- task.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- task.windowingMode != WINDOWING_MODE_FREEFORM -> false
+ request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
+ request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false
// Otherwise process it
else -> true
}
@@ -310,10 +310,11 @@
return null
}
- val activeTasks = desktopModeTaskRepository.getActiveTasks()
+ val task: RunningTaskInfo = request.triggerTask
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
// Check if we should switch a fullscreen task to freeform
- if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) {
// If there are any visible desktop tasks, switch the task to freeform
if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
ProtoLog.d(
@@ -329,7 +330,7 @@
}
// CHeck if we should switch a freeform task to fullscreen
- if (task?.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (task.windowingMode == WINDOWING_MODE_FREEFORM) {
// If no visible desktop tasks, switch this task to freeform as the transition came
// outside of this controller
if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
@@ -559,20 +560,19 @@
controller = null
}
- override fun showDesktopApps() {
+ override fun showDesktopApps(displayId: Int) {
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
- "showDesktopApps",
- Consumer(DesktopTasksController::showDesktopApps)
- )
+ "showDesktopApps"
+ ) { c -> c.showDesktopApps(displayId) }
}
- override fun getVisibleTaskCount(): Int {
+ override fun getVisibleTaskCount(displayId: Int): Int {
val result = IntArray(1)
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
"getVisibleTaskCount",
- { controller -> result[0] = controller.getVisibleTaskCount() },
+ { controller -> result[0] = controller.getVisibleTaskCount(displayId) },
true /* blocking */
)
return result[0]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index d0739e1..899d672 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -21,9 +21,9 @@
*/
interface IDesktopMode {
- /** Show apps on the desktop */
- void showDesktopApps();
+ /** Show apps on the desktop on the given display */
+ void showDesktopApps(int displayId);
- /** Get count of visible desktop tasks */
- int getVisibleTaskCount();
+ /** Get count of visible desktop tasks on the given display */
+ int getVisibleTaskCount(int displayId);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 4cfaae6..091de3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -94,7 +94,24 @@
void onDragStarted();
}
- public DragAndDropController(Context context,
+ /**
+ * Creates {@link DragAndDropController}. Returns {@code null} if the feature is disabled.
+ */
+ public static DragAndDropController create(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ UiEventLogger uiEventLogger,
+ IconProvider iconProvider,
+ ShellExecutor mainExecutor) {
+ if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) {
+ return null;
+ }
+ return new DragAndDropController(context, shellInit, shellController, displayController,
+ uiEventLogger, iconProvider, mainExecutor);
+ }
+
+ DragAndDropController(Context context,
ShellInit shellInit,
ShellController shellController,
DisplayController displayController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 48487bc..22541bbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -94,11 +94,12 @@
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
if (taskInfo.isVisible) {
- if (repository.addActiveTask(taskInfo.taskId)) {
+ if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, true);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
+ true);
}
});
}
@@ -117,7 +118,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Removing active freeform task: #%d", taskInfo.taskId);
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, false);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false);
});
}
@@ -137,12 +138,13 @@
if (DesktopModeStatus.isAnyEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
if (taskInfo.isVisible) {
- if (repository.addActiveTask(taskInfo.taskId)) {
+ if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
}
}
- repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible);
+ repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
+ taskInfo.isVisible);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 566c130..d3e7f9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -72,7 +72,6 @@
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransactionCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -139,17 +138,6 @@
// the runnable to execute after WindowContainerTransactions is applied to finish resizing pip
private Runnable mPipFinishResizeWCTRunnable;
- private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback =
- new WindowContainerTransactionCallback() {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- t.apply();
-
- // execute the runnable if non-null after WCT is applied to finish resizing pip
- maybePerformFinishResizeCallback();
- }
- };
-
private void maybePerformFinishResizeCallback() {
if (mPipFinishResizeWCTRunnable != null) {
mPipFinishResizeWCTRunnable.run();
@@ -175,6 +163,7 @@
final int direction = animator.getTransitionDirection();
if (mIsCancelled) {
sendOnPipTransitionFinished(direction);
+ maybePerformFinishResizeCallback();
return;
}
final int animationType = animator.getAnimationType();
@@ -199,6 +188,10 @@
|| isRemovePipDirection(direction);
if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
|| isExitPipDirection) {
+ // execute the finish resize callback if needed after the transaction is committed
+ tx.addTransactionCommittedListener(mMainExecutor,
+ PipTaskOrganizer.this::maybePerformFinishResizeCallback);
+
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP
@@ -1614,12 +1607,8 @@
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSplitScreenOptional.ifPresent(splitScreenController ->
splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
- } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
- // when leaving PiP we can call the callback without sync
- maybePerformFinishResizeCallback();
- mTaskOrganizer.applyTransaction(wct);
} else {
- mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
+ mTaskOrganizer.applyTransaction(wct);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index b0bb14b..f8e1435 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1060,13 +1060,22 @@
/** Save the state to restore to on re-entry. */
public void saveReentryState(Rect pipBounds) {
float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds);
- if (mPipBoundsState.hasUserResizedPip()) {
- final Rect reentryBounds = mTouchHandler.getUserResizeBounds();
- final Size reentrySize = new Size(reentryBounds.width(), reentryBounds.height());
- mPipBoundsState.saveReentryState(reentrySize, snapFraction);
- } else {
+
+ if (!mPipBoundsState.hasUserResizedPip()) {
mPipBoundsState.saveReentryState(null /* bounds */, snapFraction);
+ return;
}
+
+ Size reentrySize = new Size(pipBounds.width(), pipBounds.height());
+
+ // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on
+ // fallback to using the userResizeBounds if userResizeBounds are not empty
+ if (!mTouchHandler.getUserResizeBounds().isEmpty()) {
+ Rect userResizeBounds = mTouchHandler.getUserResizeBounds();
+ reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height());
+ }
+
+ mPipBoundsState.saveReentryState(reentrySize, snapFraction);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 956af70..281cae5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -104,6 +104,7 @@
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
+ private boolean mEnableTouch;
private boolean mEnablePinchResize;
private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
@@ -138,6 +139,7 @@
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
+ mEnableTouch = true;
mUpdateResizeBoundsCallback = (rect) -> {
mUserResizeBounds.set(rect);
@@ -248,6 +250,11 @@
return;
}
+ if (!mEnableTouch) {
+ // No need to handle anything if touches are not enabled for resizing.
+ return;
+ }
+
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
@@ -581,14 +588,13 @@
mLastResizeBounds, movementBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
- // disable the pinch resizing until the final bounds are updated
- final boolean prevEnablePinchResize = mEnablePinchResize;
- mEnablePinchResize = false;
+ // disable the resizing until the final bounds are updated
+ mEnableTouch = false;
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
// reset the pinch resizing to its default state
- mEnablePinchResize = prevEnablePinchResize;
+ mEnableTouch = true;
});
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index c5bfd87..5c9709c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -245,7 +245,7 @@
}
@Override
- public void onActiveTasksChanged() {
+ public void onActiveTasksChanged(int displayId) {
notifyRecentTasksChanged();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index eb4d2a1..bffc51c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -549,6 +549,14 @@
// are in mOpening.
for (int i = 0; i < closingTasks.size(); ++i) {
final TransitionInfo.Change change = closingTasks.get(i);
+ final int pausingIdx = TaskState.indexOf(mPausingTasks, change);
+ if (pausingIdx >= 0) {
+ mPausingTasks.remove(pausingIdx);
+ didMergeThings = true;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " closing pausing taskId=%d", change.getTaskInfo().taskId);
+ continue;
+ }
int openingIdx = TaskState.indexOf(mOpeningTasks, change);
if (openingIdx < 0) {
Slog.w(TAG, "Closing a task that wasn't opening, this may be split or"
@@ -601,6 +609,11 @@
didMergeThings = true;
mState = STATE_NEW_TASK;
}
+ if (mPausingTasks.isEmpty()) {
+ // The pausing tasks may be removed by the incoming closing tasks.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: empty pausing tasks", mInstanceId);
+ }
if (!hasTaskChange) {
// Activity only transition, so consume the merge as it doesn't affect the rest of
// recents.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6432459..af52350 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -165,7 +165,7 @@
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
- private final DragAndDropController mDragAndDropController;
+ private final Optional<DragAndDropController> mDragAndDropController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final IconProvider mIconProvider;
@@ -191,7 +191,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
@@ -253,7 +253,7 @@
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
- mDragAndDropController = dragAndDropController;
+ mDragAndDropController = Optional.of(dragAndDropController);
mTransitions = transitions;
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
@@ -289,7 +289,7 @@
// TODO: Multi-display
mStageCoordinator = createStageCoordinator();
}
- mDragAndDropController.setSplitScreenController(this);
+ mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this));
}
protected StageCoordinator createStageCoordinator() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 51b8000..a2af93f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -30,8 +30,6 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -165,7 +163,7 @@
t.setLayer(leash, Integer.MAX_VALUE);
t.show(leash);
}
- boolean isOpening = isOpeningTransition(info);
+ boolean isOpening = TransitionUtil.isOpeningType(info.getType());
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
// fade in
startExampleAnimation(leash, true /* show */);
@@ -295,14 +293,16 @@
@Nullable RemoteTransition remoteTransition,
Transitions.TransitionHandler handler,
@Nullable TransitionConsumedCallback consumedCallback,
- @Nullable TransitionFinishedCallback finishedCallback) {
+ @Nullable TransitionFinishedCallback finishedCallback,
+ int extraTransitType) {
if (mPendingEnter != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
return null;
}
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback);
+ setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback,
+ extraTransitType);
return transition;
}
@@ -310,9 +310,10 @@
void setEnterTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionConsumedCallback consumedCallback,
- @Nullable TransitionFinishedCallback finishedCallback) {
+ @Nullable TransitionFinishedCallback finishedCallback,
+ int extraTransitType) {
mPendingEnter = new TransitSession(
- transition, consumedCallback, finishedCallback, remoteTransition);
+ transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter split screen");
@@ -513,12 +514,6 @@
mTransitions.getAnimExecutor().execute(va::start);
}
- private boolean isOpeningTransition(TransitionInfo info) {
- return TransitionUtil.isOpeningType(info.getType())
- || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
- || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
- }
-
/** Calls when the transition got consumed. */
interface TransitionConsumedCallback {
void onConsumed(boolean aborted);
@@ -539,16 +534,19 @@
/** Whether the transition was canceled. */
boolean mCanceled;
+ /** A note for extra transit type, to help indicate custom transition. */
+ final int mExtraTransitType;
+
TransitSession(IBinder transition,
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishedCallback) {
- this(transition, consumedCallback, finishedCallback, null /* remoteTransition */);
+ this(transition, consumedCallback, finishedCallback, null /* remoteTransition */, 0);
}
TransitSession(IBinder transition,
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishedCallback,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition, int extraTransitType) {
mTransition = transition;
mConsumedCallback = consumedCallback;
mFinishedCallback = finishedCallback;
@@ -560,6 +558,7 @@
mTransitions.getMainExecutor(), remoteTransition);
mRemoteHandler.setTransition(transition);
}
+ mExtraTransitType = extraTransitType;
}
/** Sets transition consumed callback. */
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 4c903f5..0ef26fc 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
@@ -446,26 +446,10 @@
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
- boolean openingToSide = false;
- if (apps != null) {
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING
- && mSideStage.containsTask(apps[i].taskId)) {
- openingToSide = true;
- break;
- }
- }
- } else if (mSideStage.getChildCount() != 0) {
- // There are chances the entering app transition got canceled by performing
- // rotation transition. Checks if there is any child task existed in split
- // screen before fallback to cancel entering flow.
- openingToSide = true;
- }
-
- if (isEnteringSplit && !openingToSide) {
+ if (isEnteringSplit && mSideStage.getChildCount() == 0) {
mMainExecutor.execute(() -> exitSplitScreen(
- mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
- EXIT_REASON_UNKNOWN));
+ null /* childrenToTop */, EXIT_REASON_UNKNOWN));
+ mSplitUnsupportedToast.show();
}
if (finishedCallback != null) {
@@ -526,17 +510,17 @@
wct.sendPendingIntent(intent, fillInIntent, options);
// If split screen is not activated, we're expecting to open a pair of apps to split.
- final int transitType = mMainStage.isActive()
+ final int extraTransitType = mMainStage.isActive()
? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
prepareEnterSplitScreen(wct, null /* taskInfo */, position);
- mSplitTransitions.startEnterTransition(transitType, wct, null, this,
+ mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this,
null /* consumedCallback */,
(finishWct, finishT) -> {
if (!evictWct.isEmpty()) {
finishWct.merge(evictWct, true);
}
- } /* finishedCallback */);
+ } /* finishedCallback */, extraTransitType);
}
/** Launches an activity into split by legacy transition. */
@@ -550,26 +534,10 @@
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
- boolean openingToSide = false;
- if (apps != null) {
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING
- && mSideStage.containsTask(apps[i].taskId)) {
- openingToSide = true;
- break;
- }
- }
- } else if (mSideStage.getChildCount() != 0) {
- // There are chances the entering app transition got canceled by performing
- // rotation transition. Checks if there is any child task existed in split
- // screen before fallback to cancel entering flow.
- openingToSide = true;
- }
-
- if (isEnteringSplit && !openingToSide && apps != null) {
+ if (isEnteringSplit && mSideStage.getChildCount() == 0) {
mMainExecutor.execute(() -> exitSplitScreen(
- mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
- EXIT_REASON_UNKNOWN));
+ null /* childrenToTop */, EXIT_REASON_UNKNOWN));
+ mSplitUnsupportedToast.show();
}
if (apps != null) {
@@ -709,7 +677,8 @@
wct.startTask(mainTaskId, mainOptions);
mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null);
+ TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
setEnterInstanceId(instanceId);
}
@@ -760,7 +729,8 @@
}
mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null);
+ TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
setEnterInstanceId(instanceId);
}
@@ -1088,7 +1058,7 @@
private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- mSplitRequest = null;
+ clearRequestIfPresented();
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
// multi-instance, we should exit split and expand that app as full screen.
@@ -1108,7 +1078,7 @@
private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- mSplitRequest = null;
+ clearRequestIfPresented();
// 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.
@@ -1381,6 +1351,7 @@
});
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = false;
+ mSplitRequest = null;
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
@@ -1473,6 +1444,13 @@
}
}
+ private void clearRequestIfPresented() {
+ if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
+ && mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
+ mSplitRequest = null;
+ }
+ }
+
/**
* Returns whether the split pair in the recent tasks list should be broken.
*/
@@ -1851,6 +1829,7 @@
true /* setReparentLeafTaskIfRelaunch */);
setRootForceTranslucent(true, wct);
} else {
+ clearRequestIfPresented();
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* setReparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
@@ -2010,7 +1989,7 @@
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
- mSplitRequest = null;
+ clearRequestIfPresented();
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
@@ -2334,7 +2313,8 @@
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
- null /* consumedCallback */, null /* finishedCallback */);
+ null /* consumedCallback */, null /* finishedCallback */,
+ 0 /* extraTransitType */);
}
}
return out;
@@ -2573,7 +2553,8 @@
}
}
- if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ if (mSplitTransitions.mPendingEnter.mExtraTransitType
+ == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
if (mainChild == null && sideChild == null) {
Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */);
@@ -2716,6 +2697,7 @@
}
});
mShouldUpdateRecents = false;
+ mSplitRequest = null;
// Update local states.
setSplitsVisible(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index ead0bcd..a841b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -220,20 +220,12 @@
mCallbacks.onNoLongerSupportMultiWindow();
return;
}
- if (taskInfo.topActivity == null && mChildrenTaskInfo.contains(taskInfo.taskId)
- && mChildrenTaskInfo.get(taskInfo.taskId).topActivity != null) {
- // If top activity become null, it means the task is about to vanish, we use this
- // signal to remove it from children list earlier for smooth dismiss transition.
- mChildrenTaskInfo.remove(taskInfo.taskId);
- mChildrenLeashes.remove(taskInfo.taskId);
- } else {
- mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
- }
+ mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
taskInfo.isVisible);
- if (!ENABLE_SHELL_TRANSITIONS && mChildrenLeashes.contains(taskInfo.taskId)) {
- updateChildTaskSurface(taskInfo, mChildrenLeashes.get(taskInfo.taskId),
- false /* firstAppeared */);
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ updateChildTaskSurface(
+ taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
}
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
@@ -267,6 +259,9 @@
return;
}
sendStatusChanged();
+ } else {
+ throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ + "\n mRootTaskInfo: " + mRootTaskInfo);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 46d2a5a..27d520d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -72,7 +72,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 7991c52..2ab4c75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -82,6 +82,10 @@
mGuard.open("release");
}
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
/**
* Sets the provided {@link TaskViewBase}, which is used to notify the client part about the
* task related changes and getting the current bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 81d69a4..c7e534a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -336,6 +336,19 @@
tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction,
chg.getTaskInfo(), chg.getLeash(), wct);
changesHandled++;
+ } else if (chg.getMode() == TRANSIT_CHANGE) {
+ TaskViewTaskController tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ if (pending != null) {
+ Slog.w(TAG, "Found a non-TaskView task in a TaskView Transition. This "
+ + "shouldn't happen, so there may be a visual artifact: "
+ + chg.getTaskInfo().taskId);
+ }
+ continue;
+ }
+ startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+ finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+ changesHandled++;
}
}
if (stillNeedsMatchingLaunch) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 5b7231c..ef2a511 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -94,9 +94,11 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (!Transitions.SHELL_TRANSITIONS_ROTATION && TransitionUtil.hasDisplayChange(info)) {
+ if (!Transitions.SHELL_TRANSITIONS_ROTATION && TransitionUtil.hasDisplayChange(info)
+ && !TransitionUtil.alwaysReportToKeyguard(info)) {
// Note that if the remote doesn't have permission ACCESS_SURFACE_FLINGER, some
// operations of the start transaction may be ignored.
+ mRequestedRemotes.remove(transition);
return false;
}
RemoteTransition pendingRemote = mRequestedRemotes.get(transition);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
new file mode 100644
index 0000000..ba364f8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.os.Build.IS_USER;
+
+import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER;
+import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_H;
+import static com.android.wm.shell.WmShellTransitionTraceProto.MAGIC_NUMBER_L;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.TraceBuffer;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to collect and dump transition traces.
+ */
+public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
+ private static final int ALWAYS_ON_TRACING_CAPACITY = 15 * 1024; // 15 KB
+ private static final int ACTIVE_TRACING_BUFFER_CAPACITY = 5000 * 1024; // 5 MB
+
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ static final String WINSCOPE_EXT = ".winscope";
+ private static final String TRACE_FILE =
+ "/data/misc/wmtrace/shell_transition_trace" + WINSCOPE_EXT;
+
+ private final Object mEnabledLock = new Object();
+ private boolean mActiveTracingEnabled = false;
+
+ private final TraceBuffer mTraceBuffer = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY,
+ (proto) -> handleOnEntryRemovedFromTrace(proto));
+ private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>();
+
+ private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>();
+ private final Map<Transitions.TransitionHandler, Integer> mHandlerUseCountInTrace =
+ new HashMap<>();
+
+ /**
+ * Adds an entry in the trace to log that a transition has been dispatched to a handler.
+ *
+ * @param transitionId The id of the transition being dispatched.
+ * @param handler The handler the transition is being dispatched to.
+ */
+ public void logDispatched(int transitionId, Transitions.TransitionHandler handler) {
+ final int handlerId;
+ if (mHandlerIds.containsKey(handler)) {
+ handlerId = mHandlerIds.get(handler);
+ } else {
+ // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto
+ handlerId = mHandlerIds.size() + 1;
+ mHandlerIds.put(handler, handlerId);
+ }
+
+ ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long protoToken =
+ outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS);
+
+ outputStream.write(com.android.wm.shell.Transition.ID, transitionId);
+ outputStream.write(com.android.wm.shell.Transition.DISPATCH_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ outputStream.write(com.android.wm.shell.Transition.HANDLER, handlerId);
+
+ outputStream.end(protoToken);
+
+ final int useCountAfterAdd = mHandlerUseCountInTrace.getOrDefault(handler, 0) + 1;
+ mHandlerUseCountInTrace.put(handler, useCountAfterAdd);
+
+ mRemovedFromTraceCallbacks.put(outputStream, () -> {
+ final int useCountAfterRemove = mHandlerUseCountInTrace.get(handler) - 1;
+ mHandlerUseCountInTrace.put(handler, useCountAfterRemove);
+ });
+
+ mTraceBuffer.add(outputStream);
+ }
+
+ /**
+ * Adds an entry in the trace to log that a request to merge a transition was made.
+ *
+ * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
+ * @param playingTransitionId The id of the transition we was to merge the transition into.
+ */
+ public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
+ ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long protoToken =
+ outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS);
+
+ outputStream.write(com.android.wm.shell.Transition.ID, mergeRequestedTransitionId);
+ outputStream.write(com.android.wm.shell.Transition.MERGE_REQUEST_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId);
+
+ outputStream.end(protoToken);
+
+ mTraceBuffer.add(outputStream);
+ }
+
+ /**
+ * Adds an entry in the trace to log that a transition was merged by the handler.
+ *
+ * @param mergedTransitionId The id of the transition that was merged.
+ * @param playingTransitionId The id of the transition the transition was merged into.
+ */
+ public void logMerged(int mergedTransitionId, int playingTransitionId) {
+ ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long protoToken =
+ outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS);
+
+ outputStream.write(com.android.wm.shell.Transition.ID, mergedTransitionId);
+ outputStream.write(
+ com.android.wm.shell.Transition.MERGE_TIME_NS, SystemClock.elapsedRealtimeNanos());
+ outputStream.write(com.android.wm.shell.Transition.MERGED_INTO, playingTransitionId);
+
+ outputStream.end(protoToken);
+
+ mTraceBuffer.add(outputStream);
+ }
+
+ /**
+ * Adds an entry in the trace to log that a transition was aborted.
+ *
+ * @param transitionId The id of the transition that was aborted.
+ */
+ public void logAborted(int transitionId) {
+ ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long protoToken =
+ outputStream.start(com.android.wm.shell.WmShellTransitionTraceProto.TRANSITIONS);
+
+ outputStream.write(com.android.wm.shell.Transition.ID, transitionId);
+ outputStream.write(
+ com.android.wm.shell.Transition.ABORT_TIME_NS, SystemClock.elapsedRealtimeNanos());
+
+ outputStream.end(protoToken);
+
+ mTraceBuffer.add(outputStream);
+ }
+
+ /**
+ * Starts collecting transitions for the trace.
+ * If called while a trace is already running, this will reset the trace.
+ */
+ public void startTrace(@Nullable PrintWriter pw) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("Tracer#startTrace");
+ LogAndPrintln.i(pw, "Starting shell transition trace.");
+ synchronized (mEnabledLock) {
+ mActiveTracingEnabled = true;
+ mTraceBuffer.resetBuffer();
+ mTraceBuffer.setCapacity(ACTIVE_TRACING_BUFFER_CAPACITY);
+ }
+ Trace.endSection();
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ *
+ * Dumps the trace to @link{TRACE_FILE}.
+ */
+ public void stopTrace(@Nullable PrintWriter pw) {
+ stopTrace(pw, new File(TRACE_FILE));
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ * @param outputFile The file to dump the transition trace to.
+ */
+ public void stopTrace(@Nullable PrintWriter pw, File outputFile) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("Tracer#stopTrace");
+ LogAndPrintln.i(pw, "Stopping shell transition trace.");
+ synchronized (mEnabledLock) {
+ mActiveTracingEnabled = false;
+ writeTraceToFileLocked(pw, outputFile);
+ mTraceBuffer.resetBuffer();
+ mTraceBuffer.setCapacity(ALWAYS_ON_TRACING_CAPACITY);
+ }
+ Trace.endSection();
+ }
+
+ /**
+ * Being called while taking a bugreport so that tracing files can be included in the bugreport.
+ *
+ * @param pw Print writer
+ */
+ public void saveForBugreport(@Nullable PrintWriter pw) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("TransitionTracer#saveForBugreport");
+ synchronized (mEnabledLock) {
+ final File outputFile = new File(TRACE_FILE);
+ writeTraceToFileLocked(pw, outputFile);
+ }
+ Trace.endSection();
+ }
+
+ private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) {
+ Trace.beginSection("TransitionTracer#writeTraceToFileLocked");
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ writeHandlerMappingToProto(proto);
+ int pid = android.os.Process.myPid();
+ LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath()
+ + " from process " + pid);
+ mTraceBuffer.writeTraceToFile(file, proto);
+ } catch (IOException e) {
+ LogAndPrintln.e(pw, "Unable to write buffer to file", e);
+ }
+ Trace.endSection();
+ }
+
+ private void writeHandlerMappingToProto(ProtoOutputStream outputStream) {
+ for (Transitions.TransitionHandler handler : mHandlerUseCountInTrace.keySet()) {
+ final int count = mHandlerUseCountInTrace.get(handler);
+ if (count > 0) {
+ final long protoToken = outputStream.start(
+ com.android.wm.shell.WmShellTransitionTraceProto.HANDLER_MAPPINGS);
+ outputStream.write(com.android.wm.shell.HandlerMapping.ID,
+ mHandlerIds.get(handler));
+ outputStream.write(com.android.wm.shell.HandlerMapping.NAME,
+ handler.getClass().getName());
+ outputStream.end(protoToken);
+ }
+ }
+ }
+
+ private void handleOnEntryRemovedFromTrace(Object proto) {
+ if (mRemovedFromTraceCallbacks.containsKey(proto)) {
+ mRemovedFromTraceCallbacks.get(proto).run();
+ mRemovedFromTraceCallbacks.remove(proto);
+ }
+ }
+
+ @Override
+ public boolean onShellCommand(String[] args, PrintWriter pw) {
+ switch (args[0]) {
+ case "start": {
+ startTrace(pw);
+ return true;
+ }
+ case "stop": {
+ stopTrace(pw);
+ return true;
+ }
+ case "save-for-bugreport": {
+ saveForBugreport(pw);
+ return true;
+ }
+ default: {
+ pw.println("Invalid command: " + args[0]);
+ printShellCommandHelp(pw, "");
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public void printShellCommandHelp(PrintWriter pw, String prefix) {
+ pw.println(prefix + "start");
+ pw.println(prefix + " Start tracing the transitions.");
+ pw.println(prefix + "stop");
+ pw.println(prefix + " Stop tracing the transitions.");
+ pw.println(prefix + "save-for-bugreport");
+ pw.println(prefix + " Flush in memory transition trace to file.");
+ }
+
+ private static class LogAndPrintln {
+ private static final String LOG_TAG = "ShellTransitionTracer";
+
+ private static void i(@Nullable PrintWriter pw, String msg) {
+ Log.i(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg) {
+ Log.e(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println("ERROR: " + msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) {
+ Log.e(LOG_TAG, msg, e);
+ if (pw != null) {
+ pw.println("ERROR: " + msg + " ::\n " + e);
+ pw.flush();
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 5c8791e..f79f1f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -74,10 +74,12 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.TransitionUtil;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -106,7 +108,8 @@
* track, it will be marked as SYNC. This means that all currently active tracks must be flushed
* before the SYNC transition can play.
*/
-public class Transitions implements RemoteCallable<Transitions> {
+public class Transitions implements RemoteCallable<Transitions>,
+ ShellCommandHandler.ShellCommandActionHandler {
static final String TAG = "ShellTransitions";
/** Set to {@code true} to enable shell transitions. */
@@ -165,12 +168,15 @@
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
private final SleepHandler mSleepHandler = new SleepHandler();
-
+ private final Tracer mTracer = new Tracer();
private boolean mIsRegistered = false;
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
+ @Nullable
+ private final ShellCommandHandler mShellCommandHandler;
+
private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
/** List of {@link Runnable} instances to run when the last active transition has finished. */
@@ -246,8 +252,23 @@
@NonNull WindowOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
- @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor mainExecutor,
+ @NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor) {
+ this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor,
+ mainHandler, animExecutor, null);
+ }
+
+ public Transitions(@NonNull Context context,
+ @NonNull ShellInit shellInit,
+ @NonNull ShellController shellController,
+ @NonNull WindowOrganizer organizer,
+ @NonNull TransactionPool pool,
+ @NonNull DisplayController displayController,
+ @NonNull ShellExecutor mainExecutor,
+ @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor,
+ @Nullable ShellCommandHandler shellCommandHandler) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
@@ -263,6 +284,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
+ mShellCommandHandler = shellCommandHandler;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
}
@@ -294,6 +316,10 @@
// Pre-load the instance.
TransitionMetrics.getInstance();
}
+
+ if (mShellCommandHandler != null) {
+ mShellCommandHandler.addCommandCallback("transitions", this, this);
+ }
}
public boolean isRegistered() {
@@ -650,7 +676,7 @@
active.mToken, info, active.mStartT, active.mFinishT);
}
- if (info.getRootCount() == 0 && !alwaysReportToKeyguard(info)) {
+ if (info.getRootCount() == 0 && !TransitionUtil.alwaysReportToKeyguard(info)) {
// No root-leashes implies that the transition is empty/no-op, so just do
// housekeeping and return.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
@@ -699,23 +725,6 @@
return true;
}
- /**
- * Some transitions we always need to report to keyguard even if they are empty.
- * TODO (b/274954192): Remove this once keyguard dispatching moves to Shell.
- */
- private static boolean alwaysReportToKeyguard(TransitionInfo info) {
- // occlusion status of activities can change while screen is off so there will be no
- // visibility change but we still need keyguardservice to be notified.
- if (info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) return true;
-
- // It's possible for some activities to stop with bad timing (esp. since we can't yet
- // queue activity transitions initiated by apps) that results in an empty transition for
- // keyguard going-away. In general, we should should always report Keyguard-going-away.
- if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) return true;
-
- return false;
- }
-
private boolean areTracksIdle() {
for (int i = 0; i < mTracks.size(); ++i) {
if (!mTracks.get(i).isIdle()) return false;
@@ -783,6 +792,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ " %s is still animating. Notify the animating transition"
+ " in case they can be merged", ready, playing);
+ mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
playing.mToken, (wct, cb) -> onMerged(playing, ready));
}
@@ -816,6 +826,7 @@
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken);
}
+ mTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
// See if we should merge another transition.
processReadyQueue(track);
}
@@ -842,6 +853,8 @@
// Otherwise give every other handler a chance
active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
active.mFinishT, (wct, cb) -> onFinish(active, wct, cb), active.mHandler);
+
+ mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
}
/**
@@ -893,6 +906,8 @@
transition.mFinishT.apply();
transition.mAborted = true;
+ mTracer.logAborted(transition.mInfo.getDebugId());
+
if (transition.mHandler != null) {
// Notifies to clean-up the aborted transition.
transition.mHandler.onTransitionConsumed(
@@ -1367,4 +1382,26 @@
mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
}
}
+
+
+ @Override
+ public boolean onShellCommand(String[] args, PrintWriter pw) {
+ switch (args[0]) {
+ case "tracing": {
+ mTracer.onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
+ return true;
+ }
+ default: {
+ pw.println("Invalid command: " + args[0]);
+ printShellCommandHelp(pw, "");
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public void printShellCommandHelp(PrintWriter pw, String prefix) {
+ pw.println(prefix + "tracing");
+ mTracer.printShellCommandHelp(pw, prefix + " ");
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index ce10291..c5cd2d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -24,7 +24,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -77,6 +79,23 @@
return false;
}
+ /**
+ * Some transitions we always need to report to keyguard even if they are empty.
+ * TODO (b/274954192): Remove this once keyguard dispatching moves to Shell.
+ */
+ public static boolean alwaysReportToKeyguard(TransitionInfo info) {
+ // occlusion status of activities can change while screen is off so there will be no
+ // visibility change but we still need keyguardservice to be notified.
+ if (info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) return true;
+
+ // It's possible for some activities to stop with bad timing (esp. since we can't yet
+ // queue activity transitions initiated by apps) that results in an empty transition for
+ // keyguard going-away. In general, we should should always report Keyguard-going-away.
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) return true;
+
+ return false;
+ }
+
/** Returns `true` if `change` is a wallpaper. */
public static boolean isWallpaper(TransitionInfo.Change change) {
return (change.getTaskInfo() == null)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index e8a6a15..212ad9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -185,8 +185,8 @@
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- final TaskPositioner taskPositioner =
- new TaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController);
+ final FluidResizeTaskPositioner taskPositioner =
+ new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController);
final CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 8fb56fc..4ef3350 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -101,7 +101,7 @@
private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
new SparseArray<>();
- private final DragListenerImpl mDragStartListener = new DragListenerImpl();
+ private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
@@ -777,21 +777,32 @@
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
- final TaskPositioner taskPositioner =
- new TaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController,
- mDragStartListener);
+ final DragPositioningCallback dragPositioningCallback;
+ if (!DesktopModeStatus.isVeiledResizeEnabled()) {
+ dragPositioningCallback =
+ new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration,
+ mDisplayController, mDragStartListener);
+ } else {
+ windowDecoration.createResizeVeil();
+ dragPositioningCallback =
+ new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
+ mDisplayController, mDragStartListener);
+ }
final DesktopModeTouchEventListener touchEventListener =
- new DesktopModeTouchEventListener(taskInfo, taskPositioner);
+ new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
+
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setCornersListener(mCornersListener);
- windowDecoration.setDragPositioningCallback(taskPositioner);
+ windowDecoration.setDragPositioningCallback(dragPositioningCallback);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */);
incrementEventReceiverTasks(taskInfo.displayId);
}
- private class DragListenerImpl implements TaskPositioner.DragStartListener {
+
+ private class DragStartListenerImpl
+ implements DragPositioningCallbackUtility.DragStartListener {
@Override
public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 836efe0..b1c3791 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -26,6 +26,7 @@
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -73,31 +74,15 @@
new WindowDecoration.RelayoutResult<>();
private final Point mPositionInParent = new Point();
- private final PointF mHandleMenuAppInfoPillPosition = new PointF();
- private final PointF mHandleMenuWindowingPillPosition = new PointF();
- private final PointF mHandleMenuMoreActionsPillPosition = new PointF();
-
- // Collection of additional windows that comprise the handle menu.
- private AdditionalWindow mHandleMenuAppInfoPill;
- private AdditionalWindow mHandleMenuWindowingPill;
- private AdditionalWindow mHandleMenuMoreActionsPill;
private HandleMenu mHandleMenu;
+ private ResizeVeil mResizeVeil;
+
private Drawable mAppIcon;
private CharSequence mAppName;
private TaskCornersListener mCornersListener;
- private int mMenuWidth;
- private int mMarginMenuTop;
- private int mMarginMenuStart;
- private int mMarginMenuSpacing;
- private int mAppInfoPillHeight;
- private int mWindowingPillHeight;
- private int mMoreActionsPillHeight;
- private int mShadowRadius;
- private int mMenuCornerRadius;
-
DesktopModeWindowDecoration(
Context context,
DisplayController displayController,
@@ -286,6 +271,42 @@
}
/**
+ * Create the resize veil for this task. Note the veil's visibility is View.GONE by default
+ * until a resize event calls showResizeVeil below.
+ */
+ void createResizeVeil() {
+ mResizeVeil = new ResizeVeil(mContext, mAppIcon, mTaskInfo,
+ mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
+ }
+
+ /**
+ * Fade in the resize veil
+ */
+ void showResizeVeil() {
+ mResizeVeil.showVeil(mTaskSurface);
+ }
+
+ /**
+ * Set new bounds for the resize veil
+ */
+ void updateResizeVeil(Rect newBounds) {
+ mResizeVeil.relayout(newBounds);
+ }
+
+ /**
+ * Fade the resize veil out.
+ */
+ void hideResizeVeil() {
+ mResizeVeil.hideVeil();
+ }
+
+ private void disposeResizeVeil() {
+ if (mResizeVeil == null) return;
+ mResizeVeil.dispose();
+ mResizeVeil = null;
+ }
+
+ /**
* Create and display handle menu window
*/
void createHandleMenu() {
@@ -412,6 +433,7 @@
closeDragResizeListener();
closeHandleMenu();
mCornersListener.onTaskCornersRemoved(mTaskInfo.taskId);
+ disposeResizeVeil();
super.close();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
index 0191c60..4e98f0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallback.java
@@ -16,19 +16,29 @@
package com.android.wm.shell.windowdecor;
+import android.annotation.IntDef;
+
/**
* Callback called when receiving drag-resize or drag-move related input events.
*/
public interface DragPositioningCallback {
+ @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
+ @interface CtrlType {}
+
+ int CTRL_TYPE_UNDEFINED = 0;
+ int CTRL_TYPE_LEFT = 1;
+ int CTRL_TYPE_RIGHT = 2;
+ int CTRL_TYPE_TOP = 4;
+ int CTRL_TYPE_BOTTOM = 8;
/**
* Called when a drag-resize or drag-move starts.
*
- * @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
+ * @param ctrlType {@link CtrlType} indicating the direction of resizing, use
* {@code 0} to indicate it's a move
* @param x x coordinate in window decoration coordinate system where the drag starts
* @param y y coordinate in window decoration coordinate system where the drag starts
*/
- void onDragPositioningStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
+ void onDragPositioningStart(@CtrlType int ctrlType, float x, float y);
/**
* Called when the pointer moves during a drag-resize or drag-move.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
new file mode 100644
index 0000000..ae93a43
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+
+/**
+ * Utility class that contains logic common to classes implementing {@link DragPositioningCallback}
+ * Specifically, this class contains logic for determining changed bounds from a drag input
+ * and applying that change to the task bounds when applicable.
+ */
+public class DragPositioningCallbackUtility {
+
+ /**
+ * Determine the delta between input's current point and the input start point.
+ * @param inputX current input x coordinate
+ * @param inputY current input y coordinate
+ * @param repositionStartPoint initial input coordinate
+ * @return delta between these two points
+ */
+ static PointF calculateDelta(float inputX, float inputY, PointF repositionStartPoint) {
+ final float deltaX = inputX - repositionStartPoint.x;
+ final float deltaY = inputY - repositionStartPoint.y;
+ return new PointF(deltaX, deltaY);
+ }
+
+ /**
+ * Based on type of drag and delta provided, calculate the new bounds to display for this task.
+ * @param ctrlType type of drag being performed
+ * @param hasMoved whether the current drag has moved on a prior input event
+ * @param repositionTaskBounds the bounds the task is being repositioned to
+ * @param taskBoundsAtDragStart the bounds of the task on the first drag input event
+ * @param stableBounds bounds that represent the resize limit of this task
+ * @param delta difference between start input and current input in x/y coordinates
+ * @param displayController task's display controller
+ * @param windowDecoration window decoration of the task being dragged
+ * @return whether this method changed repositionTaskBounds
+ */
+ static boolean changeBounds(int ctrlType, boolean hasMoved,
+ Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds,
+ PointF delta, DisplayController displayController, WindowDecoration windowDecoration) {
+ // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not
+ // true, we can compare it against |mTaskBoundsAtDragStart|.
+ final int oldLeft = hasMoved ? repositionTaskBounds.left : taskBoundsAtDragStart.left;
+ final int oldTop = hasMoved ? repositionTaskBounds.top : taskBoundsAtDragStart.top;
+ final int oldRight = hasMoved ? repositionTaskBounds.right : taskBoundsAtDragStart.right;
+ final int oldBottom =
+ hasMoved ? repositionTaskBounds.bottom : taskBoundsAtDragStart.bottom;
+
+
+ repositionTaskBounds.set(taskBoundsAtDragStart);
+
+ // Make sure the new resizing destination in any direction falls within the stable bounds.
+ // If not, set the bounds back to the old location that was valid to avoid conflicts with
+ // some regions such as the gesture area.
+ displayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
+ .getStableBounds(stableBounds);
+ if ((ctrlType & CTRL_TYPE_LEFT) != 0) {
+ final int candidateLeft = repositionTaskBounds.left + (int) delta.x;
+ repositionTaskBounds.left = (candidateLeft > stableBounds.left)
+ ? candidateLeft : oldLeft;
+ }
+ if ((ctrlType & CTRL_TYPE_RIGHT) != 0) {
+ final int candidateRight = repositionTaskBounds.right + (int) delta.x;
+ repositionTaskBounds.right = (candidateRight < stableBounds.right)
+ ? candidateRight : oldRight;
+ }
+ if ((ctrlType & CTRL_TYPE_TOP) != 0) {
+ final int candidateTop = repositionTaskBounds.top + (int) delta.y;
+ repositionTaskBounds.top = (candidateTop > stableBounds.top)
+ ? candidateTop : oldTop;
+ }
+ if ((ctrlType & CTRL_TYPE_BOTTOM) != 0) {
+ final int candidateBottom = repositionTaskBounds.bottom + (int) delta.y;
+ repositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
+ ? candidateBottom : oldBottom;
+ }
+ if (ctrlType == CTRL_TYPE_UNDEFINED) {
+ repositionTaskBounds.offset((int) delta.x, (int) delta.y);
+ }
+
+ // If width or height are negative or less than the minimum width or height, revert the
+ // respective bounds to use previous bound dimensions.
+ if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) {
+ repositionTaskBounds.right = oldRight;
+ repositionTaskBounds.left = oldLeft;
+ }
+ if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) {
+ repositionTaskBounds.top = oldTop;
+ repositionTaskBounds.bottom = oldBottom;
+ }
+ // If there are no changes to the bounds after checking new bounds against minimum width
+ // and height, do not set bounds and return false
+ if (oldLeft == repositionTaskBounds.left && oldTop == repositionTaskBounds.top
+ && oldRight == repositionTaskBounds.right
+ && oldBottom == repositionTaskBounds.bottom) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Apply a bounds change to a task.
+ * @param wct provided {@link WindowContainerTransaction} that may contain other changes
+ * @param windowDecoration decor of task we are changing bounds for
+ * @param taskBounds new bounds of this task
+ * @param taskOrganizer applies the provided WindowContainerTransaction
+ */
+ static void applyTaskBoundsChange(WindowContainerTransaction wct,
+ WindowDecoration windowDecoration, Rect taskBounds, ShellTaskOrganizer taskOrganizer) {
+ wct.setBounds(windowDecoration.mTaskInfo.token, taskBounds);
+ taskOrganizer.applyTransaction(wct);
+ }
+
+ private static float getMinWidth(DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize(displayController,
+ windowDecoration)
+ : windowDecoration.mTaskInfo.minWidth;
+ }
+
+ private static float getMinHeight(DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize(displayController,
+ windowDecoration)
+ : windowDecoration.mTaskInfo.minHeight;
+ }
+
+ private static float getDefaultMinSize(DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ float density = displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId)
+ .densityDpi() * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ return windowDecoration.mTaskInfo.defaultMinSize * density;
+ }
+
+ interface DragStartListener {
+ /**
+ * Inform the implementing class that a drag resize has started
+ * @param taskId id of this positioner's {@link WindowDecoration}
+ */
+ void onDragStart(int taskId);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 34c8c08..287d861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -21,6 +21,11 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
+import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
@@ -367,7 +372,7 @@
return calculateResizeHandlesCtrlType(x, y) != 0;
}
- @TaskPositioner.CtrlType
+ @DragPositioningCallback.CtrlType
private int calculateCtrlType(boolean isTouch, float x, float y) {
if (isTouch) {
return calculateCornersCtrlType(x, y);
@@ -375,62 +380,62 @@
return calculateResizeHandlesCtrlType(x, y);
}
- @TaskPositioner.CtrlType
+ @DragPositioningCallback.CtrlType
private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0;
if (x < 0) {
- ctrlType |= TaskPositioner.CTRL_TYPE_LEFT;
+ ctrlType |= CTRL_TYPE_LEFT;
}
if (x > mTaskWidth) {
- ctrlType |= TaskPositioner.CTRL_TYPE_RIGHT;
+ ctrlType |= CTRL_TYPE_RIGHT;
}
if (y < 0) {
- ctrlType |= TaskPositioner.CTRL_TYPE_TOP;
+ ctrlType |= CTRL_TYPE_TOP;
}
if (y > mTaskHeight) {
- ctrlType |= TaskPositioner.CTRL_TYPE_BOTTOM;
+ ctrlType |= CTRL_TYPE_BOTTOM;
}
return ctrlType;
}
- @TaskPositioner.CtrlType
+ @DragPositioningCallback.CtrlType
private int calculateCornersCtrlType(float x, float y) {
int xi = (int) x;
int yi = (int) y;
if (mLeftTopCornerBounds.contains(xi, yi)) {
- return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP;
+ return CTRL_TYPE_LEFT | CTRL_TYPE_TOP;
}
if (mLeftBottomCornerBounds.contains(xi, yi)) {
- return TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM;
+ return CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM;
}
if (mRightTopCornerBounds.contains(xi, yi)) {
- return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP;
+ return CTRL_TYPE_RIGHT | CTRL_TYPE_TOP;
}
if (mRightBottomCornerBounds.contains(xi, yi)) {
- return TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM;
+ return CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM;
}
return 0;
}
private void updateCursorType(float x, float y) {
- @TaskPositioner.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
+ @DragPositioningCallback.CtrlType int ctrlType = calculateResizeHandlesCtrlType(x, y);
int cursorType = PointerIcon.TYPE_DEFAULT;
switch (ctrlType) {
- case TaskPositioner.CTRL_TYPE_LEFT:
- case TaskPositioner.CTRL_TYPE_RIGHT:
+ case CTRL_TYPE_LEFT:
+ case CTRL_TYPE_RIGHT:
cursorType = PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
break;
- case TaskPositioner.CTRL_TYPE_TOP:
- case TaskPositioner.CTRL_TYPE_BOTTOM:
+ case CTRL_TYPE_TOP:
+ case CTRL_TYPE_BOTTOM:
cursorType = PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
break;
- case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP:
- case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM:
+ case CTRL_TYPE_LEFT | CTRL_TYPE_TOP:
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM:
cursorType = PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
break;
- case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM:
- case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP:
+ case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM:
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP:
cursorType = PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
break;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
new file mode 100644
index 0000000..b366c80
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+
+/**
+ * A task positioner that resizes/relocates task contents as it is dragged.
+ * Utilizes {@link DragPositioningCallbackUtility} to determine new task bounds.
+ */
+class FluidResizeTaskPositioner implements DragPositioningCallback {
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final WindowDecoration mWindowDecoration;
+ private DisplayController mDisplayController;
+ private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
+ private final Rect mStableBounds = new Rect();
+ private final Rect mTaskBoundsAtDragStart = new Rect();
+ private final PointF mRepositionStartPoint = new PointF();
+ private final Rect mRepositionTaskBounds = new Rect();
+ private int mCtrlType;
+ private boolean mHasMoved;
+
+ FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
+ DisplayController displayController) {
+ this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {});
+ }
+
+ FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
+ DisplayController displayController,
+ DragPositioningCallbackUtility.DragStartListener dragStartListener) {
+ mTaskOrganizer = taskOrganizer;
+ mWindowDecoration = windowDecoration;
+ mDisplayController = displayController;
+ mDragStartListener = dragStartListener;
+ }
+
+ @Override
+ public void onDragPositioningStart(int ctrlType, float x, float y) {
+ mCtrlType = ctrlType;
+ mTaskBoundsAtDragStart.set(
+ mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
+ mRepositionStartPoint.set(x, y);
+ mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
+ }
+
+ @Override
+ public void onDragPositioningMove(float x, float y) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint);
+ if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
+ mDisplayController, mWindowDecoration)) {
+ // The task is being resized, send the |dragResizing| hint to core with the first
+ // bounds-change wct.
+ if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
+ // This is the first bounds change since drag resize operation started.
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
+ }
+ DragPositioningCallbackUtility.applyTaskBoundsChange(wct, mWindowDecoration,
+ mRepositionTaskBounds, mTaskOrganizer);
+ mHasMoved = true;
+ }
+ }
+
+ @Override
+ public void onDragPositioningEnd(float x, float y) {
+ // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
+ // we don't need a WCT to finish it.
+ if (mHasMoved) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+ PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
+ mRepositionStartPoint);
+ if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
+ mDisplayController, mWindowDecoration)) {
+ wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
+ }
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
+ mTaskBoundsAtDragStart.setEmpty();
+ mRepositionStartPoint.set(0, 0);
+ mCtrlType = CTRL_TYPE_UNDEFINED;
+ mHasMoved = false;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
new file mode 100644
index 0000000..fa2d7a8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.ImageView;
+import android.window.TaskConstants;
+
+import com.android.wm.shell.R;
+
+import java.util.function.Supplier;
+
+/**
+ * Creates and updates a veil that covers task contents on resize.
+ */
+public class ResizeVeil {
+ private static final int RESIZE_ALPHA_DURATION = 200;
+ private final Context mContext;
+ private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+ private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
+ private final Drawable mAppIcon;
+ private SurfaceControl mParentSurface;
+ private SurfaceControl mVeilSurface;
+ private final RunningTaskInfo mTaskInfo;
+ private SurfaceControlViewHost mViewHost;
+ private final Display mDisplay;
+
+ public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display,
+ Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
+ mContext = context;
+ mAppIcon = appIcon;
+ mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+ mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
+ mTaskInfo = taskInfo;
+ mDisplay = display;
+ setupResizeVeil();
+ }
+
+ /**
+ * Create the veil in its default invisible state.
+ */
+ private void setupResizeVeil() {
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+ mVeilSurface = builder
+ .setName("Resize veil of Task= " + mTaskInfo.taskId)
+ .setContainerLayer()
+ .build();
+ View v = LayoutInflater.from(mContext)
+ .inflate(R.layout.desktop_mode_resize_veil, null);
+
+ t.setPosition(mVeilSurface, 0, 0)
+ .setLayer(mVeilSurface, TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL)
+ .apply();
+ Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(taskBounds.width(),
+ taskBounds.height(),
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT);
+ lp.setTitle("Resize veil of Task=" + mTaskInfo.taskId);
+ lp.setTrustedOverlay();
+ WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
+ mVeilSurface, null /* hostInputToken */);
+ mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil");
+ mViewHost.setView(v, lp);
+
+ final ImageView appIcon = mViewHost.getView().findViewById(R.id.veil_application_icon);
+ appIcon.setImageDrawable(mAppIcon);
+ }
+
+ /**
+ * Animate veil's alpha to 1, fading it in.
+ */
+ public void showVeil(SurfaceControl parentSurface) {
+ // Parent surface can change, ensure it is up to date.
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ if (!parentSurface.equals(mParentSurface)) {
+ t.reparent(mVeilSurface, parentSurface);
+ mParentSurface = parentSurface;
+ }
+ t.show(mVeilSurface)
+ .apply();
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(RESIZE_ALPHA_DURATION);
+ animator.addUpdateListener(animation -> {
+ t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
+ t.apply();
+ });
+ animator.start();
+ }
+
+ /**
+ * Update veil bounds to match bounds changes.
+ * @param newBounds bounds to update veil to.
+ */
+ public void relayout(Rect newBounds) {
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ mViewHost.relayout(newBounds.width(), newBounds.height());
+ t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height());
+ t.setPosition(mParentSurface, newBounds.left, newBounds.top);
+ t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height());
+ mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
+ }
+
+ /**
+ * Animate veil's alpha to 0, fading it out.
+ */
+ public void hideVeil() {
+ final View resizeVeilView = mViewHost.getView();
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(1, 0);
+ animator.setDuration(RESIZE_ALPHA_DURATION);
+ animator.addUpdateListener(animation -> {
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ t.setAlpha(mVeilSurface, 1 - animator.getAnimatedFraction());
+ t.apply();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ t.hide(mVeilSurface);
+ t.apply();
+ }
+ });
+ animator.start();
+ }
+
+ /**
+ * Dispose of veil when it is no longer needed, likely on close of its container decor.
+ */
+ void dispose() {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mVeilSurface != null) {
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ t.remove(mVeilSurface);
+ mVeilSurface = null;
+ t.apply();
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
deleted file mode 100644
index 0bce3ac..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.windowdecor;
-
-import android.annotation.IntDef;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.util.DisplayMetrics;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-
-class TaskPositioner implements DragPositioningCallback {
-
- @IntDef({CTRL_TYPE_UNDEFINED, CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
- @interface CtrlType {}
-
- static final int CTRL_TYPE_UNDEFINED = 0;
- static final int CTRL_TYPE_LEFT = 1;
- static final int CTRL_TYPE_RIGHT = 2;
- static final int CTRL_TYPE_TOP = 4;
- static final int CTRL_TYPE_BOTTOM = 8;
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final DisplayController mDisplayController;
- private final WindowDecoration mWindowDecoration;
-
- private final Rect mTempBounds = new Rect();
- private final Rect mTaskBoundsAtDragStart = new Rect();
- private final PointF mRepositionStartPoint = new PointF();
- private final Rect mRepositionTaskBounds = new Rect();
- private boolean mHasMoved = false;
-
- private int mCtrlType;
- private DragStartListener mDragStartListener;
-
- TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
- DisplayController displayController) {
- this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {});
- }
-
- TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
- DisplayController displayController, DragStartListener dragStartListener) {
- mTaskOrganizer = taskOrganizer;
- mWindowDecoration = windowDecoration;
- mDisplayController = displayController;
- mDragStartListener = dragStartListener;
- }
-
- @Override
- public void onDragPositioningStart(int ctrlType, float x, float y) {
- mHasMoved = false;
-
- mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
- mCtrlType = ctrlType;
-
- mTaskBoundsAtDragStart.set(
- mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
- mRepositionStartPoint.set(x, y);
- }
-
- @Override
- public void onDragPositioningMove(float x, float y) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (changeBounds(wct, x, y)) {
- // The task is being resized, send the |dragResizing| hint to core with the first
- // bounds-change wct.
- if (!mHasMoved && mCtrlType != CTRL_TYPE_UNDEFINED) {
- // This is the first bounds change since drag resize operation started.
- wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
- }
- mTaskOrganizer.applyTransaction(wct);
- mHasMoved = true;
- }
- }
-
- @Override
- public void onDragPositioningEnd(float x, float y) {
- // |mHasMoved| being false means there is no real change to the task bounds in WM core, so
- // we don't need a WCT to finish it.
- if (mHasMoved) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
- changeBounds(wct, x, y);
- mTaskOrganizer.applyTransaction(wct);
- }
-
- mCtrlType = CTRL_TYPE_UNDEFINED;
- mTaskBoundsAtDragStart.setEmpty();
- mRepositionStartPoint.set(0, 0);
- mHasMoved = false;
- }
-
- private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
- // |mRepositionTaskBounds| is the bounds last reported if |mHasMoved| is true. If it's not
- // true, we can compare it against |mTaskBoundsAtDragStart|.
- final int oldLeft = mHasMoved ? mRepositionTaskBounds.left : mTaskBoundsAtDragStart.left;
- final int oldTop = mHasMoved ? mRepositionTaskBounds.top : mTaskBoundsAtDragStart.top;
- final int oldRight = mHasMoved ? mRepositionTaskBounds.right : mTaskBoundsAtDragStart.right;
- final int oldBottom =
- mHasMoved ? mRepositionTaskBounds.bottom : mTaskBoundsAtDragStart.bottom;
-
- final float deltaX = x - mRepositionStartPoint.x;
- final float deltaY = y - mRepositionStartPoint.y;
- mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
-
- final Rect stableBounds = mTempBounds;
- // Make sure the new resizing destination in any direction falls within the stable bounds.
- // If not, set the bounds back to the old location that was valid to avoid conflicts with
- // some regions such as the gesture area.
- mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
- .getStableBounds(stableBounds);
- if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
- final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX;
- mRepositionTaskBounds.left = (candidateLeft > stableBounds.left)
- ? candidateLeft : oldLeft;
- }
- if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
- final int candidateRight = mRepositionTaskBounds.right + (int) deltaX;
- mRepositionTaskBounds.right = (candidateRight < stableBounds.right)
- ? candidateRight : oldRight;
- }
- if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
- final int candidateTop = mRepositionTaskBounds.top + (int) deltaY;
- mRepositionTaskBounds.top = (candidateTop > stableBounds.top)
- ? candidateTop : oldTop;
- }
- if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
- final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY;
- mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
- ? candidateBottom : oldBottom;
- }
- if (mCtrlType == CTRL_TYPE_UNDEFINED) {
- mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
- }
-
- // If width or height are negative or less than the minimum width or height, revert the
- // respective bounds to use previous bound dimensions.
- if (mRepositionTaskBounds.width() < getMinWidth()) {
- mRepositionTaskBounds.right = oldRight;
- mRepositionTaskBounds.left = oldLeft;
- }
- if (mRepositionTaskBounds.height() < getMinHeight()) {
- mRepositionTaskBounds.top = oldTop;
- mRepositionTaskBounds.bottom = oldBottom;
- }
- // If there are no changes to the bounds after checking new bounds against minimum width
- // and height, do not set bounds and return false
- if (oldLeft == mRepositionTaskBounds.left && oldTop == mRepositionTaskBounds.top
- && oldRight == mRepositionTaskBounds.right
- && oldBottom == mRepositionTaskBounds.bottom) {
- return false;
- }
-
- wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
- return true;
- }
-
- private float getMinWidth() {
- return mWindowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize()
- : mWindowDecoration.mTaskInfo.minWidth;
- }
-
- private float getMinHeight() {
- return mWindowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize()
- : mWindowDecoration.mTaskInfo.minHeight;
- }
-
- private float getDefaultMinSize() {
- float density = mDisplayController.getDisplayLayout(mWindowDecoration.mTaskInfo.displayId)
- .densityDpi() * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- return mWindowDecoration.mTaskInfo.defaultMinSize * density;
- }
-
- interface DragStartListener {
- /**
- * Inform the implementing class that a drag resize has started
- * @param taskId id of this positioner's {@link WindowDecoration}
- */
- void onDragStart(int taskId);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
new file mode 100644
index 0000000..1d416c6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+
+/**
+ * A task positioner that also takes into account resizing a
+ * {@link com.android.wm.shell.windowdecor.ResizeVeil}.
+ * If the drag is resizing the task, we resize the veil instead.
+ * If the drag is repositioning, we update in the typical manner.
+ */
+public class VeiledResizeTaskPositioner implements DragPositioningCallback {
+
+ private DesktopModeWindowDecoration mDesktopWindowDecoration;
+ private ShellTaskOrganizer mTaskOrganizer;
+ private DisplayController mDisplayController;
+ private DragPositioningCallbackUtility.DragStartListener mDragStartListener;
+ private final Rect mStableBounds = new Rect();
+ private final Rect mTaskBoundsAtDragStart = new Rect();
+ private final PointF mRepositionStartPoint = new PointF();
+ private final Rect mRepositionTaskBounds = new Rect();
+ private int mCtrlType;
+ private boolean mHasMoved;
+
+
+ public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
+ DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
+ DragPositioningCallbackUtility.DragStartListener dragStartListener) {
+ mTaskOrganizer = taskOrganizer;
+ mDesktopWindowDecoration = windowDecoration;
+ mDisplayController = displayController;
+ mDragStartListener = dragStartListener;
+ }
+
+ @Override
+ public void onDragPositioningStart(int ctrlType, float x, float y) {
+ mCtrlType = ctrlType;
+ mTaskBoundsAtDragStart.set(
+ mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
+ mRepositionStartPoint.set(x, y);
+ if (mCtrlType != CTRL_TYPE_UNDEFINED) {
+ mDesktopWindowDecoration.showResizeVeil();
+ }
+ mHasMoved = false;
+ mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
+ }
+
+ @Override
+ public void onDragPositioningMove(float x, float y) {
+ PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y, mRepositionStartPoint);
+ if (DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
+ mDisplayController, mDesktopWindowDecoration)) {
+ if (mCtrlType != CTRL_TYPE_UNDEFINED) {
+ mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
+ } else {
+ DragPositioningCallbackUtility.applyTaskBoundsChange(
+ new WindowContainerTransaction(), mDesktopWindowDecoration,
+ mRepositionTaskBounds, mTaskOrganizer);
+ }
+ mHasMoved = true;
+ }
+ }
+
+ @Override
+ public void onDragPositioningEnd(float x, float y) {
+ PointF delta = DragPositioningCallbackUtility.calculateDelta(x, y,
+ mRepositionStartPoint);
+ if (mHasMoved) {
+ DragPositioningCallbackUtility.changeBounds(mCtrlType, mHasMoved,
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
+ mDisplayController, mDesktopWindowDecoration);
+ DragPositioningCallbackUtility.applyTaskBoundsChange(
+ new WindowContainerTransaction(), mDesktopWindowDecoration,
+ mRepositionTaskBounds, mTaskOrganizer);
+ }
+ // TODO: (b/279062291) Synchronize the start of hide to the end of the draw triggered above.
+ if (mCtrlType != CTRL_TYPE_UNDEFINED) {
+ mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
+ mDesktopWindowDecoration.hideResizeVeil();
+ }
+ mCtrlType = CTRL_TYPE_UNDEFINED;
+ mTaskBoundsAtDragStart.setEmpty();
+ mRepositionStartPoint.set(0, 0);
+ mHasMoved = false;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
new file mode 100644
index 0000000..e06e074
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.app.Instrumentation
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.tapl.LauncherInstrumentation
+
+abstract class BaseBenchmarkTest
+@JvmOverloads
+constructor(
+ protected open val flicker: FlickerTest,
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
+) {
+ /** Specification of the test transition to execute */
+ abstract val transition: FlickerBuilder.() -> Unit
+
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache flicker
+ * executions
+ */
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup { flicker.scenario.setIsTablet(tapl.isTablet) }
+ transition()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index c5ee7b7..c98c5a0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -17,24 +17,10 @@
package com.android.wm.shell.flicker
import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.device.flicker.junit.FlickerBuilderProvider
-import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
-import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
-import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
-import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
-import org.junit.Assume
-import org.junit.Test
/**
* Base test class containing common assertions for [ComponentNameMatcher.NAV_BAR],
@@ -44,124 +30,7 @@
abstract class BaseTest
@JvmOverloads
constructor(
- protected val flicker: FlickerTest,
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
- protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
-) {
- /** Specification of the test transition to execute */
- abstract val transition: FlickerBuilder.() -> Unit
-
- /**
- * Entry point for the test runner. It will use this method to initialize and cache flicker
- * executions
- */
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup { flicker.scenario.setIsTablet(tapl.isTablet) }
- transition()
- }
- }
-
- /** Checks that all parts of the screen are covered during the transition */
- @Presubmit @Test open fun entireScreenCovered() = flicker.entireScreenCovered()
-
- /**
- * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun navBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
- flicker.navBarLayerIsVisibleAtStartAndEnd()
- }
-
- /**
- * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
- * transition
- */
- @Presubmit
- @Test
- open fun navBarLayerPositionAtStartAndEnd() {
- Assume.assumeFalse(flicker.scenario.isTablet)
- flicker.navBarLayerPositionAtStartAndEnd()
- }
-
- /**
- * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
- *
- * Note: Phones only
- */
- @Presubmit
- @Test
- open fun navBarWindowIsAlwaysVisible() {
- Assume.assumeFalse(flicker.scenario.isTablet)
- flicker.navBarWindowIsAlwaysVisible()
- }
-
- /**
- * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun taskBarLayerIsVisibleAtStartAndEnd() {
- Assume.assumeTrue(flicker.scenario.isTablet)
- flicker.taskBarLayerIsVisibleAtStartAndEnd()
- }
-
- /**
- * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
- *
- * Note: Large screen only
- */
- @Presubmit
- @Test
- open fun taskBarWindowIsAlwaysVisible() {
- Assume.assumeTrue(flicker.scenario.isTablet)
- flicker.taskBarWindowIsAlwaysVisible()
- }
-
- /**
- * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole
- * transition
- */
- @Presubmit
- @Test
- open fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd()
-
- /**
- * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
- * transition
- */
- @Presubmit
- @Test
- open fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd()
-
- /**
- * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
- * transition
- */
- @Presubmit
- @Test
- open fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible()
-
- /**
- * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
- * entries.
- */
- @Presubmit
- @Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() }
- }
-
- /**
- * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
- * entries.
- */
- @Presubmit
- @Test
- open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() }
- }
-}
+ override val flicker: FlickerTest,
+ instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ tapl: LauncherInstrumentation = LauncherInstrumentation()
+) : BaseBenchmarkTest(flicker, instrumentation, tapl), ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 45024f3..798cc95 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -20,9 +20,9 @@
import android.tools.common.Rotation
import android.tools.common.datatypes.Region
-import android.tools.common.datatypes.component.IComponentMatcher
import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.helpers.WindowUtils
@@ -218,11 +218,11 @@
assertLayers {
if (landscapePosLeft) {
splitAppLayerBoundsSnapToDivider(
- component,
- landscapePosLeft,
- portraitPosTop,
- scenario.endRotation
- )
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
} else {
splitAppLayerBoundsSnapToDivider(
component,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 983640a..3bc1e2a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -18,7 +18,7 @@
package com.android.wm.shell.flicker
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
const val LAUNCHER_UI_PACKAGE_NAME = "com.google.android.apps.nexuslauncher"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
new file mode 100644
index 0000000..02d9a056
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
+import org.junit.Assume
+import org.junit.Test
+
+interface ICommonAssertions {
+ val flicker: FlickerTest
+
+ /** Checks that all parts of the screen are covered during the transition */
+ @Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered()
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
+ */
+ @Presubmit
+ @Test
+ fun navBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun navBarLayerPositionAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerPositionAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible during the whole transition
+ */
+ @Presubmit
+ @Test
+ fun taskBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
+ @Test
+ fun taskBarWindowIsAlwaysVisible() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd()
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
+ * transition
+ */
+ @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible()
+
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() }
+ }
+
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index d01a0ee..11c5951 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -20,11 +20,11 @@
import android.system.helpers.CommandsHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
-import com.android.wm.shell.flicker.BaseTest
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.LetterboxAppHelper
import android.tools.device.flicker.legacy.FlickerTestFactory
import android.tools.device.flicker.legacy.IFlickerTestData
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.flicker.BaseTest
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import org.junit.Assume
@@ -104,8 +104,8 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
- * navigation modes.
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
+ * modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index c57100e..f212a4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
-import androidx.test.filters.RequiresDevice
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -35,13 +35,13 @@
* ```
* Rotate non resizable portrait only app to opposite orientation to trigger size compat mode
* ```
+ *
* Notes:
* ```
* Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [BaseTest]
* ```
*/
-
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -86,4 +86,4 @@
.isInvisible(ComponentNameMatcher.ROTATION)
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index f111a8d..8e75439 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
-import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -36,13 +36,13 @@
* Rotate app to opposite orientation to trigger size compat mode
* Press restart button and wait for letterboxed app to resize
* ```
+ *
* Notes:
* ```
* Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [BaseTest]
* ```
*/
-
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -56,9 +56,7 @@
teardown { letterboxApp.exit(wmHelper) }
}
- @Postsubmit
- @Test
- fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd()
+ @Postsubmit @Test fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd()
@Postsubmit
@Test
@@ -83,4 +81,4 @@
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 18a3aa7..889e177 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
@@ -149,9 +149,7 @@
@Ignore("Not applicable to this CUJ. Taskbar is not shown on lock screen")
override fun taskBarWindowIsAlwaysVisible() {}
- /**
- * Checks that the [ComponentNameMatcher.TASK_BAR] is visible at the end of the transition
- */
+ /** Checks that the [ComponentNameMatcher.TASK_BAR] is visible at the end of the transition */
@Postsubmit
@Test
fun taskBarLayerIsVisibleAtEnd() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 59918fb..e6544c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -17,7 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
index 36c6f7c..2417c45 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index db18edb..4b461370 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -21,7 +21,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index 51f0136..bfd5778 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 2001f48..5ac9829 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 6deba1b..bbb1c6c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index a626713..6b061bbb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b30f308..eb1245b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,7 +20,7 @@
import android.content.Intent
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 0c9c161..b7e73ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -19,13 +19,14 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.common.datatypes.component.EdgeExtensionComponentMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.EdgeExtensionComponentMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
@@ -34,6 +35,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import com.android.wm.shell.flicker.splitscreen.benchmark.CopyContentInSplitBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,29 +51,19 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CopyContentInSplit(flicker: FlickerTest) : SplitScreenBase(flicker) {
- private val textEditApp = SplitScreenUtils.getIme(instrumentation)
- private val MagnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#")
- private val PopupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
-
+class CopyContentInSplit(override val flicker: FlickerTest) :
+ CopyContentInSplitBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
- transitions {
- SplitScreenUtils.copyContentInSplit(
- instrumentation,
- device,
- primaryApp,
- textEditApp
- )
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() {
+ override fun cujCompleted() {
flicker.appWindowIsVisibleAtStart(primaryApp)
flicker.appWindowIsVisibleAtStart(textEditApp)
flicker.splitScreenDividerIsVisibleAtStart()
@@ -128,8 +120,8 @@
ComponentNameMatcher.SNAPSHOT,
ComponentNameMatcher.IME_SNAPSHOT,
EdgeExtensionComponentMatcher(),
- MagnifierLayer,
- PopupWindowLayer
+ magnifierLayer,
+ popupWindowLayer
)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 1b55f39..3fd6d17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -17,22 +17,21 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
-import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByDividerBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,37 +47,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicker) {
+class DismissSplitScreenByDivider(override val flicker: FlickerTest) :
+ DismissSplitScreenByDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
- transitions {
- if (tapl.isTablet) {
- SplitScreenUtils.dragDividerToDismissSplit(
- device,
- wmHelper,
- dragToRight = false,
- dragToBottom = true
- )
- } else {
- SplitScreenUtils.dragDividerToDismissSplit(
- device,
- wmHelper,
- dragToRight = true,
- dragToBottom = true
- )
- }
- wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false)
-
@Presubmit
@Test
fun splitScreenDividerBecomesInvisible() = flicker.splitScreenDividerBecomesInvisible()
@@ -176,12 +154,4 @@
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 2e81b30..e05b221 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -17,18 +17,18 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
-import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,21 +44,14 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByGoHome(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
+class DismissSplitScreenByGoHome(override val flicker: FlickerTest) :
+ DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
- transitions {
- tapl.goHome()
- wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 5180791..0b0a3da 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -24,6 +24,7 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
@@ -32,8 +33,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsChanges
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.splitscreen.benchmark.DragDividerToResizeBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,24 +49,19 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DragDividerToResize(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
+class DragDividerToResize(override val flicker: FlickerTest) :
+ DragDividerToResizeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
- transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @Before
- fun before() {
- Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart)
- }
-
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() {
+ override fun cujCompleted() {
flicker.appWindowIsVisibleAtStart(primaryApp)
flicker.appWindowIsVisibleAtStart(secondaryApp)
flicker.splitScreenDividerIsVisibleAtStart()
@@ -74,9 +69,6 @@
flicker.appWindowIsVisibleAtEnd(primaryApp)
flicker.appWindowIsVisibleAtEnd(secondaryApp)
flicker.splitScreenDividerIsVisibleAtEnd()
-
- // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is
- // robust enough to get the correct end state.
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 69da1e2..e558686 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -26,6 +25,7 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -34,9 +34,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromAllAppsBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,40 +51,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromAllApps(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
- @Before
- fun before() {
- Assume.assumeTrue(tapl.isTablet)
- }
-
+class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) :
+ EnterSplitScreenByDragFromAllAppsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
- transitions {
- tapl.launchedAppState.taskbar
- .openAllApps()
- .getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() =
- flicker.splitScreenEntered(
- primaryApp,
- secondaryApp,
- fromOtherApp = false,
- appExistAtStart = false
- )
-
@FlakyTest(bugId = 245472831)
@Test
fun splitScreenDividerBecomesVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 1773846..ab8ecc5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -26,6 +25,7 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesVisible
@@ -33,9 +33,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromNotificationBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,39 +50,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromNotification(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
- private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
-
- @Before
- fun before() {
- Assume.assumeTrue(tapl.isTablet)
- }
-
+class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) :
+ EnterSplitScreenByDragFromNotificationBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- // Send a notification
- sendNotificationApp.launchViaIntent(wmHelper)
- sendNotificationApp.postNotification(wmHelper)
- tapl.goHome()
- primaryApp.launchViaIntent(wmHelper)
- }
- transitions {
- SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
- }
- teardown { sendNotificationApp.exit(wmHelper) }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() =
- flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false)
-
@FlakyTest(bugId = 245472831)
@Test
fun splitScreenDividerBecomesVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index c1977e9..516ca97 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -25,15 +24,14 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromShortcutBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,42 +47,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromShortcut(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
- @Before
- fun before() {
- Assume.assumeTrue(tapl.isTablet)
- }
+class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) :
+ EnterSplitScreenByDragFromShortcutBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- tapl.goHome()
- SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
- primaryApp.launchViaIntent(wmHelper)
- }
- transitions {
- tapl.launchedAppState.taskbar
- .getAppIcon(secondaryApp.appName)
- .openDeepShortcutMenu()
- .getMenuItem("Split Screen Secondary Activity")
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() =
- flicker.splitScreenEntered(
- primaryApp,
- secondaryApp,
- fromOtherApp = false,
- appExistAtStart = false
- )
-
@Presubmit
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 3bea66e..4af7e24 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -26,6 +25,7 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -34,9 +34,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenByDragFromTaskbarBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,41 +51,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromTaskbar(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
- @Before
- fun before() {
- Assume.assumeTrue(tapl.isTablet)
- }
-
+class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) :
+ EnterSplitScreenByDragFromTaskbarBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- tapl.goHome()
- SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
- primaryApp.launchViaIntent(wmHelper)
- }
- transitions {
- tapl.launchedAppState.taskbar
- .getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() =
- flicker.splitScreenEntered(
- primaryApp,
- secondaryApp,
- fromOtherApp = false,
- appExistAtStart = false
- )
-
@FlakyTest(bugId = 245472831)
@Test
fun splitScreenDividerBecomesVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index c453877..faad9e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -17,20 +17,20 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.benchmark.EnterSplitScreenFromOverviewBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,31 +46,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenFromOverview(flicker: FlickerTest) : SplitScreenBase(flicker) {
+class EnterSplitScreenFromOverview(override val flicker: FlickerTest) :
+ EnterSplitScreenFromOverviewBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- primaryApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- tapl.goHome()
- wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
- }
- transitions {
- SplitScreenUtils.splitFromOverview(tapl, device)
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
-
@Presubmit
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 7abdc06..195b73a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -20,25 +20,33 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.BaseTest
+import com.android.wm.shell.flicker.BaseBenchmarkTest
-abstract class SplitScreenBase(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class SplitScreenBase(flicker: FlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- tapl.setEnableRotation(true)
- setRotation(flicker.scenario.startRotation)
- tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- tapl.workspace.switchToOverview().dismissAllTasks()
- }
- teardown {
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- }
+ protected open val defaultSetup: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setEnableRotation(true)
+ setRotation(flicker.scenario.startRotation)
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
}
+ }
+
+ protected open val defaultTeardown: FlickerBuilder.() -> Unit = {
+ teardown {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+ }
+
+ protected open val withoutTracing: FlickerBuilder.() -> Unit = {
+ withoutLayerTracing()
+ withoutWindowManagerTracing()
+ withoutTransitionTracing()
+ withoutTransactionsTracing()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
index 625987a..1063dfd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -19,9 +19,9 @@
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.common.datatypes.component.IComponentMatcher
-import android.tools.common.datatypes.component.IComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.IComponentMatcher
+import android.tools.common.traces.component.IComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index fbb7c71..8cf871f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -20,14 +20,12 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
-import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.helpers.WindowUtils
-import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
@@ -36,6 +34,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchAppByDoubleTapDividerBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,98 +50,19 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
+class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) :
+ SwitchAppByDoubleTapDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
- transitions {
- SplitScreenUtils.doubleTapDividerToSwitch(device)
- wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
-
- waitForLayersToSwitch(wmHelper)
- waitForWindowsToSwitch(wmHelper)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
- wmHelper
- .StateSyncBuilder()
- .add("appWindowsSwitched") {
- val primaryAppWindow =
- it.wmState.visibleWindows.firstOrNull { window ->
- primaryApp.windowMatchesAnyOf(window)
- }
- ?: return@add false
- val secondaryAppWindow =
- it.wmState.visibleWindows.firstOrNull { window ->
- secondaryApp.windowMatchesAnyOf(window)
- }
- ?: return@add false
-
- if (isLandscape(flicker.scenario.endRotation)) {
- return@add if (flicker.scenario.isTablet) {
- secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
- } else {
- primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
- }
- } else {
- return@add if (flicker.scenario.isTablet) {
- primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
- } else {
- primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
- }
- }
- }
- .waitForAndVerify()
- }
-
- private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
- wmHelper
- .StateSyncBuilder()
- .add("appLayersSwitched") {
- val primaryAppLayer =
- it.layerState.visibleLayers.firstOrNull { window ->
- primaryApp.layerMatchesAnyOf(window)
- }
- ?: return@add false
- val secondaryAppLayer =
- it.layerState.visibleLayers.firstOrNull { window ->
- secondaryApp.layerMatchesAnyOf(window)
- }
- ?: return@add false
-
- val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
- val secondaryVisibleRegion =
- secondaryAppLayer.visibleRegion?.bounds ?: return@add false
-
- if (isLandscape(flicker.scenario.endRotation)) {
- return@add if (flicker.scenario.isTablet) {
- secondaryVisibleRegion.right <= primaryVisibleRegion.left
- } else {
- primaryVisibleRegion.right <= secondaryVisibleRegion.left
- }
- } else {
- return@add if (flicker.scenario.isTablet) {
- primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
- } else {
- primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
- }
- }
- }
- .waitForAndVerify()
- }
-
- private fun isLandscape(rotation: Rotation): Boolean {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return displayBounds.width > displayBounds.height
- }
-
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() {
+ override fun cujCompleted() {
flicker.appWindowIsVisibleAtStart(primaryApp)
flicker.appWindowIsVisibleAtStart(secondaryApp)
flicker.splitScreenDividerIsVisibleAtStart()
@@ -150,9 +70,6 @@
flicker.appWindowIsVisibleAtEnd(primaryApp)
flicker.appWindowIsVisibleAtEnd(secondaryApp)
flicker.splitScreenDividerIsVisibleAtEnd()
-
- // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is
- // robust enough to get the correct end state.
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index d675bfb..078d95d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -25,11 +24,12 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromAnotherAppBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,29 +45,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromAnotherApp(flicker: FlickerTest) : SplitScreenBase(flicker) {
- val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
-
+class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) :
+ SwitchBackToSplitFromAnotherAppBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-
- thirdApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
- }
- transitions {
- tapl.launchedAppState.quickSwitchToPreviousApp()
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
-
@Presubmit
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 9f4cb8c..7c84243 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -25,11 +24,12 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromHomeBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,28 +45,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromHome(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
+class SwitchBackToSplitFromHome(override val flicker: FlickerTest) :
+ SwitchBackToSplitFromHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-
- tapl.goHome()
- wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- }
- transitions {
- tapl.workspace.quickSwitchToPreviousApp()
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
-
@Presubmit
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index a33d8ca..7c46d3e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -25,11 +24,12 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
-import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBackToSplitFromRecentBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,28 +45,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromRecent(flicker: FlickerTest) : SplitScreenBase(flicker) {
-
+class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) :
+ SwitchBackToSplitFromRecentBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
-
- tapl.goHome()
- wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
- }
- transitions {
- tapl.workspace.switchToOverview().currentTask.open()
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
- @IwTest(focusArea = "sysui")
- @Presubmit
- @Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
-
@Presubmit
@Test
fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 4c96b3a..3b2da8d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -24,6 +24,7 @@
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -36,6 +37,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsSnapToDivider
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
+import com.android.wm.shell.flicker.splitscreen.benchmark.SwitchBetweenSplitPairsBenchmark
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,32 +53,19 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBetweenSplitPairs(flicker: FlickerTest) : SplitScreenBase(flicker) {
- private val thirdApp = SplitScreenUtils.getIme(instrumentation)
- private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
-
+class SwitchBetweenSplitPairs(override val flicker: FlickerTest) :
+ SwitchBetweenSplitPairsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
- setup {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
- SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
- }
- transitions {
- tapl.launchedAppState.quickSwitchToPreviousApp()
- SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
- teardown {
- thirdApp.exit(wmHelper)
- fourthApp.exit(wmHelper)
- }
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() {
+ override fun cujCompleted() {
flicker.appWindowIsVisibleAtStart(thirdApp)
flicker.appWindowIsVisibleAtStart(fourthApp)
flicker.splitScreenDividerIsVisibleAtStart()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
new file mode 100644
index 0000000..a189a3f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val textEditApp = SplitScreenUtils.getIme(instrumentation)
+ protected val magnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#")
+ protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp) }
+ transitions {
+ SplitScreenUtils.copyContentInSplit(
+ instrumentation,
+ device,
+ primaryApp,
+ textEditApp
+ )
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ open fun cujCompleted() {
+ // The validation of copied text is already done in SplitScreenUtils.copyContentInSplit()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
new file mode 100644
index 0000000..55ab7b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenDismissed
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ if (tapl.isTablet) {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = false,
+ dragToBottom = true
+ )
+ } else {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = true,
+ dragToBottom = true
+ )
+ }
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = false)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
new file mode 100644
index 0000000..c4cfd1a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenDismissed
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenDismissed(primaryApp, secondaryApp, toHome = true)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
new file mode 100644
index 0000000..146287c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.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.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ open fun cujCompleted() {
+ // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
new file mode 100644
index 0000000..cc71502
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
new file mode 100644
index 0000000..de78f09
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ sendNotificationApp.postNotification(wmHelper)
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+ }
+ teardown { sendNotificationApp.exit(wmHelper) }
+ }
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() =
+ flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false)
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
new file mode 100644
index 0000000..a29eb40
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ protected val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .openDeepShortcutMenu()
+ .getMenuItem("Split Screen Secondary Activity")
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
new file mode 100644
index 0000000..b2395ca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
new file mode 100644
index 0000000..e1d85d0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ transitions {
+ SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
new file mode 100644
index 0000000..ba8c460
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ transitions {
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appWindowsSwitched") {
+ val primaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ if (isLandscape(flicker.scenario.endRotation)) {
+ return@add if (flicker.scenario.isTablet) {
+ secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+ } else {
+ primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ }
+ } else {
+ return@add if (flicker.scenario.isTablet) {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ } else {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appLayersSwitched") {
+ val primaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+ val secondaryVisibleRegion =
+ secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+ if (isLandscape(flicker.scenario.endRotation)) {
+ return@add if (flicker.scenario.isTablet) {
+ secondaryVisibleRegion.right <= primaryVisibleRegion.left
+ } else {
+ primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ }
+ } else {
+ return@add if (flicker.scenario.isTablet) {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ } else {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun isLandscape(rotation: Rotation): Boolean {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return displayBounds.width > displayBounds.height
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ open fun cujCompleted() {
+ // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
new file mode 100644
index 0000000..bbb2edc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
new file mode 100644
index 0000000..fa38293
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
new file mode 100644
index 0000000..1064bd9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitScreenEntered
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ tapl.workspace.switchToOverview().currentTask.open()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ withoutTracing(this)
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui")
+ @Presubmit
+ @Test
+ fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
new file mode 100644
index 0000000..8f4393f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.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.wm.shell.flicker.splitscreen.benchmark
+
+import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
+import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) :
+ SplitScreenBase(flicker) {
+ protected val thirdApp = SplitScreenUtils.getIme(instrumentation)
+ protected val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ teardown {
+ thirdApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ }
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index de967bf..afec1ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -33,6 +33,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +43,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.bubbles.BubbleInfo;
import org.junit.Before;
import org.junit.Test;
@@ -77,7 +79,7 @@
Intent target = new Intent(mContext, BubblesTestActivity.class);
Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, target, PendingIntent.FLAG_MUTABLE),
- Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
+ Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
.build();
when(mSbn.getNotification()).thenReturn(mNotif);
when(mNotif.getBubbleMetadata()).thenReturn(metadata);
@@ -179,6 +181,34 @@
assertThat(bubble.isConversation()).isFalse();
}
+ @Test
+ public void testBubbleAsBubbleBarBubble_withShortcut() {
+ Bubble bubble = createBubbleWithShortcut();
+ BubbleInfo bubbleInfo = bubble.asBubbleBarBubble();
+
+ assertThat(bubble.getShortcutInfo()).isNotNull();
+ assertThat(bubbleInfo.getShortcutId()).isNotNull();
+ assertThat(bubbleInfo.getShortcutId()).isEqualTo(bubble.getShortcutId());
+ assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey());
+ assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier());
+ assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName());
+ }
+
+ @Test
+ public void testBubbleAsBubbleBarBubble_withoutShortcut() {
+ Intent intent = new Intent(mContext, BubblesTestActivity.class);
+ intent.setPackage(mContext.getPackageName());
+ Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1 /* userId */),
+ null /* icon */, mMainExecutor);
+ BubbleInfo bubbleInfo = bubble.asBubbleBarBubble();
+
+ assertThat(bubble.getShortcutInfo()).isNull();
+ assertThat(bubbleInfo.getShortcutId()).isNull();
+ assertThat(bubbleInfo.getKey()).isEqualTo(bubble.getKey());
+ assertThat(bubbleInfo.getUserId()).isEqualTo(bubble.getUser().getIdentifier());
+ assertThat(bubbleInfo.getPackageName()).isEqualTo(bubble.getPackageName());
+ }
+
private Bubble createBubbleWithShortcut() {
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext)
.setId("mockShortcutId")
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 1347e06..60ee918 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -112,9 +112,9 @@
@Test
public void testOnTaskProfileLocked() {
ActivityManager.RunningTaskInfo info = mock(ActivityManager.RunningTaskInfo.class);
- mImpl.onTaskProfileLocked(info);
- verify(mCallback).onTaskProfileLocked(eq(info));
- verify(mOtherCallback).onTaskProfileLocked(eq(info));
+ mImpl.onTaskProfileLocked(info, 0);
+ verify(mCallback).onTaskProfileLocked(eq(info), eq(0));
+ verify(mOtherCallback).onTaskProfileLocked(eq(info), eq(0));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 63de74f..d6387ee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -82,6 +83,8 @@
@RunWith(AndroidTestingRunner.class)
public class DesktopModeControllerTest extends ShellTestCase {
+ private static final int SECOND_DISPLAY = 2;
+
@Mock
private ShellController mShellController;
@Mock
@@ -248,22 +251,22 @@
public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
// Set up two active tasks on desktop, task2 is on top of task1.
RunningTaskInfo freeformTask1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId);
mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- freeformTask1.taskId, false /* visible */);
+ DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */);
RunningTaskInfo freeformTask2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- freeformTask2.taskId, false /* visible */);
+ DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
freeformTask1);
when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
freeformTask2);
// Run show desktop apps logic
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Check wct has reorder calls
@@ -283,17 +286,19 @@
@Test
public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() {
final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Check wct has reorder calls
@@ -312,17 +317,19 @@
@Test
public void testShowDesktopApps_someAppsInvisible_reordersAll() {
final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ false /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
- mController.showDesktopApps();
+ mController.showDesktopApps(DEFAULT_DISPLAY);
final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Both tasks should be reordered to top, even if one was already visible.
@@ -336,38 +343,87 @@
}
@Test
+ public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+ RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(
+ DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */);
+ when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn(
+ taskDefaultDisplay);
+
+ RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY);
+ mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(
+ SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */);
+ when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn(
+ taskSecondDisplay);
+
+ mController.showDesktopApps(DEFAULT_DISPLAY);
+
+ WindowContainerTransaction wct = getBringAppsToFrontTransaction();
+ assertThat(wct.getHierarchyOps()).hasSize(1);
+ HierarchyOp op = wct.getHierarchyOps().get(0);
+ assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder());
+ }
+
+ @Test
public void testGetVisibleTaskCount_noTasks_returnsZero() {
- assertThat(mController.getVisibleTaskCount()).isEqualTo(0);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0);
}
@Test
public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ true /* visible */);
- assertThat(mController.getVisibleTaskCount()).isEqualTo(2);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2);
}
@Test
public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task1.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
+ true /* visible */);
RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(task2.taskId);
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, false /* visible */);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
+ false /* visible */);
- assertThat(mController.getVisibleTaskCount()).isEqualTo(1);
+ assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1);
+ }
+
+ @Test
+ public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ RunningTaskInfo taskDefaultDisplay = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY,
+ taskDefaultDisplay.taskId,
+ true /* visible */);
+
+ RunningTaskInfo taskSecondDisplay = createFreeformTask();
+ mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
+ mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY,
+ taskSecondDisplay.taskId,
+ true /* visible */);
+
+ assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 45cb3a0..3bc2f0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.desktopmode
import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,8 +43,8 @@
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
assertThat(repo.isActiveTask(1)).isTrue()
}
@@ -51,9 +53,9 @@
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- repo.addActiveTask(1)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(1)
}
@Test
@@ -61,9 +63,22 @@
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
- repo.addActiveTask(2)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+ }
+
+ @Test
+ fun addActiveTask_multipleDisplays_notifiesCorrectListener() {
+ val listener = TestListener()
+ repo.addActiveTaskListener(listener)
+
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 2)
+ repo.addActiveTask(SECOND_DISPLAY, taskId = 3)
+
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
+ assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(1)
}
@Test
@@ -71,10 +86,10 @@
val listener = TestListener()
repo.addActiveTaskListener(listener)
- repo.addActiveTask(1)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
repo.removeActiveTask(1)
// Notify once for add and once for remove
- assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(2)
assertThat(repo.isActiveTask(1)).isFalse()
}
@@ -83,7 +98,17 @@
val listener = TestListener()
repo.addActiveTaskListener(listener)
repo.removeActiveTask(99)
- assertThat(listener.activeTaskChangedCalls).isEqualTo(0)
+ assertThat(listener.activeChangesOnDefaultDisplay).isEqualTo(0)
+ }
+
+ @Test
+ fun remoteActiveTask_listenerForOtherDisplayNotNotified() {
+ val listener = TestListener()
+ repo.addActiveTaskListener(listener)
+ repo.addActiveTask(DEFAULT_DISPLAY, taskId = 1)
+ repo.removeActiveTask(1)
+ assertThat(listener.activeChangesOnSecondaryDisplay).isEqualTo(0)
+ assertThat(repo.isActiveTask(1)).isFalse()
}
@Test
@@ -93,14 +118,27 @@
@Test
fun addListener_notifiesVisibleFreeformTask() {
- repo.updateVisibleFreeformTasks(1, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
+ // One call as adding listener notifies it
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(0)
}
@Test
@@ -108,13 +146,61 @@
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
- repo.updateVisibleFreeformTasks(1, true)
- repo.updateVisibleFreeformTasks(2, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- // Equal to 2 because adding the listener notifies the current state
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_addVisibleTaskNotifiesListenerForThatDisplay() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isFalse()
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(0)
+
+ repo.updateVisibleFreeformTasks(displayId = 1, taskId = 2, visible = true)
+ executor.flushAll()
+
+ // Listener for secondary display is notified
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue()
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ // No changes to listener for default display
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_taskOnDefaultBecomesVisibleOnSecondDisplay_listenersNotified() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ executor.flushAll()
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+
+ // Mark task 1 visible on secondary display
+ repo.updateVisibleFreeformTasks(displayId = 1, taskId = 1, visible = true)
+ executor.flushAll()
+
+ // Default display should have 2 calls
+ // 1 - visible task added
+ // 2 - visible task removed
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
+
+ // Secondary display should have 1 call for visible task added
+ assertThat(listener.visibleChangesOnSecondaryDisplay).isEqualTo(1)
+ assertThat(listener.hasVisibleTasksOnSecondaryDisplay).isTrue()
}
@Test
@@ -122,52 +208,83 @@
val listener = TestVisibilityListener()
val executor = TestShellExecutor()
repo.addVisibleTasksListener(listener, executor)
- repo.updateVisibleFreeformTasks(1, true)
- repo.updateVisibleFreeformTasks(2, true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isTrue()
- repo.updateVisibleFreeformTasks(1, false)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isTrue()
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
executor.flushAll()
- // Equal to 2 because adding the listener notifies the current state
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(1)
- repo.updateVisibleFreeformTasks(2, false)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
executor.flushAll()
- assertThat(listener.hasVisibleFreeformTasks).isFalse()
- assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(3)
+ assertThat(listener.hasVisibleTasksOnDefaultDisplay).isFalse()
+ assertThat(listener.visibleChangesOnDefaultDisplay).isEqualTo(2)
}
@Test
fun getVisibleTaskCount() {
// No tasks, count is 0
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
// New task increments count to 1
- repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Visibility update to same task does not increase count
- repo.updateVisibleFreeformTasks(taskId = 1, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Second task visible increments count
- repo.updateVisibleFreeformTasks(taskId = 2, visible = true)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(2)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
// Hiding a task decrements count
- repo.updateVisibleFreeformTasks(taskId = 1, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(1)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
// Hiding all tasks leaves count at 0
- repo.updateVisibleFreeformTasks(taskId = 2, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = false)
+ assertThat(repo.getVisibleTaskCount(displayId = 9)).isEqualTo(0)
// Hiding a not existing task, count remains at 0
- repo.updateVisibleFreeformTasks(taskId = 999, visible = false)
- assertThat(repo.getVisibleTaskCount()).isEqualTo(0)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 999, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ }
+
+ @Test
+ fun getVisibleTaskCount_multipleDisplays() {
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+
+ // New task on default display increments count for that display only
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(0)
+
+ // New task on secondary display, increments count for that display only
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 2, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
+
+ // Marking task visible on another display, updates counts for both displays
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+
+ // Marking task that is on secondary display, hidden on default display, does not affect
+ // secondary display
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(2)
+
+ // Hiding a task on that display, decrements count
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = false)
+ assertThat(repo.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
+ assertThat(repo.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
}
@Test
@@ -197,19 +314,40 @@
}
class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
- var activeTaskChangedCalls = 0
- override fun onActiveTasksChanged() {
- activeTaskChangedCalls++
+ var activeChangesOnDefaultDisplay = 0
+ var activeChangesOnSecondaryDisplay = 0
+ override fun onActiveTasksChanged(displayId: Int) {
+ when (displayId) {
+ DEFAULT_DISPLAY -> activeChangesOnDefaultDisplay++
+ SECOND_DISPLAY -> activeChangesOnSecondaryDisplay++
+ else -> fail("Active task listener received unexpected display id: $displayId")
+ }
}
}
class TestVisibilityListener : DesktopModeTaskRepository.VisibleTasksListener {
- var hasVisibleFreeformTasks = false
- var visibleFreeformTaskChangedCalls = 0
+ var hasVisibleTasksOnDefaultDisplay = false
+ var hasVisibleTasksOnSecondaryDisplay = false
- override fun onVisibilityChanged(hasVisibleTasks: Boolean) {
- hasVisibleFreeformTasks = hasVisibleTasks
- visibleFreeformTaskChangedCalls++
+ var visibleChangesOnDefaultDisplay = 0
+ var visibleChangesOnSecondaryDisplay = 0
+
+ override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {
+ when (displayId) {
+ DEFAULT_DISPLAY -> {
+ hasVisibleTasksOnDefaultDisplay = hasVisibleFreeformTasks
+ visibleChangesOnDefaultDisplay++
+ }
+ SECOND_DISPLAY -> {
+ hasVisibleTasksOnSecondaryDisplay = hasVisibleFreeformTasks
+ visibleChangesOnSecondaryDisplay++
+ }
+ else -> fail("Visible task listener received unexpected display id: $displayId")
+ }
}
}
+
+ companion object {
+ const val SECOND_DISPLAY = 1
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index c9bd695..f506969 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -25,6 +25,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.os.Binder
import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
@@ -84,10 +85,10 @@
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
- lateinit var mockitoSession: StaticMockitoSession
- lateinit var controller: DesktopTasksController
- lateinit var shellInit: ShellInit
- lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var controller: DesktopTasksController
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -155,7 +156,7 @@
markTaskHidden(task1)
markTaskHidden(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -173,7 +174,7 @@
markTaskVisible(task1)
markTaskVisible(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -191,7 +192,7 @@
markTaskHidden(task1)
markTaskVisible(task2)
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(3)
@@ -205,7 +206,7 @@
fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
val homeTask = setUpHomeTask()
- controller.showDesktopApps()
+ controller.showDesktopApps(DEFAULT_DISPLAY)
val wct = getLatestWct(expectTransition = TRANSIT_NONE)
assertThat(wct.hierarchyOps).hasSize(1)
@@ -213,8 +214,26 @@
}
@Test
+ fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+ val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
+ val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+ setUpHomeTask(SECOND_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(taskDefaultDisplay)
+ markTaskHidden(taskSecondDisplay)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY)
+
+ val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Expect order to be from bottom: home, task
+ wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
+ wct.assertReorderAt(index = 1, taskDefaultDisplay)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
- assertThat(controller.getVisibleTaskCount()).isEqualTo(0)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@Test
@@ -222,7 +241,7 @@
setUpHomeTask()
setUpFreeformTask().also(::markTaskVisible)
setUpFreeformTask().also(::markTaskVisible)
- assertThat(controller.getVisibleTaskCount()).isEqualTo(2)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2)
}
@Test
@@ -230,7 +249,15 @@
setUpHomeTask()
setUpFreeformTask().also(::markTaskVisible)
setUpFreeformTask().also(::markTaskHidden)
- assertThat(controller.getVisibleTaskCount()).isEqualTo(1)
+ assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1)
+ }
+
+ @Test
+ fun getVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
+ setUpHomeTask()
+ setUpFreeformTask(DEFAULT_DISPLAY).also(::markTaskVisible)
+ setUpFreeformTask(SECOND_DISPLAY).also(::markTaskVisible)
+ assertThat(controller.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1)
}
@Test
@@ -258,6 +285,7 @@
controller.moveToDesktop(fullscreenTask)
with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ // Operations should include home task, freeform task
assertThat(hierarchyOps).hasSize(3)
assertReorderSequence(homeTask, freeformTask, fullscreenTask)
assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
@@ -266,6 +294,28 @@
}
@Test
+ fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
+ setUpHomeTask(displayId = DEFAULT_DISPLAY)
+ val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val fullscreenTaskDefault = setUpFullscreenTask(displayId = DEFAULT_DISPLAY)
+ markTaskHidden(freeformTaskDefault)
+
+ val homeTaskSecond = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val freeformTaskSecond = setUpFreeformTask(displayId = SECOND_DISPLAY)
+ markTaskHidden(freeformTaskSecond)
+
+ controller.moveToDesktop(fullscreenTaskDefault)
+
+ with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ // Check that hierarchy operations do not include tasks from second display
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(homeTaskSecond.token.asBinder())
+ assertThat(hierarchyOps.map { it.container })
+ .doesNotContain(freeformTaskSecond.token.asBinder())
+ }
+ }
+
+ @Test
fun moveToFullscreen() {
val task = setUpFreeformTask()
controller.moveToFullscreen(task)
@@ -281,6 +331,19 @@
}
@Test
+ fun moveToFullscreen_secondDisplayTaskHasFreeform_secondDisplayNotAffected() {
+ val taskDefaultDisplay = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val taskSecondDisplay = setUpFreeformTask(displayId = SECOND_DISPLAY)
+
+ controller.moveToFullscreen(taskDefaultDisplay)
+
+ with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
+ assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
+ }
+ }
+
+ @Test
fun getTaskWindowingMode() {
val fullscreenTask = setUpFullscreenTask()
val freeformTask = setUpFreeformTask()
@@ -324,6 +387,18 @@
}
@Test
+ fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY)
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result =
+ controller.handleRequest(Binder(), createTransition(fullscreenTaskDefaultDisplay))
+ assertThat(result).isNull()
+ }
+
+ @Test
fun handleRequest_freeformTask_freeformVisible_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -362,6 +437,18 @@
}
@Test
+ fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_returnSwitchToFullscreenWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+ assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
fun handleRequest_notOpenOrToFrontTransition_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -400,35 +487,43 @@
assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
}
- private fun setUpFreeformTask(): RunningTaskInfo {
- val task = createFreeformTask()
+ private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createFreeformTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- desktopModeTaskRepository.addActiveTask(task.taskId)
+ desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId)
runningTasks.add(task)
return task
}
- private fun setUpHomeTask(): RunningTaskInfo {
- val task = createHomeTask()
+ private fun setUpHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createHomeTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
}
- private fun setUpFullscreenTask(): RunningTaskInfo {
- val task = createFullscreenTask()
+ private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ val task = createFullscreenTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
}
private fun markTaskVisible(task: RunningTaskInfo) {
- desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true)
+ desktopModeTaskRepository.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = true
+ )
}
private fun markTaskHidden(task: RunningTaskInfo) {
- desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false)
+ desktopModeTaskRepository.updateVisibleFreeformTasks(
+ task.displayId,
+ task.taskId,
+ visible = false
+ )
}
private fun getLatestWct(
@@ -457,6 +552,10 @@
): TransitionRequestInfo {
return TransitionRequestInfo(type, task, null /* remoteTransition */)
}
+
+ companion object {
+ const val SECOND_DISPLAY = 2
+ }
}
private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index dc91d75..cf1ff32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -21,14 +21,17 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.TestRunningTaskInfoBuilder
class DesktopTestHelpers {
companion object {
/** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */
@JvmStatic
- fun createFreeformTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
@@ -38,8 +41,10 @@
/** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */
@JvmStatic
- fun createFullscreenTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
@@ -49,8 +54,10 @@
/** Create a new home task */
@JvmStatic
- fun createHomeTask(): RunningTaskInfo {
+ @JvmOverloads
+ fun createHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
return TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
.setToken(MockToken().token())
.setActivityType(ACTIVITY_TYPE_HOME)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 6995d10..04f2c99 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -268,7 +268,7 @@
}
@Test
- public void saveReentryState_userHasResized_savesSize() {
+ public void saveReentryState_nonEmptyUserResizeBounds_savesSize() {
final Rect bounds = new Rect(0, 0, 10, 10);
final Rect resizedBounds = new Rect(0, 0, 30, 30);
when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
@@ -281,6 +281,19 @@
}
@Test
+ public void saveReentryState_emptyUserResizeBounds_savesSize() {
+ final Rect bounds = new Rect(0, 0, 10, 10);
+ final Rect resizedBounds = new Rect(0, 0, 0, 0);
+ when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
+ when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
+ when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
+
+ mPipController.saveReentryState(bounds);
+
+ verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f);
+ }
+
+ @Test
public void onDisplayConfigurationChanged_inPip_movePip() {
final int displayId = 1;
final Rect bounds = new Rect(0, 0, 10, 10);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index a9f311f..92cbf7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -180,8 +180,9 @@
TestRemoteTransition testRemote = new TestRemoteTransition();
IBinder transition = mSplitScreenTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null);
+ TRANSIT_OPEN, new WindowContainerTransaction(),
+ new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -397,7 +398,7 @@
}
private TransitionInfo createEnterPairInfo() {
- return new TransitionInfoBuilder(TRANSIT_SPLIT_SCREEN_PAIR_OPEN, 0)
+ return new TransitionInfoBuilder(TRANSIT_OPEN, 0)
.addChange(TRANSIT_OPEN, mMainChild)
.addChange(TRANSIT_OPEN, mSideChild)
.build();
@@ -406,9 +407,9 @@
private void enterSplit() {
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
+ TRANSIT_OPEN, new WindowContainerTransaction(),
new RemoteTransition(new TestRemoteTransition(), "Test"),
- mStageCoordinator, null, null);
+ mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 784ad9b..1a1bebd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -126,6 +126,12 @@
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testUnknownTaskVanished() {
+ final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+ mStageTaskListener.onTaskVanished(task);
+ }
+
@Test
public void testTaskVanished() {
// With shell transitions, the transition manages status changes, so skip this test.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 94c064b..84ccdde 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -14,10 +14,10 @@
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM
-import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
-import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
-import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,21 +30,21 @@
import org.mockito.MockitoAnnotations
/**
- * Tests for [TaskPositioner].
+ * Tests for [FluidResizeTaskPositioner].
*
* Build/Install/Run:
- * atest WMShellUnitTests:TaskPositionerTest
+ * atest WMShellUnitTests:FluidResizeTaskPositionerTest
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class TaskPositionerTest : ShellTestCase() {
+class FluidResizeTaskPositionerTest : ShellTestCase() {
@Mock
private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
@Mock
private lateinit var mockWindowDecoration: WindowDecoration<*>
@Mock
- private lateinit var mockDragStartListener: TaskPositioner.DragStartListener
+ private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener
@Mock
private lateinit var taskToken: WindowContainerToken
@@ -58,18 +58,19 @@
@Mock
private lateinit var mockDisplay: Display
- private lateinit var taskPositioner: TaskPositioner
+ private lateinit var taskPositioner: FluidResizeTaskPositioner
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- taskPositioner = TaskPositioner(
+ taskPositioner =
+ FluidResizeTaskPositioner(
mockShellTaskOrganizer,
mockWindowDecoration,
mockDisplayController,
mockDragStartListener
- )
+ )
`when`(taskToken.asBinder()).thenReturn(taskBinder)
`when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
new file mode 100644
index 0000000..bf365ca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Rect
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [VeiledResizeTaskPositioner].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:VeiledResizeTaskPositionerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class VeiledResizeTaskPositionerTest : ShellTestCase() {
+
+ @Mock
+ private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+ @Mock
+ private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
+ @Mock
+ private lateinit var mockDragStartListener: DragPositioningCallbackUtility.DragStartListener
+
+ @Mock
+ private lateinit var taskToken: WindowContainerToken
+ @Mock
+ private lateinit var taskBinder: IBinder
+
+ @Mock
+ private lateinit var mockDisplayController: DisplayController
+ @Mock
+ private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock
+ private lateinit var mockDisplay: Display
+
+ private lateinit var taskPositioner: VeiledResizeTaskPositioner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ taskPositioner =
+ VeiledResizeTaskPositioner(
+ mockShellTaskOrganizer,
+ mockDesktopWindowDecoration,
+ mockDisplayController,
+ mockDragStartListener
+ )
+
+ `when`(taskToken.asBinder()).thenReturn(taskBinder)
+ `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
+ `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+ `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ }
+
+ mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ minWidth = MIN_WIDTH
+ minHeight = MIN_HEIGHT
+ defaultMinSize = DEFAULT_MIN
+ displayId = DISPLAY_ID
+ configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ }
+ mockDesktopWindowDecoration.mDisplay = mockDisplay
+ `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+ }
+
+ @Test
+ fun testDragResize_noMove_showsResizeVeil() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+ verify(mockDesktopWindowDecoration).showResizeVeil()
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+ verify(mockDesktopWindowDecoration).hideResizeVeil()
+ }
+
+ @Test
+ fun testDragResize_movesTask_doesNotShowResizeVeil() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED,
+ STARTING_BOUNDS.left.toFloat() + 50,
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningMove(
+ STARTING_BOUNDS.left.toFloat() + 60,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.left += 10
+ rectAfterMove.right += 10
+ rectAfterMove.top += 10
+ rectAfterMove.bottom += 10
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterMove
+ }
+ })
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat() + 70,
+ STARTING_BOUNDS.top.toFloat() + 20
+ )
+ val rectAfterEnd = Rect(rectAfterMove)
+ rectAfterEnd.left += 10
+ rectAfterEnd.top += 10
+ rectAfterEnd.right += 10
+ rectAfterEnd.bottom += 10
+
+ verify(mockDesktopWindowDecoration, never()).createResizeVeil()
+ verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_resize_boundsUpdateOnEnd() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+ STARTING_BOUNDS.right.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+ verify(mockDesktopWindowDecoration).showResizeVeil()
+
+ taskPositioner.onDragPositioningMove(
+ STARTING_BOUNDS.right.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+
+ val rectAfterMove = Rect(STARTING_BOUNDS)
+ rectAfterMove.right += 10
+ rectAfterMove.top += 10
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterMove
+ }
+ })
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.right.toFloat() + 20,
+ STARTING_BOUNDS.top.toFloat() + 20
+ )
+ val rectAfterEnd = Rect(rectAfterMove)
+ rectAfterEnd.right += 10
+ rectAfterEnd.top += 10
+ verify(mockDesktopWindowDecoration, times(2)).updateResizeVeil(any())
+ verify(mockDesktopWindowDecoration).hideResizeVeil()
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterEnd
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_noEffectiveMove_skipsTransactionOnEnd() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+ verify(mockDesktopWindowDecoration).showResizeVeil()
+
+ taskPositioner.onDragPositioningMove(
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ STARTING_BOUNDS.left.toFloat() + 10,
+ STARTING_BOUNDS.top.toFloat() + 10
+ )
+ verify(mockDesktopWindowDecoration).hideResizeVeil()
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)
+ }
+ })
+ }
+
+ companion object {
+ private const val TASK_ID = 5
+ private const val MIN_WIDTH = 10
+ private const val MIN_HEIGHT = 10
+ private const val DENSITY_DPI = 20
+ private const val DEFAULT_MIN = 40
+ private const val DISPLAY_ID = 1
+ private const val NAVBAR_HEIGHT = 50
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ private val STABLE_BOUNDS = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+ )
+ }
+}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 193544e..88b9643 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -934,13 +934,25 @@
}
}
levelCaps = createFromProfileLevel(mMime, profile, maxLevel);
- // remove profile from this format otherwise levelCaps.isFormatSupported will
- // get into this same conditon and loop forever.
- Map<String, Object> mapWithoutProfile = new HashMap<>(map);
- mapWithoutProfile.remove(MediaFormat.KEY_PROFILE);
- MediaFormat formatWithoutProfile = new MediaFormat(mapWithoutProfile);
- if (levelCaps != null && !levelCaps.isFormatSupported(formatWithoutProfile)) {
- return false;
+ // We must remove the profile from this format otherwise levelCaps.isFormatSupported
+ // will get into this same condition and loop forever. Furthermore, since levelCaps
+ // does not contain features and bitrate specific keys, keep only keys relevant for
+ // a level check.
+ Map<String, Object> levelCriticalFormatMap = new HashMap<>(map);
+ final Set<String> criticalKeys =
+ isVideo() ? VideoCapabilities.VIDEO_LEVEL_CRITICAL_FORMAT_KEYS :
+ isAudio() ? AudioCapabilities.AUDIO_LEVEL_CRITICAL_FORMAT_KEYS :
+ null;
+
+ // critical keys will always contain KEY_MIME, but should also contain others to be
+ // meaningful
+ if (criticalKeys != null && criticalKeys.size() > 1 && levelCaps != null) {
+ levelCriticalFormatMap.keySet().retainAll(criticalKeys);
+
+ MediaFormat levelCriticalFormat = new MediaFormat(levelCriticalFormatMap);
+ if (!levelCaps.isFormatSupported(levelCriticalFormat)) {
+ return false;
+ }
}
}
if (mAudioCaps != null && !mAudioCaps.supportsFormat(format)) {
@@ -1633,6 +1645,16 @@
}
}
+ /* package private */
+ // must not contain KEY_PROFILE
+ static final Set<String> AUDIO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
+ // We don't set level-specific limits for audio codecs today. Key candidates would
+ // be sample rate, bit rate or channel count.
+ // MediaFormat.KEY_SAMPLE_RATE,
+ // MediaFormat.KEY_CHANNEL_COUNT,
+ // MediaFormat.KEY_BIT_RATE,
+ MediaFormat.KEY_MIME);
+
/** @hide */
public boolean supportsFormat(MediaFormat format) {
Map<String, Object> map = format.getMap();
@@ -2357,6 +2379,15 @@
return ok;
}
+ /* package private */
+ // must not contain KEY_PROFILE
+ static final Set<String> VIDEO_LEVEL_CRITICAL_FORMAT_KEYS = Set.of(
+ MediaFormat.KEY_WIDTH,
+ MediaFormat.KEY_HEIGHT,
+ MediaFormat.KEY_FRAME_RATE,
+ MediaFormat.KEY_BIT_RATE,
+ MediaFormat.KEY_MIME);
+
/**
* @hide
* @throws java.lang.ClassCastException */
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index b6d70af..78352871 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -54,7 +54,6 @@
import com.android.internal.app.ISoundTriggerService;
import com.android.internal.app.ISoundTriggerSession;
-import com.android.internal.util.Preconditions;
import java.util.HashMap;
import java.util.List;
@@ -67,6 +66,8 @@
* models. Usage of this class is restricted to system or signature applications only. This allows
* OEMs to write apps that can manage non-voice based sound trigger models.
*
+ * If no ST module is available, {@link getModuleProperties()} will return {@code null}, and all
+ * other methods will throw {@link IllegalStateException}.
* @hide
*/
@SystemApi
@@ -82,7 +83,7 @@
// Stores a mapping from the sound model UUID to the SoundTriggerInstance created by
// the createSoundTriggerDetector() call.
- private final HashMap<UUID, SoundTriggerDetector> mReceiverInstanceMap;
+ private final HashMap<UUID, SoundTriggerDetector> mReceiverInstanceMap = new HashMap<>();
/**
* @hide
@@ -121,7 +122,6 @@
}
mContext = context;
mSoundTriggerService = soundTriggerService;
- mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
}
/**
@@ -160,7 +160,7 @@
.findFirst()
.orElse(null);
if (moduleProps == null) {
- throw new IllegalStateException("Fake ST HAL should always be available");
+ throw new AssertionError("Fake ST HAL should always be available");
}
return moduleProps;
}
@@ -183,7 +183,6 @@
}
mContext = Objects.requireNonNull(context);
mSoundTriggerService = Objects.requireNonNull(soundTriggerService);
- mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
}
/**
@@ -241,7 +240,8 @@
}
try {
GenericSoundModel model =
- mSoundTriggerSession.getSoundModel(new ParcelUuid(soundModelId));
+ mSoundTriggerSession.getSoundModel(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)));
if (model == null) {
return null;
}
@@ -265,7 +265,8 @@
}
try {
- mSoundTriggerSession.deleteSoundModel(new ParcelUuid(soundModelId));
+ mSoundTriggerSession.deleteSoundModel(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -291,8 +292,8 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public SoundTriggerDetector createSoundTriggerDetector(UUID soundModelId,
@NonNull SoundTriggerDetector.Callback callback, @Nullable Handler handler) {
- if (soundModelId == null || mSoundTriggerSession == null) {
- return null;
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
SoundTriggerDetector oldInstance = mReceiverInstanceMap.get(soundModelId);
@@ -301,7 +302,8 @@
}
try {
SoundTriggerDetector newInstance = new SoundTriggerDetector(mSoundTriggerSession,
- mSoundTriggerSession.getSoundModel(new ParcelUuid(soundModelId)),
+ mSoundTriggerSession.getSoundModel(
+ new ParcelUuid(Objects.requireNonNull(soundModelId))),
callback, handler);
mReceiverInstanceMap.put(soundModelId, newInstance);
return newInstance;
@@ -469,8 +471,8 @@
@UnsupportedAppUsage
@TestApi
public int loadSoundModel(@NonNull SoundModel soundModel) {
- if (soundModel == null || mSoundTriggerSession == null) {
- return STATUS_ERROR;
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
@@ -513,11 +515,11 @@
@UnsupportedAppUsage
public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
@NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
- Preconditions.checkNotNull(soundModelId);
- Preconditions.checkNotNull(detectionService);
- Preconditions.checkNotNull(config);
+ Objects.requireNonNull(soundModelId);
+ Objects.requireNonNull(detectionService);
+ Objects.requireNonNull(config);
if (mSoundTriggerSession == null) {
- return STATUS_ERROR;
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
return mSoundTriggerSession.startRecognitionForService(new ParcelUuid(soundModelId),
@@ -534,11 +536,12 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int stopRecognition(UUID soundModelId) {
- if (soundModelId == null || mSoundTriggerSession == null) {
- return STATUS_ERROR;
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
- return mSoundTriggerSession.stopRecognitionForService(new ParcelUuid(soundModelId));
+ return mSoundTriggerSession.stopRecognitionForService(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -551,12 +554,12 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int unloadSoundModel(UUID soundModelId) {
- if (soundModelId == null || mSoundTriggerSession == null) {
- return STATUS_ERROR;
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
return mSoundTriggerSession.unloadSoundModel(
- new ParcelUuid(soundModelId));
+ new ParcelUuid(Objects.requireNonNull(soundModelId)));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -604,7 +607,10 @@
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
public int getModelState(UUID soundModelId) {
- if (soundModelId == null || mSoundTriggerSession == null) {
+ if (mSoundTriggerSession == null) {
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
+ }
+ if (soundModelId == null) {
return STATUS_ERROR;
}
try {
@@ -652,11 +658,12 @@
public int setParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam, int value) {
if (mSoundTriggerSession == null) {
- return SoundTrigger.STATUS_INVALID_OPERATION;
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
- return mSoundTriggerSession.setParameter(new ParcelUuid(soundModelId), modelParam,
+ return mSoundTriggerSession.setParameter(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)), modelParam,
value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -679,11 +686,11 @@
public int getParameter(@NonNull UUID soundModelId,
@ModelParams int modelParam) {
if (mSoundTriggerSession == null) {
- throw new IllegalArgumentException("Sound model is not loaded: "
- + soundModelId.toString());
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
- return mSoundTriggerSession.getParameter(new ParcelUuid(soundModelId), modelParam);
+ return mSoundTriggerSession.getParameter(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -703,10 +710,11 @@
public ModelParamRange queryParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam) {
if (mSoundTriggerSession == null) {
- return null;
+ throw new IllegalStateException("No underlying SoundTriggerModule available");
}
try {
- return mSoundTriggerSession.queryParameter(new ParcelUuid(soundModelId), modelParam);
+ return mSoundTriggerSession.queryParameter(
+ new ParcelUuid(Objects.requireNonNull(soundModelId)), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 9552200..44aff64 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -385,6 +385,10 @@
process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
+ if (env->ExceptionCheck()) {
+ return UNKNOWN_ERROR;
+ }
+
// update the piid
Parcel *request = parcelForJavaObject(env, piidParcel);
auto reply = std::make_unique<Parcel>();
@@ -407,6 +411,10 @@
process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
+ if (env->ExceptionCheck()) {
+ return UNKNOWN_ERROR;
+ }
+
// update the piid
Parcel *request = parcelForJavaObject(env, piidParcel);
auto reply = std::make_unique<Parcel>();
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
index 6bfcd82..8c35da1 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -58,6 +58,7 @@
android:textSize="14sp"
android:layout_marginTop="2dp"
style="@style/TextAppearance"
+ android:focusable="true"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 74072e9..2502bbf 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -113,17 +113,11 @@
<!-- Back button for the helper consent dialog [CHAR LIMIT=30] -->
<string name="consent_back">Back</string>
- <!-- Action when permission list view is expanded CHAR LIMIT=30] -->
- <string name="permission_expanded">Expanded</string>
+ <!-- Expand permission in the list CHAR LIMIT=30] -->
+ <string name="permission_expand">Expand <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string>
- <!-- Expand action permission list CHAR LIMIT=30] -->
- <string name="permission_expand">Expand</string>
-
- <!-- Action when permission list view is collapsed CHAR LIMIT=30] -->
- <string name="permission_collapsed">Collapsed</string>
-
- <!-- Collapse action permission list CHAR LIMIT=30] -->
- <string name="permission_collapse">Collapse</string>
+ <!-- Collapse permission int the list CHAR LIMIT=30] -->
+ <string name="permission_collapse">Collapse <xliff:g id="permission_type" example="Notification">%1$s</xliff:g></string>
<!-- ================== System data transfer ==================== -->
<!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
index b86ef64..f594bf2 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -124,7 +124,7 @@
}
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand);
+ AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand, 0);
// Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also
// make the summary invisible by default.
@@ -137,18 +137,16 @@
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less);
viewHolder.mPermissionSummary.setVisibility(View.VISIBLE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less);
- view.setContentDescription(mContext.getString(R.string.permission_expanded));
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_collapse);
- viewHolder.mPermissionSummary.setFocusable(true);
+ AccessibilityNodeInfo.ACTION_CLICK,
+ R.string.permission_collapse, R.string.permission_expand);
} else {
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_more);
viewHolder.mPermissionSummary.setVisibility(View.GONE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
- view.setContentDescription(mContext.getString(R.string.permission_collapsed));
setAccessibility(view, viewType,
- AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expanded);
- viewHolder.mPermissionSummary.setFocusable(false);
+ AccessibilityNodeInfo.ACTION_CLICK,
+ R.string.permission_expand, R.string.permission_collapse);
}
});
} else {
@@ -200,14 +198,20 @@
}
}
- private void setAccessibility(View view, int viewType, int action, int resourceId) {
- final String actionString = mContext.getString(resourceId);
+ private void setAccessibility(View view, int viewType, int action, int statusResourceId,
+ int actionResourceId) {
final String permission = mContext.getString(sTitleMap.get(viewType));
+
+ if (actionResourceId != 0) {
+ view.announceForAccessibility(
+ getHtmlFromResources(mContext, actionResourceId, permission));
+ }
+
view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(action,
- actionString + permission));
+ getHtmlFromResources(mContext, statusResourceId, permission)));
}
});
}
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 905e0ca..3402857 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -86,6 +86,8 @@
<string name="use_provider_for_all_description">This password manager for <xliff:g id="username" example="becket@gmail.com">%1$s</xliff:g> will store your passwords and passkeys to help you easily sign in</string>
<!-- This is a label for a button that sets this password manager as the default. [CHAR LIMIT=20] -->
<string name="set_as_default">Set as default</string>
+ <!-- This is a button text to navigate the user to their system settings. [CHAR LIMIT=30] -->
+ <string name="settings">Settings</string>
<!-- This is a label for a button that makes this password manager be used just in this specific case. [CHAR LIMIT=20] -->
<string name="use_once">Use once</string>
<!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are passwords and passkeys. [CHAR LIMIT=80] -->
@@ -114,13 +116,13 @@
<!-- Strings for the get flow. -->
<!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking for user confirmation to use the single previously saved credential to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_use_sign_in_for">Use your saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
- <string name="get_dialog_title_choose_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
- <!-- This appears as the title of the dialog asking for user to make a choice from various previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+ <!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+ <!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
+ <string name="get_dialog_title_choose_sign_in_for">Choose a sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <!-- This appears as the title of the dialog asking for user to make a choice from options of available user information (e.g. driver's license, vaccination status) to pass to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
- <!-- This appears as the title of the dialog asking user to use a previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
+ <!-- This appears as the title of the dialog asking user to send a piece of user information (e.g. driver's license, vaccination status) to the app. [CHAR LIMIT=200] -->
<string name="get_dialog_title_use_info_on">Use this info on <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
<!-- This is a label for a button that links the user to different sign-in methods . [CHAR LIMIT=80] -->
<string name="get_dialog_use_saved_passkey_for">Sign in another way</string>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 01f92c4..19b7e85 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -17,6 +17,7 @@
package com.android.settingslib.collapsingtoolbar;
import android.app.ActionBar;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -59,7 +60,8 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) {
+ // for backward compatibility on R devices or wearable devices due to small device size.
+ if (mCustomizeLayoutResId > 0 && (!BuildCompatUtils.isAtLeastS() || isWatch())) {
super.setContentView(mCustomizeLayoutResId);
return;
}
@@ -157,6 +159,14 @@
return getToolbarDelegate().getAppBarLayout();
}
+ private boolean isWatch() {
+ PackageManager packageManager = getPackageManager();
+ if (packageManager == null) {
+ return false;
+ }
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
private CollapsingToolbarDelegate getToolbarDelegate() {
if (mToolbardelegate == null) {
mToolbardelegate = new CollapsingToolbarDelegate(new DelegateCallback());
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 76e1df1..ea4ac2c 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -62,7 +62,6 @@
private SparseIntArray mPostureRotationLockSettings;
private SparseIntArray mPostureDefaultRotationLockSettings;
private SparseIntArray mPostureRotationLockFallbackSettings;
- private String mLastSettingValue;
private List<SettableDeviceState> mSettableDeviceStates;
@VisibleForTesting
@@ -209,10 +208,7 @@
}
private void initializeInMemoryMap() {
- String serializedSetting =
- mSecureSettings.getStringForUser(
- Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
- UserHandle.USER_CURRENT);
+ String serializedSetting = getPersistedSettingValue();
if (TextUtils.isEmpty(serializedSetting)) {
// No settings saved, we should load the defaults and persist them.
fallbackOnDefaults();
@@ -290,19 +286,25 @@
}
private void persistSettingIfChanged(String newSettingValue) {
+ String lastSettingValue = getPersistedSettingValue();
Log.v(TAG, "persistSettingIfChanged: "
- + "last=" + mLastSettingValue + ", "
+ + "last=" + lastSettingValue + ", "
+ "new=" + newSettingValue);
- if (TextUtils.equals(mLastSettingValue, newSettingValue)) {
+ if (TextUtils.equals(lastSettingValue, newSettingValue)) {
return;
}
- mLastSettingValue = newSettingValue;
mSecureSettings.putStringForUser(
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
/* value= */ newSettingValue,
UserHandle.USER_CURRENT);
}
+ private String getPersistedSettingValue() {
+ return mSecureSettings.getStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT);
+ }
+
private void loadDefaults() {
mSettableDeviceStates = new ArrayList<>(mPostureRotationLockDefaults.length);
mPostureDefaultRotationLockSettings = new SparseIntArray(
@@ -351,7 +353,6 @@
pw.println("mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings);
pw.println("mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings);
pw.println("mSettableDeviceStates: " + mSettableDeviceStates);
- pw.println("mLastSettingValue: " + mLastSettingValue);
pw.decreaseIndent();
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
index 9c70be9..6a13eb8 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/PosturesHelper.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN
import android.provider.Settings.Secure.DeviceStateRotationLockKey
@@ -33,6 +34,8 @@
context.resources.getIntArray(R.array.config_halfFoldedDeviceStates)
private val unfoldedDeviceStates =
context.resources.getIntArray(R.array.config_openDeviceStates)
+ private val rearDisplayDeviceStates =
+ context.resources.getIntArray(R.array.config_rearDisplayDeviceStates)
@DeviceStateRotationLockKey
fun deviceStateToPosture(deviceState: Int): Int {
@@ -40,6 +43,7 @@
in foldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_FOLDED
in halfFoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
in unfoldedDeviceStates -> DEVICE_STATE_ROTATION_KEY_UNFOLDED
+ in rearDisplayDeviceStates -> DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
else -> DEVICE_STATE_ROTATION_KEY_UNKNOWN
}
}
@@ -49,6 +53,7 @@
DEVICE_STATE_ROTATION_KEY_FOLDED -> foldedDeviceStates.firstOrNull()
DEVICE_STATE_ROTATION_KEY_HALF_FOLDED -> halfFoldedDeviceStates.firstOrNull()
DEVICE_STATE_ROTATION_KEY_UNFOLDED -> unfoldedDeviceStates.firstOrNull()
+ DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY -> rearDisplayDeviceStates.firstOrNull()
else -> null
}
}
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml
deleted file mode 100644
index c8037c8..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_ic_info.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- copy from frameworks/base/core/res/res/drawable/ic_info.xml-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index ca84db8..b1c26e8 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -46,16 +46,6 @@
android:textAppearance="?android:attr/textAppearanceListItem"
style="@style/MainSwitchText.Settingslib" />
- <ImageView
- android:id="@+id/restricted_icon"
- android:layout_width="@dimen/settingslib_restricted_icon_size"
- android:layout_height="@dimen/settingslib_restricted_icon_size"
- android:tint="?android:attr/colorAccent"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
- android:src="@drawable/settingslib_ic_info"
- android:visibility="gone" />
-
<Switch
android:id="@android:id/switch_widget"
android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 2c2ad92..ab0cf31 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -49,16 +49,6 @@
android:lineBreakWordStyle="phrase"
style="@style/MainSwitchText.Settingslib" />
- <ImageView
- android:id="@+id/restricted_icon"
- android:layout_width="@dimen/settingslib_restricted_icon_size"
- android:layout_height="@dimen/settingslib_restricted_icon_size"
- android:tint="@color/settingslib_accent_primary_variant"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
- android:src="@drawable/settingslib_ic_info"
- android:visibility="gone" />
-
<Switch
android:id="@android:id/switch_widget"
android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index b39d09f..bf34db9 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -38,17 +38,6 @@
android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
android:textAlignment="viewStart"/>
- <ImageView
- android:id="@+id/restricted_icon"
- android:layout_width="@dimen/settingslib_restricted_icon_size"
- android:layout_height="@dimen/settingslib_restricted_icon_size"
- android:tint="?android:attr/colorAccent"
- android:theme="@android:style/Theme.Material"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
- android:src="@drawable/settingslib_ic_info"
- android:visibility="gone"/>
-
<Switch
android:id="@android:id/switch_widget"
android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
index a50fc7c..ad888e5 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
@@ -17,9 +17,8 @@
<resources>
- <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+ <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse">
<item name="android:textSize">20sp</item>
<item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
- <item name="android:textColor">@android:color/black</item>
</style>
</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 88b2c87..0d9ffff 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -17,16 +17,9 @@
<resources>
- <!-- Restricted icon size in switch bar -->
- <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>
-
- <!-- Restricted icon in switch bar -->
- <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen>
-
<!-- Size of title margin -->
<dimen name="settingslib_switch_title_margin">24dp</dimen>
- <!-- SwitchBar sub settings margin start / end -->
+ <!-- SwitchBar sub settings margin start -->
<dimen name="settingslib_switchbar_subsettings_margin_start">56dp</dimen>
- <dimen name="settingslib_switchbar_subsettings_margin_end">16dp</dimen>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index 3c45112..dae48db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -17,6 +17,7 @@
package com.android.settingslib.applications;
import android.app.AppGlobals;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -94,6 +95,10 @@
}
+ public @Nullable String getSummary() {
+ return this.summary;
+ }
+
@Override
public Drawable loadIcon() {
final IconDrawableFactory factory = IconDrawableFactory.newInstance(mContext);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 45c0d78..adaf4a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -14,7 +14,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -26,6 +28,8 @@
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
import android.net.ScoredNetwork;
+import android.net.TransportInfo;
+import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
@@ -34,8 +38,9 @@
import android.os.Looper;
import android.provider.Settings;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.R;
-import com.android.settingslib.Utils;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -46,6 +51,7 @@
/**
* Track status of Wi-Fi for the Sys UI.
*/
+@SuppressLint("MissingPermission")
public class WifiStatusTracker {
private static final int HISTORY_SIZE = 32;
private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -66,8 +72,9 @@
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
private final NetworkCallback mNetworkCallback =
new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
// Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
@@ -75,18 +82,10 @@
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
- boolean isVcnOverWifi = false;
- boolean isWifi = false;
- WifiInfo wifiInfo = null;
- if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- wifiInfo = Utils.tryGetWifiInfoForVcn(networkCapabilities);
- isVcnOverWifi = (wifiInfo != null);
- } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
- wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
- isWifi = true;
- }
+ WifiInfo wifiInfo = getMainOrUnderlyingWifiInfo(networkCapabilities);
+ boolean isWifi = connectionIsWifi(networkCapabilities, wifiInfo);
// As long as it is a WiFi network, we will log it in the dumpsys for debugging.
- if (isVcnOverWifi || isWifi) {
+ if (isWifi) {
String log = new StringBuilder()
.append(SSDF.format(System.currentTimeMillis())).append(",")
.append("onCapabilitiesChanged: ")
@@ -303,17 +302,8 @@
return;
}
NetworkCapabilities networkCapabilities;
- isDefaultNetwork = false;
- if (mDefaultNetworkCapabilities != null) {
- boolean isWifi = mDefaultNetworkCapabilities.hasTransport(
- NetworkCapabilities.TRANSPORT_WIFI);
- boolean isVcnOverWifi = mDefaultNetworkCapabilities.hasTransport(
- NetworkCapabilities.TRANSPORT_CELLULAR)
- && (Utils.tryGetWifiInfoForVcn(mDefaultNetworkCapabilities) != null);
- if (isWifi || isVcnOverWifi) {
- isDefaultNetwork = true;
- }
- }
+ isDefaultNetwork = mDefaultNetworkCapabilities != null
+ && connectionIsWifi(mDefaultNetworkCapabilities);
if (isDefaultNetwork) {
// Wifi is connected and the default network.
networkCapabilities = mDefaultNetworkCapabilities;
@@ -352,6 +342,70 @@
? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi);
}
+ @Nullable
+ private WifiInfo getMainOrUnderlyingWifiInfo(NetworkCapabilities networkCapabilities) {
+ WifiInfo mainWifiInfo = getMainWifiInfo(networkCapabilities);
+ if (mainWifiInfo != null) {
+ return mainWifiInfo;
+ }
+
+ // Only CELLULAR networks may have underlying wifi information that's relevant to SysUI,
+ // so skip the underlying network check if it's not CELLULAR.
+ if (!networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ return mainWifiInfo;
+ }
+
+ List<Network> underlyingNetworks = networkCapabilities.getUnderlyingNetworks();
+ if (underlyingNetworks == null) {
+ return null;
+ }
+
+ // Some connections, like VPN connections, may have underlying networks that are
+ // eventually traced to a wifi or carrier merged connection. So, check those underlying
+ // networks for possible wifi information as well. See b/225902574.
+ for (Network underlyingNetwork : underlyingNetworks) {
+ NetworkCapabilities underlyingNetworkCapabilities =
+ mConnectivityManager.getNetworkCapabilities(underlyingNetwork);
+ WifiInfo underlyingWifiInfo = getMainWifiInfo(underlyingNetworkCapabilities);
+ if (underlyingWifiInfo != null) {
+ return underlyingWifiInfo;
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private WifiInfo getMainWifiInfo(NetworkCapabilities networkCapabilities) {
+ boolean canHaveWifiInfo = networkCapabilities.hasTransport(TRANSPORT_WIFI)
+ || networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+ if (!canHaveWifiInfo) {
+ return null;
+ }
+
+ TransportInfo transportInfo = networkCapabilities.getTransportInfo();
+ if (transportInfo instanceof VcnTransportInfo) {
+ // This VcnTransportInfo logic is copied from
+ // [com.android.settingslib.Utils.tryGetWifiInfoForVcn]. It's copied instead of
+ // re-used because it makes the logic here clearer.
+ return ((VcnTransportInfo) transportInfo).getWifiInfo();
+ } else if (transportInfo instanceof WifiInfo) {
+ return (WifiInfo) transportInfo;
+ } else {
+ return null;
+ }
+ }
+
+ private boolean connectionIsWifi(NetworkCapabilities networkCapabilities) {
+ return connectionIsWifi(
+ networkCapabilities,
+ getMainOrUnderlyingWifiInfo(networkCapabilities));
+ }
+
+ private boolean connectionIsWifi(NetworkCapabilities networkCapabilities, WifiInfo wifiInfo) {
+ return wifiInfo != null || networkCapabilities.hasTransport(TRANSPORT_WIFI);
+ }
+
/** Refresh the status label on Locale changed. */
public void refreshLocale() {
updateStatusLabel();
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index d463170..ff3eeec 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -30,7 +30,10 @@
certificate: "platform",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
libs: [
"android.test.runner",
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index fdefcde..52c2a87 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -114,6 +114,21 @@
}
@Test
+ public void updateSetting_twiceWithSameValue_persistedValueDifferent_persistsAgain() {
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+ // This persists a different setting than what was set above. It simulates the persisted
+ // setting being changed from a different process.
+ persistSettings("0:1:1:2:2:2");
+ mNumSettingsChanges = 0;
+
+ // Updating again with the same value as in the first line of the test should persist the
+ // setting, as it is different to what is actually persisted.
+ mManager.updateSetting(/* deviceState= */ 1, /* rotationLocked= */ true);
+
+ assertThat(mNumSettingsChanges).isEqualTo(1);
+ }
+
+ @Test
public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
when(mMockResources.getStringArray(
R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.kt
new file mode 100644
index 0000000..d91c2fa
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/PosturesHelperTest.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.settingslib.devicestate
+
+import android.content.Context
+import android.content.res.Resources
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNKNOWN
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.R
+import com.google.common.truth.Expect
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+private const val DEVICE_STATE_UNKNOWN = 0
+private const val DEVICE_STATE_CLOSED = 1
+private const val DEVICE_STATE_HALF_FOLDED = 2
+private const val DEVICE_STATE_OPEN = 3
+private const val DEVICE_STATE_REAR_DISPLAY = 4
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PosturesHelperTest {
+
+ @get:Rule val expect: Expect = Expect.create()
+
+ @Mock private lateinit var context: Context
+
+ @Mock private lateinit var resources: Resources
+
+ private lateinit var posturesHelper: PosturesHelper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
+ .thenReturn(intArrayOf(DEVICE_STATE_CLOSED))
+ whenever(resources.getIntArray(R.array.config_halfFoldedDeviceStates))
+ .thenReturn(intArrayOf(DEVICE_STATE_HALF_FOLDED))
+ whenever(resources.getIntArray(R.array.config_openDeviceStates))
+ .thenReturn(intArrayOf(DEVICE_STATE_OPEN))
+ whenever(resources.getIntArray(R.array.config_rearDisplayDeviceStates))
+ .thenReturn(intArrayOf(DEVICE_STATE_REAR_DISPLAY))
+
+ posturesHelper = PosturesHelper(context)
+ }
+
+ @Test
+ fun deviceStateToPosture_mapsCorrectly() {
+ expect
+ .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_CLOSED))
+ .isEqualTo(DEVICE_STATE_ROTATION_KEY_FOLDED)
+ expect
+ .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+ .isEqualTo(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ expect
+ .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_OPEN))
+ .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+ expect
+ .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+ .isEqualTo(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+ expect
+ .that(posturesHelper.deviceStateToPosture(DEVICE_STATE_UNKNOWN))
+ .isEqualTo(DEVICE_STATE_ROTATION_KEY_UNKNOWN)
+ }
+
+ @Test
+ fun postureToDeviceState_mapsCorrectly() {
+ expect
+ .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED))
+ .isEqualTo(DEVICE_STATE_CLOSED)
+ expect
+ .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED))
+ .isEqualTo(DEVICE_STATE_HALF_FOLDED)
+ expect
+ .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED))
+ .isEqualTo(DEVICE_STATE_OPEN)
+ expect
+ .that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY))
+ .isEqualTo(DEVICE_STATE_REAR_DISPLAY)
+ expect.that(posturesHelper.postureToDeviceState(DEVICE_STATE_ROTATION_KEY_UNKNOWN)).isNull()
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
index dc7e313d..6e975cf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
@@ -40,6 +40,8 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.Arrays;
+
@RunWith(RobolectricTestRunner.class)
public class WifiStatusTrackerTest {
@Mock Context mContext;
@@ -48,13 +50,32 @@
@Mock ConnectivityManager mConnectivityManager;
@Mock Runnable mCallback;
+ private WifiStatusTracker mWifiStatusTracker;
+
private final ArgumentCaptor<ConnectivityManager.NetworkCallback>
mNetworkCallbackCaptor =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ private final ArgumentCaptor<ConnectivityManager.NetworkCallback>
+ mDefaultNetworkCallbackCaptor =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mWifiStatusTracker = new WifiStatusTracker(
+ mContext,
+ mWifiManager,
+ mNetworkScoreManager,
+ mConnectivityManager,
+ mCallback);
+ mWifiStatusTracker.setListening(true);
+
+ verify(mConnectivityManager)
+ .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any());
+ verify(mConnectivityManager)
+ .registerDefaultNetworkCallback(mDefaultNetworkCallbackCaptor.capture(), any());
}
/**
@@ -62,13 +83,6 @@
*/
@Test
public void testWifiInfoClearedOnPrimaryNetworkLost() {
- WifiStatusTracker wifiStatusTracker = new WifiStatusTracker(mContext, mWifiManager,
- mNetworkScoreManager, mConnectivityManager, mCallback);
- wifiStatusTracker.setListening(true);
-
- verify(mConnectivityManager)
- .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any());
-
// Trigger a validation callback for the primary Wifi network.
WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
when(primaryWifiInfo.makeCopy(anyLong())).thenReturn(primaryWifiInfo);
@@ -86,8 +100,8 @@
mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
// Verify primary wifi info is the one being used.
- assertThat(wifiStatusTracker.connected).isTrue();
- assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+ assertThat(mWifiStatusTracker.connected).isTrue();
+ assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
// Trigger a validation callback for the non-primary Wifi network.
WifiInfo nonPrimaryWifiInfo = Mockito.mock(WifiInfo.class);
@@ -106,20 +120,189 @@
mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(nonPrimaryNetwork, nonPrimaryCap);
// Verify primary wifi info is still the one being used.
- assertThat(wifiStatusTracker.connected).isTrue();
- assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+ assertThat(mWifiStatusTracker.connected).isTrue();
+ assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
// Lose the non-primary network.
mNetworkCallbackCaptor.getValue().onLost(nonPrimaryNetwork);
// Verify primary wifi info is still the one being used.
- assertThat(wifiStatusTracker.connected).isTrue();
- assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+ assertThat(mWifiStatusTracker.connected).isTrue();
+ assertThat(mWifiStatusTracker.rssi).isEqualTo(primaryRssi);
// Lose the primary network.
mNetworkCallbackCaptor.getValue().onLost(primaryNetwork);
// Verify we aren't connected anymore.
- assertThat(wifiStatusTracker.connected).isFalse();
+ assertThat(mWifiStatusTracker.connected).isFalse();
+ }
+
+ @Test
+ public void isCarrierMerged_typicalWifi_false() {
+ WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+ when(primaryWifiInfo.isPrimary()).thenReturn(true);
+
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)).thenReturn(true);
+ when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isCarrierMerged).isFalse();
+ }
+
+ @Test
+ public void isCarrierMerged_typicalCellular_false() {
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isCarrierMerged).isFalse();
+ }
+
+ @Test
+ public void isCarrierMerged_cellularCarrierMergedWifi_true() {
+ WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+ when(primaryWifiInfo.isPrimary()).thenReturn(true);
+ when(primaryWifiInfo.isCarrierMerged()).thenReturn(true);
+
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+ when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isCarrierMerged).isTrue();
+ }
+
+ /** Test for b/225902574. */
+ @Test
+ public void isCarrierMerged_cellularWithUnderlyingCarrierMergedWifi_true() {
+ WifiInfo underlyingCarrierMergedInfo = Mockito.mock(WifiInfo.class);
+ when(underlyingCarrierMergedInfo.isPrimary()).thenReturn(true);
+ when(underlyingCarrierMergedInfo.isCarrierMerged()).thenReturn(true);
+
+ NetworkCapabilities underlyingNetworkCapabilities = Mockito.mock(NetworkCapabilities.class);
+ when(underlyingNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+ .thenReturn(true);
+ when(underlyingNetworkCapabilities.getTransportInfo())
+ .thenReturn(underlyingCarrierMergedInfo);
+
+ Network underlyingNetwork = Mockito.mock(Network.class);
+ when(mConnectivityManager.getNetworkCapabilities(underlyingNetwork))
+ .thenReturn(underlyingNetworkCapabilities);
+
+ NetworkCapabilities mainCapabilities = Mockito.mock(NetworkCapabilities.class);
+ when(mainCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+ .thenReturn(true);
+ when(mainCapabilities.getTransportInfo()).thenReturn(null);
+ when(mainCapabilities.getUnderlyingNetworks())
+ .thenReturn(Arrays.asList(underlyingNetwork));
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, mainCapabilities);
+
+ assertThat(mWifiStatusTracker.isCarrierMerged).isTrue();
+ }
+
+ @Test
+ public void isDefaultNetwork_typicalWifi_true() {
+ WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+ when(primaryWifiInfo.isPrimary()).thenReturn(true);
+
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)).thenReturn(true);
+ when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
+ }
+
+ @Test
+ public void isDefaultNetwork_typicalCellular_false() {
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isDefaultNetwork).isFalse();
+ }
+
+ @Test
+ public void isDefaultNetwork_cellularCarrierMergedWifi_true() {
+ WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+ when(primaryWifiInfo.isPrimary()).thenReturn(true);
+ when(primaryWifiInfo.isCarrierMerged()).thenReturn(true);
+
+ NetworkCapabilities primaryCap = Mockito.mock(NetworkCapabilities.class);
+ when(primaryCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
+ when(primaryCap.getTransportInfo()).thenReturn(primaryWifiInfo);
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mDefaultNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+ assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
+ }
+
+ /** Test for b/225902574. */
+ @Test
+ public void isDefaultNetwork_cellularWithUnderlyingCarrierMergedWifi_true() {
+ WifiInfo underlyingCarrierMergedInfo = Mockito.mock(WifiInfo.class);
+ when(underlyingCarrierMergedInfo.isPrimary()).thenReturn(true);
+ when(underlyingCarrierMergedInfo.isCarrierMerged()).thenReturn(true);
+
+ NetworkCapabilities underlyingNetworkCapabilities = Mockito.mock(NetworkCapabilities.class);
+ when(underlyingNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+ .thenReturn(true);
+ when(underlyingNetworkCapabilities.getTransportInfo())
+ .thenReturn(underlyingCarrierMergedInfo);
+
+ Network underlyingNetwork = Mockito.mock(Network.class);
+ when(mConnectivityManager.getNetworkCapabilities(underlyingNetwork))
+ .thenReturn(underlyingNetworkCapabilities);
+
+ NetworkCapabilities mainCapabilities = Mockito.mock(NetworkCapabilities.class);
+ when(mainCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR))
+ .thenReturn(true);
+ when(mainCapabilities.getTransportInfo()).thenReturn(null);
+ when(mainCapabilities.getUnderlyingNetworks())
+ .thenReturn(Arrays.asList(underlyingNetwork));
+
+ Network primaryNetwork = Mockito.mock(Network.class);
+ int primaryNetworkId = 1;
+ when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+
+ mDefaultNetworkCallbackCaptor.getValue()
+ .onCapabilitiesChanged(primaryNetwork, mainCapabilities);
+
+ assertThat(mWifiStatusTracker.isDefaultNetwork).isTrue();
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index e4cc9f1..6a5535d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -100,6 +100,5 @@
Settings.System.CAMERA_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
- Settings.System.SMOOTH_DISPLAY
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4b72063..85623b2 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -226,6 +226,5 @@
VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(System.SMOOTH_DISPLAY, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index d1bd5e6..46b45d1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -34,7 +34,6 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM;
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
import static com.android.providers.settings.SettingsState.getTypeFromKey;
import static com.android.providers.settings.SettingsState.getUserIdFromKey;
@@ -5674,7 +5673,7 @@
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
try {
final String storedValue = resources.getString(resourceId);
@@ -5683,7 +5682,7 @@
}
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default Cred Provider not found: " + e.toString());
+ "Get default Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5732,8 +5731,8 @@
final Setting currentSetting = secureSettings
.getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
if (currentSetting.isNull()) {
- final int resourceId = com.android.internal.R.array
- .config_defaultCredentialProviderService;
+ final int resourceId =
+ com.android.internal.R.array.config_defaultCredentialProviderService;
final Resources resources = getContext().getResources();
// If the config has not be defined we might get an exception.
final List<String> providers = new ArrayList<>();
@@ -5741,7 +5740,7 @@
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5840,44 +5839,12 @@
currentVersion = 218;
}
- // v218: Convert Smooth Display and Force Peak Refresh Rate to a boolean
if (currentVersion == 218) {
- final String peakRefreshRateSettingName = "peak_refresh_rate";
- final String minRefreshRateSettingName = "min_refresh_rate";
-
- final SettingsState systemSettings = getSystemSettingsLocked(userId);
- final Setting peakRefreshRateSetting =
- systemSettings.getSettingLocked(peakRefreshRateSettingName);
- final Setting minRefreshRateSetting =
- systemSettings.getSettingLocked(minRefreshRateSettingName);
-
- float peakRefreshRate = DEFAULT_REFRESH_RATE;
- float minRefreshRate = 0;
- try {
- if (!peakRefreshRateSetting.isNull()) {
- peakRefreshRate = Float.parseFloat(peakRefreshRateSetting.getValue());
- }
- } catch (NumberFormatException e) {
- // Do nothing. Overwrite with default value.
- }
- try {
- if (!minRefreshRateSetting.isNull()) {
- minRefreshRate = Float.parseFloat(minRefreshRateSetting.getValue());
- }
- } catch (NumberFormatException e) {
- // Do nothing. Overwrite with default value.
- }
-
- systemSettings.deleteSettingLocked(peakRefreshRateSettingName);
- systemSettings.deleteSettingLocked(minRefreshRateSettingName);
-
- systemSettings.insertSettingLocked(Settings.System.SMOOTH_DISPLAY,
- peakRefreshRate > DEFAULT_REFRESH_RATE ? "1" : "0", /* tag= */ null,
- /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
- systemSettings.insertSettingLocked(Settings.System.FORCE_PEAK_REFRESH_RATE,
- minRefreshRate > 0 ? "1" : "0", /* tag= */ null,
- /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
-
+ // Version 219: Removed
+ // TODO(b/211737588): Back up the Smooth Display setting
+ // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings
+ // should account for the database in a non-upgraded and upgraded (change id:
+ // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state.
currentVersion = 219;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 36aa2ac..706666c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -97,7 +97,8 @@
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
Settings.System.DESKTOP_MODE, // developer setting for internal prototyping
- Settings.System.FORCE_PEAK_REFRESH_RATE, // depends on hardware capabilities
+ Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
+ Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a5908a8..e6bbf97 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -173,7 +173,6 @@
"androidx.palette_palette",
"androidx.legacy_legacy-preference-v14",
"androidx.leanback_leanback",
- "androidx.tracing_tracing",
"androidx.slice_slice-core",
"androidx.slice_slice-view",
"androidx.slice_slice-builders",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4652ef1..c8eb4b4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -695,7 +695,9 @@
android:relinquishTaskIdentity="true"
android:configChanges=
"screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
- android:visibleToInstantApps="true"/>
+ android:visibleToInstantApps="true"
+ android:exported="false"
+ android:permission="android.permission.MANAGE_MEDIA_PROJECTION"/>
<!-- started from TvNotificationPanel -->
<activity
@@ -966,22 +968,6 @@
android:permission="android.permission.BIND_JOB_SERVICE"/>
<!-- region Note Task -->
- <activity
- android:name=".notetask.shortcut.CreateNoteTaskShortcutActivity"
- android:enabled="false"
- android:exported="true"
- android:excludeFromRecents="true"
- android:resizeableActivity="false"
- android:theme="@android:style/Theme.NoDisplay"
- android:label="@string/note_task_button_label"
- android:icon="@drawable/ic_note_task_shortcut_widget">
-
- <intent-filter>
- <action android:name="android.intent.action.CREATE_SHORTCUT" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
<service android:name=".notetask.NoteTaskControllerUpdateService" />
<activity
@@ -1006,6 +992,18 @@
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" />
+
+ <activity
+ android:name=".notetask.LaunchNotesRoleSettingsTrampolineActivity"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:resizeableActivity="false"
+ android:theme="@android:style/Theme.NoDisplay" >
+ <intent-filter>
+ <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<!-- endregion -->
<!-- started from ControlsRequestReceiver -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 77ddc6e..1ce3472 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -55,6 +55,7 @@
madym@google.com
mankoff@google.com
mateuszc@google.com
+mgalhardo@google.com
michaelmikhil@google.com
michschn@google.com
mkephart@google.com
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 9d1dd1b..3a19990 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -22,12 +22,11 @@
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
-import android.graphics.Rect
import android.text.Layout
import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
-import android.util.MathUtils
+import android.util.MathUtils.constrainedMap
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
@@ -40,8 +39,6 @@
import java.util.Calendar
import java.util.Locale
import java.util.TimeZone
-import kotlin.math.max
-import kotlin.math.min
/**
* Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
@@ -478,122 +475,56 @@
pw.println(" time=$time")
}
- fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) {
- // Do we need to cancel an in-flight animation?
- // Need to also check against 0.0f here; we can sometimes get two calls with fraction == 0,
- // which trips up the check otherwise.
- if (lastSeenAnimationProgress != 1.0f &&
- lastSeenAnimationProgress != 0.0f &&
- fraction == 0.0f) {
- // New animation, but need to stop the old one. Figure out where each glyph currently
- // is in relation to the box position. After that, use the leading digit's current
- // position as the stop target.
- currentAnimationNeededStop = true
+ private val moveToCenterDelays
+ get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS
- // We assume that the current glyph offsets would be relative to the "from" position.
- val moveAmount = toRect.left - fromRect.left
+ private val moveToSideDelays
+ get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS
- // Remap the current glyph offsets to be relative to the new "end" position, and figure
- // out the start/end positions for the stop animation.
- for (i in 0 until NUM_DIGITS) {
- glyphOffsets[i] = -moveAmount + glyphOffsets[i]
- animationCancelStartPosition[i] = glyphOffsets[i]
- }
-
- // Use the leading digit's offset as the stop position.
- if (toRect.left > fromRect.left) {
- // It _was_ moving left
- animationCancelStopPosition = glyphOffsets[0]
- } else {
- // It was moving right
- animationCancelStopPosition = glyphOffsets[1]
- }
- }
-
- // Is there a cancellation in progress?
- if (currentAnimationNeededStop && fraction < ANIMATION_CANCELLATION_TIME) {
- val animationStopProgress = MathUtils.constrainedMap(
- 0.0f, 1.0f, 0.0f, ANIMATION_CANCELLATION_TIME, fraction
- )
-
- // One of the digits has already stopped.
- val animationStopStep = 1.0f / (NUM_DIGITS - 1)
-
- for (i in 0 until NUM_DIGITS) {
- val stopAmount = if (toRect.left > fromRect.left) {
- // It was moving left (before flipping)
- MOVE_LEFT_DELAYS[i] * animationStopStep
- } else {
- // It was moving right (before flipping)
- MOVE_RIGHT_DELAYS[i] * animationStopStep
- }
-
- // Leading digit stops immediately.
- if (stopAmount == 0.0f) {
- glyphOffsets[i] = animationCancelStopPosition
- } else {
- val actualStopAmount = MathUtils.constrainedMap(
- 0.0f, 1.0f, 0.0f, stopAmount, animationStopProgress
+ /**
+ * Offsets the glyphs of the clock for the step clock animation.
+ *
+ * The animation makes the glyphs of the clock move at different speeds, when the clock is
+ * moving horizontally.
+ *
+ * @param clockStartLeft the [getLeft] position of the clock, before it started moving.
+ * @param clockMoveDirection the direction in which it is moving. A positive number means right,
+ * and negative means left.
+ * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1
+ * means it finished moving.
+ */
+ fun offsetGlyphsForStepClockAnimation(
+ clockStartLeft: Int,
+ clockMoveDirection: Int,
+ moveFraction: Float
+ ) {
+ val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0
+ val currentMoveAmount = left - clockStartLeft
+ val digitOffsetDirection = if (isLayoutRtl) -1 else 1
+ for (i in 0 until NUM_DIGITS) {
+ // The delay for the digit, in terms of fraction (i.e. the digit should not move
+ // during 0.0 - 0.1).
+ val digitInitialDelay =
+ if (isMovingToCenter) {
+ moveToCenterDelays[i] * MOVE_DIGIT_STEP
+ } else {
+ moveToSideDelays[i] * MOVE_DIGIT_STEP
+ }
+ val digitFraction =
+ MOVE_INTERPOLATOR.getInterpolation(
+ constrainedMap(
+ 0.0f,
+ 1.0f,
+ digitInitialDelay,
+ digitInitialDelay + AVAILABLE_ANIMATION_TIME,
+ moveFraction
+ )
)
- val easedProgress = MOVE_INTERPOLATOR.getInterpolation(actualStopAmount)
- val glyphMoveAmount =
- animationCancelStopPosition - animationCancelStartPosition[i]
- glyphOffsets[i] =
- animationCancelStartPosition[i] + glyphMoveAmount * easedProgress
- }
- }
- } else {
- // Normal part of the animation.
- // Do we need to remap the animation progress to take account of the cancellation?
- val actualFraction = if (currentAnimationNeededStop) {
- MathUtils.constrainedMap(
- 0.0f, 1.0f, ANIMATION_CANCELLATION_TIME, 1.0f, fraction
- )
- } else {
- fraction
- }
-
- val digitFractions = (0 until NUM_DIGITS).map {
- // The delay for each digit, in terms of fraction (i.e. the digit should not move
- // during 0.0 - 0.1).
- val initialDelay = if (toRect.left > fromRect.left) {
- MOVE_RIGHT_DELAYS[it] * MOVE_DIGIT_STEP
- } else {
- MOVE_LEFT_DELAYS[it] * MOVE_DIGIT_STEP
- }
-
- val f = MathUtils.constrainedMap(
- 0.0f, 1.0f,
- initialDelay, initialDelay + AVAILABLE_ANIMATION_TIME,
- actualFraction
- )
- MOVE_INTERPOLATOR.getInterpolation(max(min(f, 1.0f), 0.0f))
- }
-
- // Was there an animation halt?
- val moveAmount = if (currentAnimationNeededStop) {
- // Only need to animate over the remaining space if the animation was aborted.
- -animationCancelStopPosition
- } else {
- toRect.left.toFloat() - fromRect.left.toFloat()
- }
-
- for (i in 0 until NUM_DIGITS) {
- glyphOffsets[i] = -moveAmount + (moveAmount * digitFractions[i])
- }
+ val moveAmountForDigit = currentMoveAmount * digitFraction
+ val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
+ glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit
}
-
invalidate()
-
- if (fraction == 1.0f) {
- // Reset
- currentAnimationNeededStop = false
- }
-
- lastSeenAnimationProgress = fraction
-
- // Ensure that the actual clock container is always in the "end" position.
- this.setLeftTopRightBottom(toRect.left, toRect.top, toRect.right, toRect.bottom)
}
// DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
@@ -647,9 +578,6 @@
private const val NUM_DIGITS = 4
private const val DIGITS_PER_LINE = 2
- // How much of "fraction" to spend on canceling the animation, if needed
- private const val ANIMATION_CANCELLATION_TIME = 0f
-
// Delays. Each digit's animation should have a slight delay, so we get a nice
// "stepping" effect. When moving right, the second digit of the hour should move first.
// When moving left, the first digit of the hour should move first. The lists encode
@@ -668,6 +596,6 @@
// Total available transition time for each digit, taking into account the step. If step is
// 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
- private val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)
+ private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index f57432c..b0c0240 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -151,6 +151,7 @@
{ str1 = id },
{ "Clock Id conflict on load: $str1 is double registered" }
)
+ manager.unloadPlugin()
continue
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 3fda83d..2cc3600 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -189,8 +189,9 @@
view.setLayoutParams(lp)
}
- fun moveForSplitShade(fromRect: Rect, toRect: Rect, fraction: Float) {
- view.moveForSplitShade(fromRect, toRect, fraction)
+ /** See documentation at [AnimatableClockView.offsetGlyphsForStepClockAnimation]. */
+ fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) {
+ view.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
}
}
@@ -277,8 +278,8 @@
dozeFraction: Float,
foldFraction: Float,
) : DefaultClockAnimations(view, dozeFraction, foldFraction) {
- override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
- largeClock.moveForSplitShade(fromRect, toRect, fraction)
+ override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
+ largeClock.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
}
}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java
index e6b2c2f..e839d9b 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/DynamicColor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -71,12 +71,11 @@
* The base constructor for DynamicColor.
*
* <p>Functional arguments allow overriding without risks that come with subclasses. _Strongly_
- * prefer using one of the static convenience constructors. This class is arguably too flexible
- * to
+ * prefer using one of the static convenience constructors. This class is arguably too
+ * flexible to
* ensure it can support any scenario.
*
- * <p>For example, the default behavior of adjust tone at max contrast to be at a 7.0 ratio
- * with
+ * <p>For example, the default behavior of adjust tone at max contrast to be at a 7.0 ratio with
* its background is principled and matches a11y guidance. That does not mean it's the desired
* approach for _every_ design system, and every color pairing, always, in every case.
*
@@ -89,23 +88,23 @@
* lower and raise contrast are
* made.
* @param toneMinContrast given DynamicScheme, return tone in HCT/L* in L*a*b* this color
- * should
- * be at minimum contrast. See toneMinContrastDefault for the default
- * behavior, and strongly
+ * should
+ * be at minimum contrast. See toneMinContrastDefault for the
+ * default behavior, and strongly
* consider using it unless you have strong opinions on a11y. The
* static constructors use it.
* @param toneMaxContrast given DynamicScheme, return tone in HCT/L* in L*a*b* this color
- * should
- * be at maximum contrast. See toneMaxContrastDefault for the default
- * behavior, and strongly
+ * should
+ * be at maximum contrast. See toneMaxContrastDefault for the
+ * default behavior, and strongly
* consider using it unless you have strong opinions on a11y. The
* static constructors use it.
* @param toneDeltaConstraint given DynamicScheme, return a ToneDeltaConstraint instance that
* describes a requirement that this DynamicColor must always have
* some difference in tone/L*
* from another DynamicColor.<br>
- * Unlikely to be useful unless a design system has some distortions
- * where colors that don't
+ * Unlikely to be useful unless a design system has some
+ * distortions where colors that don't
* have a background/foreground relationship must have _some_
* difference in tone, yet, not
* enough difference to create meaningful contrast.
@@ -164,8 +163,8 @@
* <p>If the design system uses the same hex code on multiple backgrounds, define that in
* multiple
* DynamicColors so that the background is accurate for each one. If you define a DynamicColor
- * with one background, and actually use it on another, DynamicColor can't guarantee contrast.
- * For
+ * with one background, and actually use it on another, DynamicColor can't guarantee contrast
+ * . For
* example, if you use a color on both black and white, increasing the contrast on one
* necessarily
* decreases contrast of the other.
@@ -195,8 +194,8 @@
* for contrast, given a background, colors can adjust to
* increase/decrease contrast.
* @param toneDeltaConstraint Function that provides a ToneDeltaConstraint given DynamicScheme.
- * Useful for ensuring lightness difference between colors that don't
- * _require_ contrast or
+ * Useful for ensuring lightness difference between colors that
+ * don't _require_ contrast or
* have a formal background/foreground relationship.
*/
public static DynamicColor fromArgb(
@@ -212,17 +211,17 @@
* Create a DynamicColor.
*
* @param palette Function that provides a TonalPalette given DynamicScheme. A TonalPalette is
- * defined by a hue and chroma, so this replaces the need to specify hue/chroma.
- * By providing
+ * defined by a hue and chroma, so this replaces the need to specify
+ * hue/chroma. By providing
* a tonal palette, when contrast adjustments are made, intended chroma can be
* preserved. For
* example, at T/L* 90, there is a significant limit to the amount of chroma.
* There is no
- * colorful red, a red that light is pink. By preserving the _intended_ chroma if
- * lightness
+ * colorful red, a red that light is pink. By preserving the _intended_ chroma
+ * if lightness
* lowers for contrast adjustments, the intended chroma is restored.
- * @param tone Function that provides a tone given DynamicScheme. (useful for dark vs. light
- * mode)
+ * @param tone Function that provides a tone given DynamicScheme. (useful for dark vs.
+ * light mode)
*/
public static DynamicColor fromPalette(
Function<DynamicScheme, TonalPalette> palette, Function<DynamicScheme, Double> tone) {
@@ -232,16 +231,16 @@
/**
* Create a DynamicColor.
*
- * @param palette Function that provides a TonalPalette given DynamicScheme. A TonalPalette
- * is
+ * @param palette Function that provides a TonalPalette given DynamicScheme. A
+ * TonalPalette is
* defined by a hue and chroma, so this replaces the need to specify
* hue/chroma. By providing
- * a tonal palette, when contrast adjustments are made, intended chroma can be
- * preserved. For
- * example, at T/L* 90, there is a significant limit to the amount of chroma.
- * There is no
- * colorful red, a red that light is pink. By preserving the _intended_ chroma
- * if lightness
+ * a tonal palette, when contrast adjustments are made, intended chroma can
+ * be preserved. For
+ * example, at T/L* 90, there is a significant limit to the amount of
+ * chroma. There is no
+ * colorful red, a red that light is pink. By preserving the _intended_
+ * chroma if lightness
* lowers for contrast adjustments, the intended chroma is restored.
* @param tone Function that provides a tone given DynamicScheme. (useful for dark vs.
* light mode)
@@ -261,12 +260,12 @@
*
* @param palette Function that provides a TonalPalette given DynamicScheme. A
* TonalPalette is
- * defined by a hue and chroma, so this replaces the need to specify
- * hue/chroma. By providing
+ * defined by a hue and chroma, so this replaces the need to
+ * specify hue/chroma. By providing
* a tonal palette, when contrast adjustments are made, intended
* chroma can be preserved. For
- * example, at T/L* 90, there is a significant limit to the amount of
- * chroma. There is no
+ * example, at T/L* 90, there is a significant limit to the amount
+ * of chroma. There is no
* colorful red, a red that light is pink. By preserving the
* _intended_ chroma if lightness
* lowers for contrast adjustments, the intended chroma is restored.
@@ -277,8 +276,8 @@
* for contrast, given a background, colors can adjust to
* increase/decrease contrast.
* @param toneDeltaConstraint Function that provides a ToneDeltaConstraint given DynamicScheme.
- * Useful for ensuring lightness difference between colors that don't
- * _require_ contrast or
+ * Useful for ensuring lightness difference between colors that
+ * don't _require_ contrast or
* have a formal background/foreground relationship.
*/
public static DynamicColor fromPalette(
@@ -297,96 +296,6 @@
toneDeltaConstraint);
}
- /** Returns the ARGB (i.e. hex code) representation of the resolved color given scheme. */
- public int getArgb(DynamicScheme scheme) {
- final int argb = getHct(scheme).toInt();
- if (opacity == null) {
- return argb;
- }
- final double percentage = opacity.apply(scheme);
- final int alpha = MathUtils.clampInt(0, 255, (int) Math.round(percentage * 255));
- return (argb & 0x00ffffff) | (alpha << 24);
- }
-
- /** Returns the HCT representation of the resolved color given scheme. */
- public Hct getHct(DynamicScheme scheme) {
- final Hct cachedAnswer = hctCache.get(scheme);
- if (cachedAnswer != null) {
- return cachedAnswer;
- }
- // This is crucial for aesthetics: we aren't simply the taking the standard color
- // and changing its tone for contrast. Rather, we find the tone for contrast, then
- // use the specified chroma from the palette to construct a new color.
- //
- // For example, this enables colors with standard tone of T90, which has limited chroma, to
- // "recover" intended chroma as contrast increases.
- final Hct answer = Hct.from(hue.apply(scheme), chroma.apply(scheme), getTone(scheme));
- // NOMUTANTS--trivial test with onerous dependency injection requirement.
- if (hctCache.size() > 4) {
- hctCache.clear();
- }
- // NOMUTANTS--trivial test with onerous dependency injection requirement.
- hctCache.put(scheme, answer);
- return answer;
- }
-
- /** Returns the tone in HCT, ranging from 0 to 100, of the resolved color given scheme. */
- public double getTone(DynamicScheme scheme) {
- double answer = tone.apply(scheme);
-
- final boolean decreasingContrast = scheme.contrastLevel < 0.0;
- if (scheme.contrastLevel != 0.0) {
- final double startTone = tone.apply(scheme);
- final double endTone =
- decreasingContrast ? toneMinContrast.apply(scheme) : toneMaxContrast.apply(
- scheme);
- final double delta = (endTone - startTone) * Math.abs(scheme.contrastLevel);
- answer = delta + startTone;
- }
-
- final DynamicColor bgDynamicColor = background == null ? null : background.apply(scheme);
- double minRatio = Contrast.RATIO_MIN;
- double maxRatio = Contrast.RATIO_MAX;
- if (bgDynamicColor != null) {
- final boolean bgHasBg =
- bgDynamicColor.background != null && bgDynamicColor.background.apply(scheme)
- != null;
- final double standardRatio =
- Contrast.ratioOfTones(tone.apply(scheme), bgDynamicColor.tone.apply(scheme));
- if (decreasingContrast) {
- final double minContrastRatio =
- Contrast.ratioOfTones(
- toneMinContrast.apply(scheme),
- bgDynamicColor.toneMinContrast.apply(scheme));
- minRatio = bgHasBg ? minContrastRatio : 1.0;
- maxRatio = standardRatio;
- } else {
- final double maxContrastRatio =
- Contrast.ratioOfTones(
- toneMaxContrast.apply(scheme),
- bgDynamicColor.toneMaxContrast.apply(scheme));
- minRatio = bgHasBg ? min(maxContrastRatio, standardRatio) : 1.0;
- maxRatio = bgHasBg ? max(maxContrastRatio, standardRatio) : 21.0;
- }
- }
-
- final double finalMinRatio = minRatio;
- final double finalMaxRatio = maxRatio;
- final double finalAnswer = answer;
- answer =
- calculateDynamicTone(
- scheme,
- this.tone,
- (dynamicColor) -> dynamicColor.getTone(scheme),
- (a, b) -> finalAnswer,
- (s) -> bgDynamicColor,
- toneDeltaConstraint,
- (s) -> finalMinRatio,
- (s) -> finalMaxRatio);
-
- return answer;
- }
-
/**
* The default algorithm for calculating the tone of a color at minimum contrast.<br>
* If the original contrast ratio was >= 7.0, reach contrast 4.5.<br>
@@ -475,8 +384,8 @@
* <p>It enforces important properties:<br>
* #1. Desired contrast ratio is reached.<br>
* As contrast increases from standard to max, the tones involved should always be at least the
- * standard ratio. For example, if a button is T90, and button text is T0, and the button is T0
- * at
+ * standard ratio. For example, if a button is T90, and button text is T0, and the button is
+ * T0 at
* max contrast, the button text cannot simply linearly interpolate from T0 to T100, or at some
* point they'll both be at the same tone.
*
@@ -489,8 +398,7 @@
*
* <p>#3. Ensure tone delta with another color.<br>
* In design systems, there may be colors that don't have a pure background/foreground
- * relationship, but, do require different tones for visual differentiation.
- * ToneDeltaConstraint
+ * relationship, but, do require different tones for visual differentiation. ToneDeltaConstraint
* models this requirement, and DynamicColor enforces it.
*/
public static double calculateDynamicTone(
@@ -596,13 +504,16 @@
final boolean preferLighter = tonePrefersLightForeground(bgTone);
if (preferLighter) {
- // "Negligible difference" handles an edge case where the initial contrast ratio is high
+ // "Neglible difference" handles an edge case where the initial contrast ratio is high
// (ex. 13.0), and the ratio passed to the function is that high ratio, and both the
- // lighter and darker ratio fails to pass that ratio.
+ // lighter
+ // and darker ratio fails to pass that ratio.
//
// This was observed with Tonal Spot's On Primary Container turning black momentarily
- // between high and max contrast in light mode. PC's standard tone was T90, OPC's was
- // T10, it was light mode, and the contrast level was 0.6568521221032331.
+ // between
+ // high and max contrast in light mode. PC's standard tone was T90, OPC's was T10, it
+ // was
+ // light mode, and the contrast level was 0.6568521221032331.
final boolean negligibleDifference =
Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio
&& darkerRatio < ratio;
@@ -634,13 +545,109 @@
* <p>T60 used as to create the smallest discontinuity possible when skipping down to T49 in
* order
* to ensure light foregrounds.
+ *
+ * <p>Since `tertiaryContainer` in dark monochrome scheme requires a tone of 60, it should
+ * not be
+ * adjusted. Therefore, 60 is excluded here.
*/
public static boolean tonePrefersLightForeground(double tone) {
- return Math.round(tone) <= 60;
+ return Math.round(tone) < 60;
}
- /** Tones less than ~T50 always permit white at 4.5 contrast. */
+ /**
+ * Tones less than ~T50 always permit white at 4.5 contrast.
+ */
public static boolean toneAllowsLightForeground(double tone) {
return Math.round(tone) <= 49;
}
+
+ public int getArgb(DynamicScheme scheme) {
+ final int argb = getHct(scheme).toInt();
+ if (opacity == null) {
+ return argb;
+ }
+ final double percentage = opacity.apply(scheme);
+ final int alpha = MathUtils.clampInt(0, 255, (int) Math.round(percentage * 255));
+ return (argb & 0x00ffffff) | (alpha << 24);
+ }
+
+ public Hct getHct(DynamicScheme scheme) {
+ final Hct cachedAnswer = hctCache.get(scheme);
+ if (cachedAnswer != null) {
+ return cachedAnswer;
+ }
+ // This is crucial for aesthetics: we aren't simply the taking the standard color
+ // and changing its tone for contrast. Rather, we find the tone for contrast, then
+ // use the specified chroma from the palette to construct a new color.
+ //
+ // For example, this enables colors with standard tone of T90, which has limited chroma, to
+ // "recover" intended chroma as contrast increases.
+ final Hct answer = Hct.from(hue.apply(scheme), chroma.apply(scheme), getTone(scheme));
+ // NOMUTANTS--trivial test with onerous dependency injection requirement.
+ if (hctCache.size() > 4) {
+ hctCache.clear();
+ }
+ // NOMUTANTS--trivial test with onerous dependency injection requirement.
+ hctCache.put(scheme, answer);
+ return answer;
+ }
+
+ /**
+ * Returns the tone in HCT, ranging from 0 to 100, of the resolved color given scheme.
+ */
+ public double getTone(DynamicScheme scheme) {
+ double answer = tone.apply(scheme);
+
+ final boolean decreasingContrast = scheme.contrastLevel < 0.0;
+ if (scheme.contrastLevel != 0.0) {
+ final double startTone = tone.apply(scheme);
+ final double endTone =
+ decreasingContrast ? toneMinContrast.apply(scheme) : toneMaxContrast.apply(
+ scheme);
+ final double delta = (endTone - startTone) * Math.abs(scheme.contrastLevel);
+ answer = delta + startTone;
+ }
+
+ final DynamicColor bgDynamicColor = background == null ? null : background.apply(scheme);
+ double minRatio = Contrast.RATIO_MIN;
+ double maxRatio = Contrast.RATIO_MAX;
+ if (bgDynamicColor != null) {
+ final boolean bgHasBg =
+ bgDynamicColor.background != null && bgDynamicColor.background.apply(scheme)
+ != null;
+ final double standardRatio =
+ Contrast.ratioOfTones(tone.apply(scheme), bgDynamicColor.tone.apply(scheme));
+ if (decreasingContrast) {
+ final double minContrastRatio =
+ Contrast.ratioOfTones(
+ toneMinContrast.apply(scheme),
+ bgDynamicColor.toneMinContrast.apply(scheme));
+ minRatio = bgHasBg ? minContrastRatio : 1.0;
+ maxRatio = standardRatio;
+ } else {
+ final double maxContrastRatio =
+ Contrast.ratioOfTones(
+ toneMaxContrast.apply(scheme),
+ bgDynamicColor.toneMaxContrast.apply(scheme));
+ minRatio = bgHasBg ? min(maxContrastRatio, standardRatio) : 1.0;
+ maxRatio = bgHasBg ? max(maxContrastRatio, standardRatio) : 21.0;
+ }
+ }
+
+ final double finalMinRatio = minRatio;
+ final double finalMaxRatio = maxRatio;
+ final double finalAnswer = answer;
+ answer =
+ calculateDynamicTone(
+ scheme,
+ this.tone,
+ (dynamicColor) -> dynamicColor.getTone(scheme),
+ (a, b) -> finalAnswer,
+ (s) -> bgDynamicColor,
+ toneDeltaConstraint,
+ (s) -> finalMinRatio,
+ (s) -> finalMaxRatio);
+
+ return answer;
+ }
}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
index 5212e8e..21218a2 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
@@ -22,7 +22,7 @@
import com.android.systemui.monet.scheme.DynamicScheme;
import com.android.systemui.monet.scheme.Variant;
-/** Named colors, otherwise known as tokens, or roles, in the Material Design system. */
+/** Named colors, otherwise known as tokens, or roles, in the Material Design system.*/
// Prevent lint for Function.apply not being available on Android before API level 14 (4.0.1).
// "AndroidJdkLibsChecker" for Function, "NewApi" for Function.apply().
// A java_library Bazel rule with an Android constraint cannot skip these warnings without this
@@ -33,341 +33,54 @@
private static final double CONTAINER_ACCENT_TONE_DELTA = 15.0;
- private MaterialDynamicColors() {
+ public MaterialDynamicColors() {
}
- public static DynamicColor highestSurface(DynamicScheme s) {
- return s.isDark ? surfaceBright : surfaceDim;
- }
-
- public static final DynamicColor background =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
-
- public static final DynamicColor onBackground =
- DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background);
-
- public static final DynamicColor surface =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
-
- public static final DynamicColor surfaceInverse =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 30.0);
-
- public static final DynamicColor surfaceBright =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0);
-
- public static final DynamicColor surfaceDim =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0);
-
- public static final DynamicColor surfaceSub2 =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0);
-
- public static final DynamicColor surfaceSub1 =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0);
-
- public static final DynamicColor surfaceContainer =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0);
-
- public static final DynamicColor surfaceAdd1 =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0);
-
- public static final DynamicColor surfaceAdd2 =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0);
-
- public static final DynamicColor onSurface =
- DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor onSurfaceInverse =
- DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> surfaceInverse);
-
- public static final DynamicColor surfaceVariant =
- DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 90.0);
-
- public static final DynamicColor onSurfaceVariant =
- DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0,
- (s) -> surfaceVariant);
-
- public static final DynamicColor outline =
- DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> 50.0, (s) -> highestSurface(s));
-
- public static final DynamicColor outlineVariant =
- DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor primaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 30.0 : 90.0;
- }
- return performAlbers(s.sourceColorHct, s);
- },
- (s) -> highestSurface(s));
-
- public static final DynamicColor onPrimaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 90.0 : 10.0;
- }
- return DynamicColor.contrastingTone(primaryContainer.tone.apply(s), 4.5);
- },
- (s) -> primaryContainer,
- null);
-
- public static final DynamicColor primary =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- (s) -> highestSurface(s),
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- primaryContainer,
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
-
- public static final DynamicColor primaryInverse =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> surfaceInverse);
-
- public static final DynamicColor onPrimary =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> primary);
-
- public static final DynamicColor secondaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> {
- final double initialTone = s.isDark ? 30.0 : 90.0;
- if (!isFidelity(s)) {
- return initialTone;
- }
- double answer =
- findDesiredChromaByTone(
- s.secondaryPalette.getHue(),
- s.secondaryPalette.getChroma(),
- initialTone,
- !s.isDark);
- answer = performAlbers(s.secondaryPalette.getHct(answer), s);
- return answer;
- },
- (s) -> highestSurface(s));
-
- public static final DynamicColor onSecondaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 90.0 : 10.0;
- }
- return DynamicColor.contrastingTone(secondaryContainer.tone.apply(s), 4.5);
- },
- (s) -> secondaryContainer);
-
- public static final DynamicColor secondary =
- DynamicColor.fromPalette(
- (s) -> s.secondaryPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- (s) -> highestSurface(s),
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- secondaryContainer,
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
-
- public static final DynamicColor onSecondary =
- DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> secondary);
-
- public static final DynamicColor tertiaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 30.0 : 90.0;
- }
- final double albersTone =
- performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()),
- s);
- final Hct proposedHct = s.tertiaryPalette.getHct(albersTone);
- return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone();
- },
- (s) -> highestSurface(s));
-
- public static final DynamicColor onTertiaryContainer =
- DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> {
- if (!isFidelity(s)) {
- return s.isDark ? 90.0 : 10.0;
- }
- return DynamicColor.contrastingTone(tertiaryContainer.tone.apply(s), 4.5);
- },
- (s) -> tertiaryContainer);
-
- public static final DynamicColor tertiary =
- DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- (s) -> highestSurface(s),
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- tertiaryContainer,
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
-
- public static final DynamicColor onTertiary =
- DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> tertiary);
-
- public static final DynamicColor errorContainer =
- DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0, (s) -> highestSurface(s));
-
- public static final DynamicColor onErrorContainer =
- DynamicColor.fromPalette(
- (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer);
-
- public static final DynamicColor error =
- DynamicColor.fromPalette(
- (s) -> s.errorPalette,
- (s) -> s.isDark ? 80.0 : 40.0,
- (s) -> highestSurface(s),
- (s) ->
- new ToneDeltaConstraint(
- CONTAINER_ACCENT_TONE_DELTA,
- errorContainer,
- s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
-
- public static final DynamicColor onError =
- DynamicColor.fromPalette((s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0,
- (s) -> error);
-
- public static final DynamicColor primaryFixed =
- DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 90.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor primaryFixedDarker =
- DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 80.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor onPrimaryFixed =
- DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 10.0,
- (s) -> primaryFixedDarker);
-
- public static final DynamicColor onPrimaryFixedVariant =
- DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> 30.0,
- (s) -> primaryFixedDarker);
-
- public static final DynamicColor secondaryFixed =
- DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 90.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor secondaryFixedDarker =
- DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 80.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor onSecondaryFixed =
- DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 10.0,
- (s) -> secondaryFixedDarker);
-
- public static final DynamicColor onSecondaryFixedVariant =
- DynamicColor.fromPalette((s) -> s.secondaryPalette, (s) -> 30.0,
- (s) -> secondaryFixedDarker);
-
- public static final DynamicColor tertiaryFixed =
- DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 90.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor tertiaryFixedDarker =
- DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 80.0,
- (s) -> highestSurface(s));
-
- public static final DynamicColor onTertiaryFixed =
- DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 10.0,
- (s) -> tertiaryFixedDarker);
-
- public static final DynamicColor onTertiaryFixedVariant =
- DynamicColor.fromPalette((s) -> s.tertiaryPalette, (s) -> 30.0,
- (s) -> tertiaryFixedDarker);
-
/**
* These colors were present in Android framework before Android U, and used by MDC controls.
* They
* should be avoided, if possible. It's unclear if they're used on multiple backgrounds, and if
- * they are, they can't be adjusted for contrast.* For now, they will be set with no
- * background,
+ * they are, they can't be adjusted for contrast.* For now, they will be set with no background,
* and those won't adjust for contrast, avoiding issues.
*
- * <p>* For example, if the same color is on a white background _and_ black background, there's
- * no
+ * <p>* For example, if the same color is on a white background _and_ black background,
+ * there's no
* way to increase contrast with either without losing contrast with the other.
*/
// colorControlActivated documented as colorAccent in M3 & GM3.
// colorAccent documented as colorSecondary in M3 and colorPrimary in GM3.
// Android used Material's Container as Primary/Secondary/Tertiary at launch.
// Therefore, this is a duplicated version of Primary Container.
- public static final DynamicColor controlActivated =
- DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null);
+ public static DynamicColor controlActivated() {
+ return DynamicColor.fromPalette((s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0, null);
+ }
- // colorControlNormal documented as textColorSecondary in M3 & GM3.
- // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state,
- // which is Neutral Variant T30/80 in light/dark.
- public static final DynamicColor controlNormal =
- DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0);
+ // Compatibility Keys Colors for Android
+ public static DynamicColor primaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone());
+ }
- // colorControlHighlight documented, in both M3 & GM3:
- // Light mode: #1f000000 dark mode: #33ffffff.
- // These are black and white with some alpha.
- // 1F hex = 31 decimal; 31 / 255 = 12% alpha.
- // 33 hex = 51 decimal; 51 / 255 = 20% alpha.
- // DynamicColors do not support alpha currently, and _may_ not need it for this use case,
- // depending on how MDC resolved alpha for the other cases.
- // Returning black in dark mode, white in light mode.
- public static final DynamicColor controlHighlight =
- new DynamicColor(
- s -> 0.0,
- s -> 0.0,
- s -> s.isDark ? 100.0 : 0.0,
- s -> s.isDark ? 0.20 : 0.12,
- null,
- scheme ->
- DynamicColor.toneMinContrastDefault(
- (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
- scheme ->
- DynamicColor.toneMaxContrastDefault(
- (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
- null);
+ public static DynamicColor secondaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone());
+ }
- // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90.
- public static final DynamicColor textPrimaryInverse =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ public static DynamicColor tertiaryPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone());
+ }
- // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as
- // NV30/NV80
- public static final DynamicColor textSecondaryAndTertiaryInverse =
- DynamicColor.fromPalette((s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0);
+ public static DynamicColor neutralPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone());
+ }
- // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90
- public static final DynamicColor textPrimaryInverseDisableOnly =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
-
- // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented,
- // in both M3 & GM3, as N10/N90
- public static final DynamicColor textSecondaryAndTertiaryInverseDisabled =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
-
- // textColorHintInverse documented, in both M3 & GM3, as N10/N90
- public static final DynamicColor textHintInverse =
- DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ public static DynamicColor neutralVariantPaletteKeyColor() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette,
+ (s) -> s.neutralVariantPalette.getKeyColor().getTone());
+ }
private static ViewingConditions viewingConditionsForAlbers(DynamicScheme scheme) {
return ViewingConditions.defaultWithBackgroundLstar(scheme.isDark ? 30.0 : 80.0);
@@ -377,6 +90,10 @@
return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT;
}
+ private static boolean isMonochrome(DynamicScheme scheme) {
+ return scheme.variant == Variant.MONOCHROME;
+ }
+
static double findDesiredChromaByTone(
double hue, double chroma, double tone, boolean byDecreasingTone) {
double answer = tone;
@@ -416,34 +133,456 @@
}
}
- // Compatibility mappings for Android
- public static final DynamicColor surfaceContainerLow = surfaceSub1;
- public static final DynamicColor surfaceContainerLowest = surfaceSub2;
- public static final DynamicColor surfaceContainerHigh = surfaceAdd1;
- public static final DynamicColor surfaceContainerHighest = surfaceAdd2;
- public static final DynamicColor primaryFixedDim = primaryFixedDarker;
- public static final DynamicColor secondaryFixedDim = secondaryFixedDarker;
- public static final DynamicColor tertiaryFixedDim = tertiaryFixedDarker;
+ public static DynamicColor highestSurface(DynamicScheme s) {
+ return s.isDark ? surfaceBright() : surfaceDim();
+ }
- // Compatibility Keys Colors for Android
- public static final DynamicColor primaryPaletteKeyColor =
- DynamicColor.fromPalette(
- (s) -> s.primaryPalette, (s) -> s.primaryPalette.getKeyColor().getTone());
+ public static DynamicColor background() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
+ }
- public static final DynamicColor secondaryPaletteKeyColor =
- DynamicColor.fromPalette(
- (s) -> s.secondaryPalette, (s) -> s.secondaryPalette.getKeyColor().getTone());
+ public static DynamicColor onBackground() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> background());
+ }
- public static final DynamicColor tertiaryPaletteKeyColor =
- DynamicColor.fromPalette(
- (s) -> s.tertiaryPalette, (s) -> s.tertiaryPalette.getKeyColor().getTone());
+ public static DynamicColor surface() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 98.0);
+ }
- public static final DynamicColor neutralPaletteKeyColor =
- DynamicColor.fromPalette(
- (s) -> s.neutralPalette, (s) -> s.neutralPalette.getKeyColor().getTone());
+ public static DynamicColor inverseSurface() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 20.0);
+ }
- public static final DynamicColor neutralVariantPaletteKeyColor =
- DynamicColor.fromPalette(
- (s) -> s.neutralVariantPalette,
- (s) -> s.neutralVariantPalette.getKeyColor().getTone());
+ public static DynamicColor surfaceBright() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 24.0 : 98.0);
+ }
+
+ public static DynamicColor surfaceDim() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 6.0 : 87.0);
+ }
+
+ public static DynamicColor surfaceContainerLowest() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 4.0 : 100.0);
+ }
+
+ public static DynamicColor surfaceContainerLow() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 96.0);
+ }
+
+ public static DynamicColor surfaceContainer() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 12.0 : 94.0);
+ }
+
+ public static DynamicColor surfaceContainerHigh() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 17.0 : 92.0);
+ }
+
+ public static DynamicColor surfaceContainerHighest() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 22.0 : 90.0);
+ }
+
+ public static DynamicColor onSurface() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 90.0 : 10.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor inverseOnSurface() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralPalette, (s) -> s.isDark ? 20.0 : 95.0, (s) -> inverseSurface());
+ }
+
+ public static DynamicColor surfaceVariant() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
+ (s) -> s.isDark ? 30.0 : 90.0);
+ }
+
+ public static DynamicColor onSurfaceVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0,
+ (s) -> surfaceVariant());
+ }
+
+ public static DynamicColor outline() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> 50.0, MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor outlineVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor primaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isFidelity(s)) {
+ return performAlbers(s.sourceColorHct, s);
+ }
+ if (isMonochrome(s)) {
+ return s.isDark ? 85.0 : 25.0;
+ }
+ return s.isDark ? 30.0 : 90.0;
+ },
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onPrimaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isFidelity(s)) {
+ return DynamicColor.contrastingTone(primaryContainer().tone.apply(s), 4.5);
+ }
+ if (isMonochrome(s)) {
+ return s.isDark ? 0.0 : 100.0;
+ }
+ return s.isDark ? 90.0 : 10.0;
+ },
+ (s) -> primaryContainer(),
+ null);
+ }
+
+ public static DynamicColor primary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 100.0 : 0.0;
+ }
+ return s.isDark ? 80.0 : 40.0;
+ },
+ MaterialDynamicColors::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ primaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ public static DynamicColor inversePrimary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette, (s) -> s.isDark ? 40.0 : 80.0, (s) -> inverseSurface());
+ }
+
+ public static DynamicColor onPrimary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 90.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> primary());
+ }
+
+ public static DynamicColor secondaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 30.0 : 85.0;
+ }
+ final double initialTone = s.isDark ? 30.0 : 90.0;
+ if (!isFidelity(s)) {
+ return initialTone;
+ }
+ double answer =
+ findDesiredChromaByTone(
+ s.secondaryPalette.getHue(),
+ s.secondaryPalette.getChroma(),
+ initialTone,
+ !s.isDark);
+ answer = performAlbers(s.secondaryPalette.getHct(answer), s);
+ return answer;
+ },
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onSecondaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (!isFidelity(s)) {
+ return s.isDark ? 90.0 : 10.0;
+ }
+ return DynamicColor.contrastingTone(secondaryContainer().tone.apply(s), 4.5);
+ },
+ (s) -> secondaryContainer());
+ }
+
+ public static DynamicColor secondary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> s.isDark ? 80.0 : 40.0,
+ MaterialDynamicColors::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ secondaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ public static DynamicColor onSecondary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 100.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> secondary());
+ }
+
+ public static DynamicColor tertiaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 60.0 : 49.0;
+ }
+ if (!isFidelity(s)) {
+ return s.isDark ? 30.0 : 90.0;
+ }
+ final double albersTone =
+ performAlbers(s.tertiaryPalette.getHct(s.sourceColorHct.getTone()), s);
+ final Hct proposedHct = s.tertiaryPalette.getHct(albersTone);
+ return DislikeAnalyzer.fixIfDisliked(proposedHct).getTone();
+ },
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onTertiaryContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 0.0 : 100.0;
+ }
+ if (!isFidelity(s)) {
+ return s.isDark ? 90.0 : 10.0;
+ }
+ return DynamicColor.contrastingTone(tertiaryContainer().tone.apply(s), 4.5);
+ },
+ (s) -> tertiaryContainer());
+ }
+
+ public static DynamicColor tertiary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 90.0 : 25.0;
+ }
+ return s.isDark ? 80.0 : 40.0;
+ },
+ MaterialDynamicColors::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ tertiaryContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ public static DynamicColor onTertiary() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 90.0;
+ }
+ return s.isDark ? 20.0 : 100.0;
+ },
+ (s) -> tertiary());
+ }
+
+ public static DynamicColor errorContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 30.0 : 90.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onErrorContainer() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 90.0 : 10.0, (s) -> errorContainer());
+ }
+
+ public static DynamicColor error() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette,
+ (s) -> s.isDark ? 80.0 : 40.0,
+ MaterialDynamicColors::highestSurface,
+ (s) ->
+ new ToneDeltaConstraint(
+ CONTAINER_ACCENT_TONE_DELTA,
+ errorContainer(),
+ s.isDark ? TonePolarity.DARKER : TonePolarity.LIGHTER));
+ }
+
+ public static DynamicColor onError() {
+ return DynamicColor.fromPalette(
+ (s) -> s.errorPalette, (s) -> s.isDark ? 20.0 : 100.0, (s) -> error());
+ }
+
+ public static DynamicColor primaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 100.0 : 10.0;
+ }
+ return 90.0;
+ },
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor primaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 90.0 : 20.0;
+ }
+ return 80.0;
+ },
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onPrimaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 10.0 : 90.0;
+ }
+ return 10.0;
+ },
+ (s) -> primaryFixedDim());
+ }
+
+ public static DynamicColor onPrimaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.primaryPalette,
+ (s) -> {
+ if (isMonochrome(s)) {
+ return s.isDark ? 30.0 : 70.0;
+ }
+ return 30.0;
+ },
+ (s) -> primaryFixedDim());
+ }
+
+ public static DynamicColor secondaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 80.0 : 90.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor secondaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> isMonochrome(s) ? 70.0 : 80.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onSecondaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette, (s) -> 10.0, (s) -> secondaryFixedDim());
+ }
+
+ public static DynamicColor onSecondaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.secondaryPalette,
+ (s) -> isMonochrome(s) ? 25.0 : 30.0,
+ (s) -> secondaryFixedDim());
+ }
+
+ public static DynamicColor tertiaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 40.0 : 90.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor tertiaryFixedDim() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 30.0 : 80.0,
+ MaterialDynamicColors::highestSurface);
+ }
+
+ public static DynamicColor onTertiaryFixed() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 90.0 : 10.0,
+ (s) -> tertiaryFixedDim());
+ }
+
+ public static DynamicColor onTertiaryFixedVariant() {
+ return DynamicColor.fromPalette(
+ (s) -> s.tertiaryPalette, (s) -> isMonochrome(s) ? 70.0 : 30.0,
+ (s) -> tertiaryFixedDim());
+ }
+
+ // colorControlNormal documented as textColorSecondary in M3 & GM3.
+ // In Material, textColorSecondary points to onSurfaceVariant in the non-disabled state,
+ // which is Neutral Variant T30/80 in light/dark.
+ public static DynamicColor controlNormal() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
+ (s) -> s.isDark ? 80.0 : 30.0);
+ }
+
+ // colorControlHighlight documented, in both M3 & GM3:
+ // Light mode: #1f000000 dark mode: #33ffffff.
+ // These are black and white with some alpha.
+ // 1F hex = 31 decimal; 31 / 255 = 12% alpha.
+ // 33 hex = 51 decimal; 51 / 255 = 20% alpha.
+ // DynamicColors do not support alpha currently, and _may_ not need it for this use case,
+ // depending on how MDC resolved alpha for the other cases.
+ // Returning black in dark mode, white in light mode.
+ public static DynamicColor controlHighlight() {
+ return new DynamicColor(
+ s -> 0.0,
+ s -> 0.0,
+ s -> s.isDark ? 100.0 : 0.0,
+ s -> s.isDark ? 0.20 : 0.12,
+ null,
+ scheme ->
+
+ DynamicColor.toneMinContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
+ scheme, null),
+ scheme ->
+ DynamicColor.toneMaxContrastDefault((s) -> s.isDark ? 100.0 : 0.0, null,
+ scheme, null),
+ null);
+ }
+
+ // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90.
+ public static DynamicColor textPrimaryInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
+
+ // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as
+ // NV30/NV80
+ public static DynamicColor textSecondaryAndTertiaryInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralVariantPalette,
+ (s) -> s.isDark ? 30.0 : 80.0);
+ }
+
+ // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90
+ public static DynamicColor textPrimaryInverseDisableOnly() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
+
+ // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented,
+ // in both M3 & GM3, as N10/N90
+ public static DynamicColor textSecondaryAndTertiaryInverseDisabled() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
+
+ // textColorHintInverse documented, in both M3 & GM3, as N10/N90
+ public static DynamicColor textHintInverse() {
+ return DynamicColor.fromPalette((s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0);
+ }
}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java b/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java
index 8480684..cc6b492 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/scheme/SchemeTonalSpot.java
@@ -32,7 +32,7 @@
TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0),
TonalPalette.fromHueAndChroma(
MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0),
- TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 4.0),
+ TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 6.0),
TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0));
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 8ef2d80..ca3e710 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -141,8 +141,16 @@
/** Runs the battery animation (if any). */
fun charge() {}
- /** Move the clock, for example, if the notification tray appears in split-shade mode. */
- fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {}
+ /**
+ * Runs when the clock's position changed during the move animation.
+ *
+ * @param fromLeft the [View.getLeft] position of the clock, before it started moving.
+ * @param direction the direction in which it is moving. A positive number means right, and
+ * negative means left.
+ * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means
+ * it finished moving.
+ */
+ fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
/**
* Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 436145e..3244eb4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -131,7 +131,8 @@
/**
* A rounded corner clipping that makes QS feel as if it were behind everything.
*/
- void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible);
+ void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int cornerRadius,
+ boolean visible, boolean fullWidth);
/**
* @return if quick settings is fully collapsed currently
diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
index 29832a0..934fa6f 100644
--- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
+++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
@@ -30,7 +30,7 @@
<FrameLayout
android:id="@+id/inout_container"
- android:layout_height="17dp"
+ android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical">
<ImageView
@@ -39,24 +39,25 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
android:visibility="gone"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
/>
<ImageView
android:id="@+id/mobile_out"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
android:visibility="gone"
/>
</FrameLayout>
<ImageView
android:id="@+id/mobile_type"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:paddingStart="2.5dp"
- android:paddingEnd="1dp"
+ android:adjustViewBounds="true"
+ android:paddingStart="2.5sp"
+ android:paddingEnd="1sp"
android:visibility="gone" />
<Space
android:id="@+id/mobile_roaming_space"
@@ -70,14 +71,14 @@
android:layout_gravity="center_vertical">
<com.android.systemui.statusbar.AnimatedImageView
android:id="@+id/mobile_signal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
+ android:layout_width="@dimen/status_bar_mobile_signal_size"
systemui:hasOverlappingRendering="false"
/>
<ImageView
android:id="@+id/mobile_roaming"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="@dimen/status_bar_mobile_signal_size"
+ android:layout_height="@dimen/status_bar_mobile_signal_size"
android:src="@drawable/stat_sys_roaming"
android:contentDescription="@string/data_connection_roaming"
android:visibility="gone" />
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 4fc411e..4d289eb 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -22,6 +22,7 @@
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI">
<item name="android:textColor">?androidprv:attr/materialColorOnTertiaryFixed</item>
diff --git a/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml b/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml
index dad2cdf8e..54bdf18 100644
--- a/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml
+++ b/packages/SystemUI/res/drawable/dream_overlay_assistant_attention_indicator.xml
@@ -13,20 +13,30 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56dp"
android:height="24dp"
android:viewportWidth="56"
android:viewportHeight="24">
- <group>
- <clip-path
- android:pathData="M12 0H44C50.6274 0 56 5.37258 56 12C56 18.6274 50.6274 24 44 24H12C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0Z"
- />
+ <path
+ android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
+ android:fillColor="#ffffff"/>
+ <group
+ android:scaleX="0.8"
+ android:scaleY="0.8"
+ android:translateY="2"
+ android:translateX="18">
<path
- android:pathData="M0 0V24H56V0"
- android:fillColor="#FFFFFF"
- />
+ android:pathData="M21.5,9C22.3284,9 23,8.3284 23,7.5C23,6.6716 22.3284,6 21.5,6C20.6716,6 20,6.6716 20,7.5C20,8.3284 20.6716,9 21.5,9Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M17,14C18.6569,14 20,12.6569 20,11C20,9.3432 18.6569,8 17,8C15.3431,8 14,9.3432 14,11C14,12.6569 15.3431,14 17,14Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M17,22C18.933,22 20.5,20.433 20.5,18.5C20.5,16.567 18.933,15 17,15C15.067,15 13.5,16.567 13.5,18.5C13.5,20.433 15.067,22 17,22Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M7,14C10.3137,14 13,11.3137 13,8C13,4.6863 10.3137,2 7,2C3.6863,2 1,4.6863 1,8C1,11.3137 3.6863,14 7,14Z"
+ android:fillColor="#000000"/>
</group>
-</vector>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
index ee8d4883..a35504f 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_keyguard.xml
@@ -16,16 +16,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:fillAlpha="1"
- android:fillColor="@android:color/white"
- android:fillType="nonZero"
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
+ android:pathData="M23.41,6L22,4.59C21.63,4.21 21.12,4 20.59,4C20.06,4 19.55,4.21 19.18,4.59L11.39,12.38L9.09,14.68L8.04,18.9C8.01,18.96 8,19.04 8,19.11C8,19.6 8.4,20 8.89,20C8.96,20 9.04,19.99 9.11,19.97L13.33,18.92L15.63,16.62L23.42,8.83C23.79,8.45 24,7.94 24,7.41C24,6.88 23.79,6.37 23.41,6ZM14.21,15.21L13.21,16.21L11.8,14.8L12.8,13.8L20.59,6L22,7.41L14.21,15.21Z"
+ android:fillColor="@android:color/white"/>
<path
- android:fillAlpha="1"
- android:fillColor="@android:color/white"
- android:fillType="nonZero"
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
+ android:pathData="M6.688,20C2.047,20 0.333,18.65 0.333,16C0.333,13.61 2.439,12.474 5.713,12C6.792,11.844 7.344,11.397 7.344,10.927C7.344,9.625 4.679,9.705 3.833,9.667V7.667C3.833,7.667 6.792,7.667 8.208,8.729C8.932,9.272 9.333,9.979 9.333,11.05C9.333,12.52 8.281,13.677 5.713,13.885C4.017,14.023 2.333,14.52 2.333,16C2.333,17.33 4.013,18 7.333,18L6.688,20Z"
+ android:fillColor="@android:color/white"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
index 7590182..860fc7d 100644
--- a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget.xml
@@ -13,19 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24"
- android:viewportWidth="24">
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
- <path
- android:fillAlpha="1"
- android:fillColor="#636C6F"
- android:fillType="nonZero"
- android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
-</vector>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_note_task_shortcut_widget_background" />
+ <foreground android:drawable="@drawable/ic_note_task_shortcut_widget_foreground" />
+</adaptive-icon>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml
new file mode 100644
index 0000000..9f98f07
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M0,0h108v108h-108z"
+ android:fillColor="#0B57D0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml
new file mode 100644
index 0000000..fcb3ef4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_note_task_shortcut_widget_foreground.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M74.92,43L72.33,40.42C71.65,39.72 70.72,39.33 69.75,39.33C68.78,39.33 67.84,39.72 67.16,40.42L52.88,54.7L48.67,58.91L46.74,66.65C46.69,66.76 46.67,66.91 46.67,67.04C46.67,67.93 47.4,68.67 48.3,68.67C48.43,68.67 48.57,68.65 48.7,68.61L56.44,66.69L60.65,62.47L74.94,48.19C75.61,47.49 76,46.56 76,45.58C76,44.61 75.61,43.68 74.92,43ZM58.05,59.88L56.22,61.72L53.63,59.13L55.47,57.3L69.75,43L72.33,45.58L58.05,59.88Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M44.26,68.67C35.75,68.67 32.61,66.19 32.61,61.33C32.61,56.95 36.47,54.87 42.47,54C44.45,53.71 45.46,52.89 45.46,52.03C45.46,49.65 40.58,49.79 39.03,49.72V46.06C39.03,46.06 44.45,46.06 47.05,48C48.37,49 49.11,50.3 49.11,52.26C49.11,54.95 47.18,57.07 42.47,57.46C39.36,57.71 36.28,58.62 36.28,61.33C36.28,63.77 39.36,65 45.44,65L44.26,68.67Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 021ebe6..33f1b10 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -23,40 +23,47 @@
android:orientation="vertical"
android:theme="?app:attr/lockPinPasswordStyle">
- <RelativeLayout
+ <ScrollView
android:id="@+id/auth_credential_header"
- style="?headerStyle"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content">
- <ImageView
- android:id="@+id/icon"
- style="?headerIconStyle"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:contentDescription="@null"/>
-
- <TextView
- android:id="@+id/title"
- style="?titleTextAppearance"
- android:layout_below="@id/icon"
+ <RelativeLayout
+ style="?headerStyle"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/subtitle"
- style="?subTitleTextAppearance"
- android:layout_below="@id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ <ImageView
+ android:id="@+id/icon"
+ style="?headerIconStyle"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null" />
- <TextView
- android:id="@+id/description"
- style="?descriptionTextAppearance"
- android:layout_below="@id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </RelativeLayout>
+ <TextView
+ android:id="@+id/title"
+ style="?titleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/icon" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="?subTitleTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title" />
+
+ <TextView
+ android:id="@+id/description"
+ style="?descriptionTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/subtitle" />
+
+ </RelativeLayout>
+
+ </ScrollView>
<LinearLayout
android:id="@+id/auth_credential_input"
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 441f963..e989372 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -126,8 +126,7 @@
<com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ android:layout_height="0dp"
app:layout_constrainedWidth="true"
app:textAppearance="@style/TextAppearance.QS.Status"
app:layout_constraintStart_toEndOf="@id/statusIcons"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index ab38dd2..ae052502 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -49,13 +49,13 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:fontFamily="@*android:string/config_headlineFontFamily"
- android:text="@string/screenrecord_start_label"
+ android:text="@string/screenrecord_permission_dialog_title"
android:layout_marginTop="22dp"
android:layout_marginBottom="15dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/screenrecord_description"
+ android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:gravity="center"
@@ -168,7 +168,7 @@
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="end"
- android:text="@string/screenrecord_start"
+ android:text="@string/screenrecord_continue"
style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index bd71989..0d86e0a 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -63,7 +63,7 @@
android:id="@+id/text_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/screenrecord_description"
+ android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:textColorSecondary"
android:gravity="start"
@@ -76,7 +76,7 @@
android:orientation="horizontal"
android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
<TextView
- android:id="@+id/button_cancel"
+ android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
@@ -87,11 +87,11 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
<TextView
- android:id="@+id/button_start"
+ android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
- android:text="@string/screenrecord_start"
+ android:text="@string/screenrecord_continue"
style="@style/Widget.Dialog.Button" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
index 0ea0653..473ab08 100644
--- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
@@ -24,11 +24,11 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:layout_marginStart="2.5dp"
+ android:layout_marginStart="2.5sp"
>
<FrameLayout
android:id="@+id/inout_container"
- android:layout_height="17dp"
+ android:layout_height="@*android:dimen/status_bar_system_icon_intrinsic_size"
android:layout_width="wrap_content"
android:gravity="center_vertical" >
<ImageView
@@ -37,14 +37,14 @@
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_down"
android:visibility="gone"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
/>
<ImageView
android:id="@+id/wifi_out"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_activity_up"
- android:paddingEnd="2dp"
+ android:paddingEnd="2sp"
android:visibility="gone"
/>
</FrameLayout>
@@ -62,7 +62,7 @@
<View
android:id="@+id/wifi_signal_spacer"
android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
- android:layout_height="4dp"
+ android:layout_height="4sp"
android:visibility="gone" />
<!-- Looks like CarStatusBar uses this... -->
@@ -75,7 +75,7 @@
<View
android:id="@+id/wifi_airplane_spacer"
android:layout_width="@dimen/status_bar_airplane_spacer_width"
- android:layout_height="4dp"
+ android:layout_height="4sp"
android:visibility="gone"
/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json
new file mode 100644
index 0000000..49c1c40
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_landscape_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Landscape_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-129,"s":[-67]},{"t":-29,"s":[0]}],"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json
new file mode 100644
index 0000000..9ea0d35
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 3","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-2,"ix":10},"p":{"a":0,"k":[260.134,83.782,0],"ix":2,"l":2},"a":{"a":0,"k":[302.634,38.782,0],"ix":1,"l":2},"s":{"a":0,"k":[178,178,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.262,5.076],[0,0],[-0.424,-7.095],[-0.028,-0.225]],"o":[[3.269,-3.892],[-12.123,2.932],[0.015,0.234],[0.567,-0.034]],"v":[[9.232,0.652],[11.145,-6.746],[-11.412,6.046],[-11.346,6.746]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[241.281,55.033],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.565,-1.102],[3.269,-3.892],[0.566,-0.033],[-18.63,2.353],[-16.656,3.951],[9.004,6.546],[6.9,-2.19]],"o":[[0,0],[-4.262,5.076],[1.008,9.61],[14.171,-1.79],[-4.028,-10.569],[-4.156,1.703],[-4.392,1.392]],"v":[[-13.858,-7.546],[-15.771,-0.148],[-36.349,5.946],[-7.047,16.299],[36.349,9.142],[16.281,-17.051],[-0.156,-11.172]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[266.285,55.833],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"black circle matte 4","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"black circle matte 5","parent":14,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey700","cl":"grey700","parent":16,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json
new file mode 100644
index 0000000..f2b2593
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_rear_portrait_reverse_base.json
@@ -0,0 +1 @@
+{"v":"5.8.1","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Rear_Portrait_Reverse_Base_Foldable","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 18","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":270,"ix":10},"p":{"a":0,"k":[169.478,169.749,0],"ix":2,"l":2},"a":{"a":0,"k":[-48.123,-30.19,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".grey400","cl":"grey400","parent":13,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"black circle matte","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey904","cl":"grey904","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,35.536,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-2.552,0.087],[0,0]],"o":[[0,0],[0,-3.287],[0,0],[0,0]],"v":[[-2.301,8.869],[-2.301,-3.772],[2.301,-9.806],[2.301,9.806]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"black circle matte 2","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue401","cl":"blue401","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-62.577,-27.655,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.286],[0,0],[-2.552,0.086],[0,0]],"o":[[0,0],[0,-3.286],[0,0],[-2.552,-0.086]],"v":[[-2.301,16.282],[-2.301,-16.281],[2.301,-22.313],[2.301,22.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"black circle matte 3","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Finger 2","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-75.352,41.307,0],"ix":2,"l":2},"a":{"a":0,"k":[94.648,211.307,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.72,-5.642],[0,0],[-9.394,-0.562],[-0.298,-0.038]],"o":[[-5.153,4.329],[3.882,-16.05],[0.31,0.019],[-0.044,0.75]],"v":[[0.863,12.222],[-8.931,14.755],[8.005,-15.108],[8.931,-15.021]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156875134,0.454901963472,0.376470595598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[81.486,130.081],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 9","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.459,6.045],[-5.153,4.329],[-0.044,0.75],[3.116,-24.664],[5.23,-22.052],[8.666,11.92],[-2.9,9.135]],"o":[[0,0],[6.72,-5.642],[12.723,1.335],[-2.369,18.762],[-13.993,-5.333],[2.255,-5.502],[1.843,-5.815]],"v":[[-9.99,-18.348],[-0.196,-20.881],[7.872,-48.124],[21.578,-9.331],[12.104,48.124],[-22.574,21.555],[-14.791,-0.206]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.713725507259,0.384313732386,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82.545,163.184],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 8","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"black circle matte 4","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".grey903","cl":"grey903","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-18.345,-92.442,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[24.07,0],[0,0],[-8.27,0],[0,0]],"o":[[0,0],[0,8.269],[0,0],[-14.024,-17.379]],"v":[[-29.778,-14.252],[-29.778,-0.721],[-14.805,14.252],[29.778,14.252]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"black circle matte 5","parent":13,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey902","cl":"grey902","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-15.947,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[154.053,139.81,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.3,0.367],[0,0],[-2.364,0.157],[0,0]],"o":[[0,0],[2.3,-0.367],[0,0],[-2.364,-0.157]],"v":[[-3.5,75.533],[-3.5,-75.533],[3.5,-76.312],[3.5,76.312]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[113.225,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,8.269],[0,0],[2.181,-0.187],[0,0],[-2.23,0],[0,42.252],[10.593,13.127],[0,0]],"o":[[0,0],[-2.23,0],[0,0],[2.181,0.187],[42.252,0],[0,-18.182],[0,0],[-8.27,0]],"v":[[-34.946,-62.973],[-34.946,-76.504],[-41.558,-76.201],[-41.558,76.201],[-34.946,76.504],[41.558,0],[24.61,-48],[-19.973,-48]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.525490224361,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156.824,139.81],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Layer 5","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".black 2","cl":"black","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-48.123,-30.19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-129,"s":[0,0,100]},{"t":-79,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-42.252,0],[0,42.252],[42.252,0],[0,-42.252]],"o":[[42.252,0],[0,-42.252],[-42.252,0],[0,42.252]],"v":[[0,76.504],[76.504,0],[0,-76.504],[-76.504,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey700","cl":"grey700","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-56.481,-59.936,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.767,0],[0,0],[0,-3.767],[0,0],[-3.767,0],[0,0],[0,3.767],[0,0]],"o":[[0,0],[-3.767,0],[0,0],[0,3.767],[0,0],[3.767,0],[0,0],[0,-3.767]],"v":[[46.055,-14.479],[-46.056,-14.479],[-52.876,-7.659],[-52.876,7.658],[-46.056,14.479],[46.055,14.479],[52.876,7.658],[52.876,-7.659]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.372549027205,0.388235300779,0.407843142748,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":".grey901","cl":"grey901","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[16.485,2.727,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[50,50,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.184,0],[0,0],[0,0],[0,0],[0,-4.375]],"o":[[0,4.184],[0,0],[0,0],[0,0],[4.375,0],[0,0]],"v":[[114.116,92.129],[106.54,99.705],[7.788,99.705],[7.788,-99.704],[106.161,-99.704],[114.116,-91.749]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[5.707,0],[0,0],[1.894,-1.05],[0.886,0.346],[0,0],[2.166,0],[0,0],[0,-5.707],[0,0],[0,-1.46],[0,0],[-1.133,-0.038],[0,0],[0,-1.459],[0,0],[-1.133,-0.038],[0,0],[-5.708,0],[0,0],[-1.894,1.05],[-0.846,-0.289],[0,0],[-2.166,0],[0,0],[0,5.706],[0,0]],"o":[[0,0],[-2.166,0],[-0.883,0.354],[0,0],[-1.895,-1.05],[0,0],[-5.708,0],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[-1.133,0.038],[0,0],[0,1.46],[0,0],[0,5.707],[0,0],[2.165,0],[0.833,-0.334],[0,0],[1.894,1.05],[0,0],[5.707,0],[0,0],[0,-5.707]],"v":[[106.16,-102.082],[8.455,-102.082],[2.265,-100.48],[-0.488,-100.468],[-0.519,-100.48],[-6.71,-102.082],[-104.116,-102.082],[-114.45,-91.748],[-114.45,-36.119],[-116.494,-33.44],[-116.494,-18.979],[-114.45,-16.3],[-114.45,-0.877],[-116.494,1.802],[-116.494,28.704],[-114.45,31.383],[-114.45,91.749],[-104.116,102.083],[-6.495,102.083],[-0.305,100.481],[2.294,100.425],[2.395,100.481],[9.872,102.083],[106.161,102.083],[116.494,91.75],[116.494,-91.748]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960813999,0.529411792755,0.54509806633,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-189,"op":711,"st":-189,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 2086459..f40615e 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -17,7 +17,7 @@
-->
<resources>
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">1dp</dimen>
+ <dimen name="status_bar_icon_padding">1sp</dimen>
<dimen name="controls_header_horizontal_padding">28dp</dimen>
<dimen name="controls_content_margin_horizontal">40dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0aa880f..f5c4a4e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -122,26 +122,26 @@
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
<!-- Default horizontal drawable padding for status bar icons. -->
- <dimen name="status_bar_horizontal_padding">2.5dp</dimen>
+ <dimen name="status_bar_horizontal_padding">2.5sp</dimen>
<!-- Height of the battery icon in the status bar. -->
- <dimen name="status_bar_battery_icon_height">13.0dp</dimen>
+ <dimen name="status_bar_battery_icon_height">13.0sp</dimen>
<!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas,
- so the width of the icon should be 13.0dp * (12.0 / 20.0) -->
- <dimen name="status_bar_battery_icon_width">7.8dp</dimen>
+ so the width of the icon should be 13.0sp * (12.0 / 20.0) -->
+ <dimen name="status_bar_battery_icon_width">7.8sp</dimen>
- <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see
+ <!-- The battery icon is 13sp tall, but the other system icons are 15sp tall (see
@*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in
- the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its
+ the drawables themselves. So, the battery icon may need an extra 1sp of spacing so that its
bottom still aligns with the bottom of all the other system icons. See b/258672854. -->
- <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen>
+ <dimen name="status_bar_battery_extra_vertical_spacing">1sp</dimen>
<!-- The font size for the clock in the status bar. -->
<dimen name="status_bar_clock_size">14sp</dimen>
<!-- The starting padding for the clock in the status bar. -->
- <dimen name="status_bar_clock_starting_padding">7dp</dimen>
+ <dimen name="status_bar_clock_starting_padding">7sp</dimen>
<!-- The end padding for the clock in the status bar. -->
<dimen name="status_bar_clock_end_padding">0dp</dimen>
@@ -153,16 +153,19 @@
<dimen name="status_bar_left_clock_end_padding">2dp</dimen>
<!-- Spacing after the wifi signals that is present if there are any icons following it. -->
- <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
+ <dimen name="status_bar_wifi_signal_spacer_width">2.5sp</dimen>
<!-- Size of the view displaying the wifi signal icon in the status bar. -->
- <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen>
+ <dimen name="status_bar_wifi_signal_size">13sp</dimen>
+
+ <!-- Size of the view displaying the mobile signal icon in the status bar. -->
+ <dimen name="status_bar_mobile_signal_size">13sp</dimen>
<!-- Spacing before the airplane mode icon if there are any icons preceding it. -->
- <dimen name="status_bar_airplane_spacer_width">4dp</dimen>
+ <dimen name="status_bar_airplane_spacer_width">4sp</dimen>
<!-- Spacing between system icons. -->
- <dimen name="status_bar_system_icon_spacing">0dp</dimen>
+ <dimen name="status_bar_system_icon_spacing">2sp</dimen>
<!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. -->
<item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
@@ -310,7 +313,7 @@
<dimen name="snooze_snackbar_min_height">56dp</dimen>
<!-- size at which Notification icons will be drawn in the status bar -->
- <dimen name="status_bar_icon_drawing_size">15dp</dimen>
+ <dimen name="status_bar_icon_drawing_size">15sp</dimen>
<!-- size at which Notification icons will be drawn on Ambient Display -->
<dimen name="status_bar_icon_drawing_size_dark">
@@ -321,22 +324,22 @@
<item type="dimen" name="status_bar_icon_drawing_alpha">90%</item>
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">0dp</dimen>
+ <dimen name="status_bar_icon_padding">0sp</dimen>
<!-- the padding on the start of the statusbar -->
- <dimen name="status_bar_padding_start">8dp</dimen>
+ <dimen name="status_bar_padding_start">8sp</dimen>
<!-- the padding on the end of the statusbar -->
- <dimen name="status_bar_padding_end">8dp</dimen>
+ <dimen name="status_bar_padding_end">8sp</dimen>
<!-- the padding on the top of the statusbar (usually 0) -->
- <dimen name="status_bar_padding_top">0dp</dimen>
+ <dimen name="status_bar_padding_top">0sp</dimen>
<!-- the radius of the overflow dot in the status bar -->
- <dimen name="overflow_dot_radius">2dp</dimen>
+ <dimen name="overflow_dot_radius">2sp</dimen>
<!-- the padding between dots in the icon overflow -->
- <dimen name="overflow_icon_dot_padding">3dp</dimen>
+ <dimen name="overflow_icon_dot_padding">3sp</dimen>
<!-- Dimensions related to screenshots -->
@@ -617,8 +620,8 @@
<dimen name="qs_footer_icon_size">20dp</dimen>
<dimen name="qs_header_row_min_height">48dp</dimen>
- <dimen name="qs_header_non_clickable_element_height">24dp</dimen>
- <dimen name="new_qs_header_non_clickable_element_height">24dp</dimen>
+ <dimen name="qs_header_non_clickable_element_height">24sp</dimen>
+ <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen>
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
@@ -822,7 +825,7 @@
<!-- Padding between the mobile signal indicator and the start icon when the roaming icon
is displayed in the upper left corner. -->
- <dimen name="roaming_icon_start_padding">2dp</dimen>
+ <dimen name="roaming_icon_start_padding">2sp</dimen>
<!-- Extra padding between the mobile data type icon and the strength indicator when the data
type icon is wide for the tile in quick settings. -->
@@ -1042,13 +1045,13 @@
<dimen name="display_cutout_margin_consumption">0px</dimen>
<!-- Height of the Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_height">24dp</dimen>
+ <dimen name="ongoing_appops_chip_height">24sp</dimen>
<!-- Side padding between background of Ongoing App Ops chip and content -->
<dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
<!-- Margin between icons of Ongoing App Ops chip -->
<dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
<!-- Icon size of Ongoing App Ops chip -->
- <dimen name="ongoing_appops_chip_icon_size">16dp</dimen>
+ <dimen name="ongoing_appops_chip_icon_size">16sp</dimen>
<!-- Radius of Ongoing App Ops chip corners -->
<dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen>
<!-- One or two privacy items -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4e68efe..c57fef1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -251,25 +251,22 @@
<string name="app_clips_save_add_to_note">Add to note</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
- <string name="screenrecord_name">Screen Recorder</string>
+ <string name="screenrecord_title">Screen Recorder</string>
<!-- Processing screen recoding video in the background [CHAR LIMIT=30]-->
<string name="screenrecord_background_processing_label">Processing screen recording</string>
<!-- Description of the screen recording notification channel [CHAR LIMIT=NONE]-->
<string name="screenrecord_channel_description">Ongoing notification for a screen record session</string>
+
+ <!-- For updated Screen Recording permission dialog (i.e. with PSS)-->
<!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]-->
- <string name="screenrecord_start_label">Start Recording?</string>
- <!-- Message reminding the user that sensitive information may be captured during a screen recording [CHAR_LIMIT=NONE]-->
- <string name="screenrecord_description">While recording, Android System can capture any sensitive information that\u2019s visible on your screen or played on your device. This includes passwords, payment info, photos, messages, and audio.</string>
- <!-- Dropdown option to record the entire screen [CHAR_LIMIT=30]-->
- <string name="screenrecord_option_entire_screen">Record entire screen</string>
- <!-- Dropdown option to record a single app [CHAR_LIMIT=30]-->
- <string name="screenrecord_option_single_app">Record a single app</string>
+ <string name="screenrecord_permission_dialog_title">Start Recording?</string>
<!-- Message reminding the user that sensitive information may be captured during a full screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
- <string name="screenrecord_warning_entire_screen">While you\'re recording, Android has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen">While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- Message reminding the user that sensitive information may be captured during a single app screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
- <string name="screenrecord_warning_single_app">While you\'re recording an app, Android has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+ <string name="screenrecord_permission_dialog_warning_single_app">While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
<!-- Button to start a screen recording in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]-->
- <string name="screenrecord_start_recording">Start recording</string>
+ <string name="screenrecord_permission_dialog_continue">Start recording</string>
+
<!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]-->
<string name="screenrecord_audio_label">Record audio</string>
<!-- Label for the option to record audio from the device [CHAR LIMIT=NONE]-->
@@ -281,7 +278,7 @@
<!-- Label for an option to record audio from both device and microphone [CHAR LIMIT=NONE]-->
<string name="screenrecord_device_audio_and_mic_label">Device audio and microphone</string>
<!-- Button to start a screen recording [CHAR LIMIT=50]-->
- <string name="screenrecord_start">Start</string>
+ <string name="screenrecord_continue">Start</string>
<!-- Notification text displayed when we are recording the screen [CHAR LIMIT=100]-->
<string name="screenrecord_ongoing_screen_only">Recording screen</string>
<!-- Notification text displayed when we are recording both the screen and audio [CHAR LIMIT=100]-->
@@ -1064,47 +1061,52 @@
<!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] -->
<string name="user_remove_user_remove">Remove</string>
- <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
- <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
-
- <!-- Media projection permission dialog warning text for system services. [CHAR LIMIT=NONE] -->
- <string name="media_projection_dialog_service_text">The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
-
- <!-- Media projection permission dialog warning title for system services. [CHAR LIMIT=NONE] -->
- <string name="media_projection_dialog_service_title">Start recording or casting?</string>
-
+ <!-- Media projection without Partial Screenshare -->
<!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] -->
<string name="media_projection_dialog_title">Start recording or casting with <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g>?</string>
+ <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
+ <string name="media_projection_dialog_warning"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
+ <!-- Media projection permission dialog warning title for system services. [CHAR LIMIT=NONE] -->
+ <string name="media_projection_sys_service_dialog_title">Start recording or casting?</string>
+ <!-- Media projection permission dialog warning text for system services. [CHAR LIMIT=NONE] -->
+ <string name="media_projection_sys_service_dialog_warning">The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
- <!-- Media projection permission dialog title. [CHAR LIMIT=NONE] -->
- <string name="media_projection_permission_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> to share or record?</string>
-
- <!-- Media projection permission dropdown option for capturing the whole screen. [CHAR LIMIT=30] -->
- <string name="media_projection_permission_dialog_option_entire_screen">Entire screen</string>
-
- <!-- Media projection permission dropdown option for capturing single app. [CHAR LIMIT=30] -->
- <string name="media_projection_permission_dialog_option_single_app">A single app</string>
-
- <!-- Media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] -->
- <string name="media_projection_permission_dialog_warning_entire_screen">When you\'re sharing, recording, or casting, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string>
-
- <!-- Media projection permission warning for capturing an app. [CHAR LIMIT=350] -->
- <string name="media_projection_permission_dialog_warning_single_app">When you\'re sharing, recording, or casting an app, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string>
-
- <!-- Media projection permission button to continue with app selection or recording [CHAR LIMIT=60] -->
- <string name="media_projection_permission_dialog_continue">Continue</string>
-
+ <!-- Permission dropdown option for sharing or recording the whole screen. [CHAR LIMIT=30] -->
+ <string name="screen_share_permission_dialog_option_entire_screen">Entire screen</string>
+ <!-- Permission dropdown option for sharing or recording single app. [CHAR LIMIT=30] -->
+ <string name="screen_share_permission_dialog_option_single_app">A single app</string>
<!-- Title of the dialog that allows to select an app to share or record [CHAR LIMIT=NONE] -->
- <string name="media_projection_permission_app_selector_title">Share or record an app</string>
+ <string name="screen_share_permission_app_selector_title">Share or record an app</string>
- <!-- Media projection permission dialog title when there is no app name (e.g. it could be a system service when casting). [CHAR LIMIT=100] -->
- <string name="media_projection_permission_dialog_system_service_title">Allow this app to share or record?</string>
+ <!-- Media projection that launched from 1P/3P apps -->
+ <!-- 1P/3P app media projection permission dialog title. [CHAR LIMIT=NONE] -->
+ <string name="media_projection_entry_app_permission_dialog_title">Start recording or casting with <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g>?</string>
+ <!-- 1P/3P app media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_app_permission_dialog_warning_entire_screen">When you’re sharing, recording, or casting, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- 1P/3P app media projection permission warning for capturing an app. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_app_permission_dialog_warning_single_app">When you’re sharing, recording, or casting an app, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- 1P/3P apps media projection permission button to continue with app selection or recording [CHAR LIMIT=60] -->
+ <string name="media_projection_entry_app_permission_dialog_continue">Start</string>
- <!-- Media projection permission warning for capturing the whole screen when a system service requests it (e.g. when casting). [CHAR LIMIT=350] -->
- <string name="media_projection_permission_dialog_system_service_warning_entire_screen">When you\'re sharing, recording, or casting, this app has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+ <!-- Casting that launched by SysUI (i.e. when there is no app name) -->
+ <!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
+ <string name="media_projection_entry_cast_permission_dialog_title">Start casting?</string>
+ <!-- System casting media projection permission warning for capturing the whole screen when SysUI casting requests it. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen">When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- System casting media projection permission warning for capturing a single app when SysUI casting requests it. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_cast_permission_dialog_warning_single_app">When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- System casting media projection permission button to continue for SysUI casting. [CHAR LIMIT=60] -->
+ <string name="media_projection_entry_cast_permission_dialog_continue">Start casting</string>
- <!-- Media projection permission warning for capturing a single app when a system service requests it (e.g. when casting). [CHAR LIMIT=350] -->
- <string name="media_projection_permission_dialog_system_service_warning_single_app">When you\'re sharing, recording, or casting an app, this app has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string>
+ <!-- Other sharing (not recording nor casting) that launched by SysUI (currently not in use) -->
+ <!-- System sharing media projection permission dialog title. [CHAR LIMIT=100] -->
+ <string name="media_projection_entry_generic_permission_dialog_title">Start sharing?</string>
+ <!-- System sharing media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen">When you’re sharing, recording, or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- System sharing media projection permission warning for capturing a single app. [CHAR LIMIT=350] -->
+ <string name="media_projection_entry_generic_permission_dialog_warning_single_app">When you’re sharing, recording, or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+ <!-- System sharing media projection permission button to continue. [CHAR LIMIT=60] -->
+ <string name="media_projection_entry_generic_permission_dialog_continue">Start</string>
<!-- Title for the dialog that is shown when screen capturing is disabled by enterprise policy. [CHAR LIMIT=100] -->
<string name="screen_capturing_disabled_by_policy_dialog_title">Blocked by your IT admin</string>
@@ -1255,7 +1257,10 @@
<string name="monitoring_description_managed_profile_network_logging">Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile.</string>
<!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]-->
- <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
+ <string name="monitoring_description_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to the VPN provider.</string>
+
+ <!-- Monitoring dialog: Description of an active VPN on a managed device. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_managed_device_named_vpn">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
<!-- Monitoring dialog: Description of two active VPNs. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_two_named_vpns">This device is connected to the internet through <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>. Your network activity, including emails and browsing data, is visible to your IT admin.</string>
@@ -3033,6 +3038,19 @@
-->
<string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string>
+ <!---
+ Requirement for the notes app to be available for the user to use. This is shown as part of a
+ bulleted list of requirements. When all requirements are met, the app can be accessed through a
+ shortcut button on the lock screen. [CHAR LIMIT=NONE] -->
+ <string name="keyguard_affordance_enablement_dialog_notes_app_instruction">Select a default notes app to use the notetaking shortcut</string>
+
+ <!---
+ The action to make the lock screen shortcut for the notes app to be available for the user to
+ use. This is shown as the action button in the dialog listing the requirements. When all
+ requirements are met, the app can be accessed through a shortcut button on the lock screen.
+ [CHAR LIMIT=NONE] -->
+ <string name="keyguard_affordance_enablement_dialog_notes_app_action">Select app</string>
+
<!--
Error message shown when a shortcut must be pressed and held to activate it, usually shown when
the user tried to tap the shortcut or held it for too short a time. [CHAR LIMIT=32].
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
index 209d5e8..43df08d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java
@@ -32,8 +32,10 @@
import javax.inject.Inject;
/**
- * {@link Monitor} takes in a set of conditions, monitors whether all of them have
- * been fulfilled, and informs any registered listeners.
+ * {@link Monitor} allows {@link Subscription}s to a set of conditions and monitors whether all of
+ * them have been fulfilled.
+ * <p>
+ * This class should be used as a singleton, to prevent duplicate monitoring of the same conditions.
*/
public class Monitor {
private final String mTag = getClass().getSimpleName();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
index 3d05542..4f73fc4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
@@ -109,7 +109,7 @@
/** Load all plugins matching this instance's action. */
public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
- mBgExecutor.execute(this::queryAll);
+ mBgExecutor.execute(() -> queryAll());
}
/** Unload all plugins managed by this instance. */
@@ -255,17 +255,18 @@
intent.setPackage(pkgName);
}
List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
- if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
+ if (DEBUG) {
+ Log.d(TAG, "Found " + result.size() + " plugins");
+ for (ResolveInfo info : result) {
+ ComponentName name = new ComponentName(info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ Log.d(TAG, " " + name);
+ }
+ }
+
if (result.size() > 1 && !mAllowMultiple) {
// TODO: Show warning.
Log.w(TAG, "Multiple plugins found for " + mAction);
- if (DEBUG) {
- for (ResolveInfo info : result) {
- ComponentName name = new ComponentName(info.serviceInfo.packageName,
- info.serviceInfo.name);
- Log.w(TAG, " " + name);
- }
- }
return;
}
for (ResolveInfo info : result) {
@@ -307,7 +308,7 @@
// TODO: Only create the plugin before version check if we need it for
// legacy version check.
if (DEBUG) {
- Log.d(TAG, "createPlugin");
+ Log.d(TAG, "createPlugin: " + component);
}
try {
return mPluginInstanceFactory.create(
@@ -317,7 +318,7 @@
reportInvalidVersion(component, component.getClassName(), e);
}
} catch (Throwable e) {
- Log.w(TAG, "Couldn't load plugin: " + packageName, e);
+ Log.w(TAG, "Couldn't load plugin: " + component, e);
return null;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 016d573..4a66562 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -230,7 +230,7 @@
private ClassLoader getParentClassLoader(ClassLoader baseClassLoader) {
return new PluginManagerImpl.ClassLoaderFilter(
- baseClassLoader, "com.android.systemui.plugin");
+ baseClassLoader, "com.android.systemui.log", "com.android.systemui.plugin");
}
/** Returns class loader specific for the given plugin. */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 2f9f5b2..1e668b8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -248,19 +248,23 @@
// This allows plugins to include any libraries or copied code they want by only including
// classes from the plugin library.
static class ClassLoaderFilter extends ClassLoader {
- private final String mPackage;
+ private final String[] mPackages;
private final ClassLoader mBase;
- public ClassLoaderFilter(ClassLoader base, String pkg) {
+ ClassLoaderFilter(ClassLoader base, String... pkgs) {
super(ClassLoader.getSystemClassLoader());
mBase = base;
- mPackage = pkg;
+ mPackages = pkgs;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
- return mBase.loadClass(name);
+ for (String pkg : mPackages) {
+ if (name.startsWith(pkg)) {
+ return mBase.loadClass(name);
+ }
+ }
+ return super.loadClass(name, resolve);
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 362d7a9..7cf3121 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -68,7 +68,7 @@
onActivityLaunchOnSecondaryDisplayRerouted();
}
- default void onTaskProfileLocked(RunningTaskInfo taskInfo) { }
+ default void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) { }
default void onTaskCreated(int taskId, ComponentName componentName) { }
default void onTaskRemoved(int taskId) { }
default void onTaskMovedToFront(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index dd52cfb..c613afb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -262,8 +262,8 @@
}
@Override
- public void onTaskProfileLocked(RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskInfo).sendToTarget();
+ public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
+ mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, userId, 0, taskInfo).sendToTarget();
}
@Override
@@ -418,8 +418,9 @@
}
case ON_TASK_PROFILE_LOCKED: {
final RunningTaskInfo info = (RunningTaskInfo) msg.obj;
+ final int userId = msg.arg1;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onTaskProfileLocked(info);
+ mTaskStackListeners.get(i).onTaskProfileLocked(info, userId);
}
break;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt
new file mode 100644
index 0000000..af29b05
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/log/DebugLogger.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.os.Build
+import android.util.Log
+import android.util.Log.LOG_ID_MAIN
+
+/**
+ * A simplified debug logger built as a wrapper around Android's [Log]. Internal for development.
+ *
+ * The main advantages are:
+ * - Sensible defaults, automatically retrieving the class name from the call-site (i.e., tag);
+ * - The messages are purged from source on release builds (keep in mind they are visible on AOSP);
+ * - Lazily evaluate Strings for zero impact in production builds or when disabled;
+ *
+ * Usage example:
+ * ```kotlin
+ * // Logging a message:
+ * debugLog { "message" }
+ *
+ * // Logging an error:
+ * debugLog(error = exception) { "message" }
+ *
+ * // Logging the current stack trace, for debugging:
+ * debugLog(error = Throwable()) { "message" }
+ * ```
+ */
+object DebugLogger {
+
+ /**
+ * Log a debug message, with sensible defaults.
+ *
+ * For example:
+ * ```kotlin
+ * val one = 1
+ * debugLog { "message#$one" }
+ * ```
+ *
+ * The output will be: `D/NoteTaskController: message#1`
+ *
+ * Beware, the [debugLog] content is **REMOVED FROM SOURCE AND BINARY** in Release builds.
+ *
+ * @param enabled: whether or not the message should be logged. By default, it is
+ * [Build.IS_DEBUGGABLE].
+ * @param priority: type of this log. By default, it is [Log.DEBUG].
+ * @param tag: identifies the source of a log. By default, it is the receiver's simple name.
+ * @param error: a [Throwable] to log.
+ * @param message: a lazily evaluated message you wish to log.
+ */
+ inline fun Any.debugLog(
+ enabled: Boolean = Build.IS_DEBUGGABLE,
+ priority: Int = Log.DEBUG,
+ tag: String = this::class.simpleName.orEmpty(),
+ error: Throwable? = null,
+ message: () -> String,
+ ) {
+ if (enabled) {
+ if (error == null) {
+ Log.println(priority, tag, message())
+ } else {
+ Log.printlns(LOG_ID_MAIN, priority, tag, message(), error)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt
new file mode 100644
index 0000000..2764a1f
--- /dev/null
+++ b/packages/SystemUI/src-release/com/android/systemui/log/DebugLogger.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log
+
+import android.os.Build
+import android.util.Log
+
+/** An empty logger for release builds. */
+object DebugLogger {
+
+ @JvmName("logcatMessage")
+ inline fun Any.debugLog(
+ enabled: Boolean = Build.IS_DEBUGGABLE,
+ priority: Int = Log.DEBUG,
+ tag: String = this::class.simpleName.orEmpty(),
+ error: Throwable? = null,
+ message: () -> String,
+ ) {
+ // no-op.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a6c782d..beb0949 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -15,6 +15,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import com.android.keyguard.clock.DefaultClockController;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -46,6 +47,9 @@
public static final int LARGE = 0;
public static final int SMALL = 1;
+ // compensate for translation of parents subject to device screen
+ // In this case, the translation comes from KeyguardStatusView
+ public int screenOffsetYPadding = 0;
/** Returns a region for the large clock to position itself, based on the given parent. */
public static Rect getLargeClockRegion(ViewGroup parent) {
@@ -161,8 +165,18 @@
}
if (mLargeClockFrame.isLaidOut()) {
- mClock.getLargeClock().getEvents().onTargetRegionChanged(
- getLargeClockRegion(mLargeClockFrame));
+ Rect targetRegion = getLargeClockRegion(mLargeClockFrame);
+ if (mClock instanceof DefaultClockController) {
+ mClock.getLargeClock().getEvents().onTargetRegionChanged(
+ targetRegion);
+ } else {
+ mClock.getLargeClock().getEvents().onTargetRegionChanged(
+ new Rect(
+ targetRegion.left,
+ targetRegion.top - screenOffsetYPadding,
+ targetRegion.right,
+ targetRegion.bottom - screenOffsetYPadding));
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index a34c9fa..aee9abc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -169,6 +169,14 @@
}
/**
+ * Used for status view to pass the screen offset from parent view
+ */
+ public void setLockscreenClockY(int clockY) {
+ mView.screenOffsetYPadding = clockY;
+ mView.updateClockTargetRegions();
+ }
+
+ /**
* Attach the controller to the view it relates to.
*/
@Override
@@ -394,13 +402,6 @@
PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
x, props, animate);
}
-
- }
-
- void updateKeyguardStatusViewOffset() {
- // updateClockTargetRegions will call onTargetRegionChanged
- // which will require the correct translationY property of keyguardStatusView after updating
- mView.updateClockTargetRegions();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index edfcb8d..68d8143 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -215,6 +215,15 @@
}
/**
+ * Pass top margin from ClockPositionAlgorithm in NotificationPanelViewController
+ * Use for clock view in LS to compensate for top margin to align to the screen
+ * Regardless of translation from AOD and unlock gestures
+ */
+ public void setLockscreenClockY(int clockY) {
+ mKeyguardClockSwitchController.setLockscreenClockY(clockY);
+ }
+
+ /**
* Set whether the view accessibility importance mode.
*/
public void setStatusAccessibilityImportance(int mode) {
@@ -230,7 +239,6 @@
* Update position of the view with an optional animation
*/
public void updatePosition(int x, int y, float scale, boolean animate) {
- float oldY = mView.getY();
setProperty(AnimatableProperty.Y, y, animate);
ClockController clock = mKeyguardClockSwitchController.getClock();
@@ -246,10 +254,6 @@
setProperty(AnimatableProperty.SCALE_X, 1f, animate);
setProperty(AnimatableProperty.SCALE_Y, 1f, animate);
}
-
- if (oldY != y) {
- mKeyguardClockSwitchController.updateKeyguardStatusViewOffset();
- }
}
/**
@@ -363,8 +367,6 @@
} else {
View clockView = clockContainerView.getChildAt(0);
- transition.excludeTarget(clockView, /* exclude= */ true);
-
TransitionSet set = new TransitionSet();
set.addTransition(transition);
@@ -389,8 +391,9 @@
@VisibleForTesting
static class SplitShadeTransitionAdapter extends Transition {
- private static final String PROP_BOUNDS = "splitShadeTransitionAdapter:bounds";
- private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS };
+ private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft";
+ private static final String PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow";
+ private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW};
private final KeyguardClockSwitchController mController;
@@ -400,12 +403,10 @@
}
private void captureValues(TransitionValues transitionValues) {
- Rect boundsRect = new Rect();
- boundsRect.left = transitionValues.view.getLeft();
- boundsRect.top = transitionValues.view.getTop();
- boundsRect.right = transitionValues.view.getRight();
- boundsRect.bottom = transitionValues.view.getBottom();
- transitionValues.values.put(PROP_BOUNDS, boundsRect);
+ transitionValues.values.put(PROP_BOUNDS_LEFT, transitionValues.view.getLeft());
+ int[] locationInWindowTmp = new int[2];
+ transitionValues.view.getLocationInWindow(locationInWindowTmp);
+ transitionValues.values.put(PROP_X_IN_WINDOW, locationInWindowTmp[0]);
}
@Override
@@ -427,8 +428,12 @@
}
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- Rect from = (Rect) startValues.values.get(PROP_BOUNDS);
- Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
+ int fromLeft = (int) startValues.values.get(PROP_BOUNDS_LEFT);
+ int fromWindowX = (int) startValues.values.get(PROP_X_IN_WINDOW);
+ int toWindowX = (int) endValues.values.get(PROP_X_IN_WINDOW);
+ // Using windowX, to determine direction, instead of left, as in RTL the difference of
+ // toLeft - fromLeft is always positive, even when moving left.
+ int direction = toWindowX - fromWindowX > 0 ? 1 : -1;
anim.addUpdateListener(animation -> {
ClockController clock = mController.getClock();
@@ -437,7 +442,7 @@
}
clock.getLargeClock().getAnimations()
- .onPositionUpdated(from, to, animation.getAnimatedFraction());
+ .onPositionUpdated(fromLeft, direction, animation.getAnimatedFraction());
});
return anim;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2c669bb..1f1d3b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -396,6 +396,7 @@
private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
+ private boolean mIsFaceEnrolled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private int mPostureState = DEVICE_POSTURE_UNKNOWN;
private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
@@ -521,6 +522,14 @@
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTrustChanged(userId);
+ }
+ }
+
if (enabled) {
String message = null;
if (KeyguardUpdateMonitor.getCurrentUser() == userId
@@ -559,14 +568,6 @@
}
}
}
-
- mLogger.logTrustChanged(wasTrusted, enabled, userId);
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onTrustChanged(userId);
- }
- }
}
/**
@@ -2572,6 +2573,16 @@
}
}
+ private void updateFaceEnrolled(int userId) {
+ final Boolean isFaceEnrolled = isFaceSupported()
+ && mBiometricEnabledForUser.get(userId)
+ && mAuthController.isFaceAuthEnrolled(userId);
+ if (mIsFaceEnrolled != isFaceEnrolled) {
+ mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
+ }
+ mIsFaceEnrolled = isFaceEnrolled;
+ }
+
private boolean isFaceSupported() {
return mFaceManager != null && !mFaceSensorProperties.isEmpty();
}
@@ -2611,17 +2622,10 @@
}
/**
- * @return true if there's at least one face enrolled for the given user
- */
- private boolean isFaceEnrolled(int userId) {
- return mAuthController.isFaceAuthEnrolled(userId);
- }
-
- /**
- * @return true if there's at least one face enrolled for the current user
+ * @return true if there's at least one face enrolled
*/
public boolean isFaceEnrolled() {
- return isFaceEnrolled(getCurrentUser());
+ return mIsFaceEnrolled;
}
private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() {
@@ -3280,13 +3284,14 @@
@SuppressLint("MissingPermission")
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
- boolean newFpPossible = isFingerprintSupported()
- && !isFingerprintDisabled(userId) && mAuthController.isFingerprintEnrolled(userId);
- Boolean oldFpPossible = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
- if (oldFpPossible != newFpPossible) {
- mLogger.logFpPossibleUpdated(userId, oldFpPossible, newFpPossible);
+ // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
+ boolean newFpEnrolled = isFingerprintSupported()
+ && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+ Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+ if (oldFpEnrolled != newFpEnrolled) {
+ mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
}
- mIsUnlockWithFingerprintPossible.put(userId, newFpPossible);
+ mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
return mIsUnlockWithFingerprintPossible.get(userId);
}
@@ -3301,13 +3306,24 @@
/**
* @deprecated This is being migrated to use modern architecture.
*/
- @VisibleForTesting
@Deprecated
- public boolean isUnlockWithFacePossible(int userId) {
+ private boolean isUnlockWithFacePossible(int userId) {
if (isFaceAuthInteractorEnabled()) {
return getFaceAuthInteractor().canFaceAuthRun();
}
- return isFaceSupported() && isFaceEnrolled(userId) && !isFaceDisabled(userId);
+ return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
+ }
+
+ /**
+ * If face hardware is available, user has enrolled and enabled auth via setting.
+ *
+ * @deprecated This is being migrated to use modern architecture.
+ */
+ @Deprecated
+ public boolean isFaceAuthEnabledForUser(int userId) {
+ // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
+ updateFaceEnrolled(userId);
+ return mIsFaceEnrolled;
}
private void stopListeningForFingerprint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index fe40145..1661806 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -630,7 +630,7 @@
)
}
- fun logFpPossibleUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+ fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
logBuffer.log(
TAG,
DEBUG,
@@ -639,7 +639,7 @@
bool1 = oldValue
bool2 = newValue
},
- { "Fp possible state changed for userId: $int1 old: $bool1, new: $bool2" }
+ { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..1f66c91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 44215
+
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index f3c71da..4158390 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -45,6 +45,8 @@
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
+import com.android.internal.accessibility.util.AccessibilityUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
@@ -520,8 +522,11 @@
SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
- private void handleHeadsetHook() {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+ @VisibleForTesting
+ void handleHeadsetHook() {
+ if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+ }
}
private void handleAccessibilityButton() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 7bfd84e..155c26d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -463,13 +463,17 @@
if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0
|| (configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0
|| (configDiff & ActivityInfo.CONFIG_FONT_SCALE) != 0
+ || (configDiff & ActivityInfo.CONFIG_LOCALE) != 0
|| (configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
// We listen to following config changes to trigger layout inflation:
// CONFIG_UI_MODE: theme change
// CONFIG_ASSETS_PATHS: wallpaper change
// CONFIG_FONT_SCALE: font size change
+ // CONFIG_LOCALE: language change
// CONFIG_DENSITY: display size change
+ mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
+
boolean showSettingPanelAfterThemeChange = mIsVisible;
hideSettingPanel(/* resetPosition= */ false);
inflateView();
@@ -490,11 +494,6 @@
+ mDraggableWindowBounds.top;
return;
}
-
- if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
- updateAccessibilityWindowTitle();
- return;
- }
}
private void onWindowInsetChanged() {
@@ -515,13 +514,6 @@
}
}
- private void updateAccessibilityWindowTitle() {
- mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
- if (mIsVisible) {
- mWindowManager.updateViewLayout(mSettingView, mParams);
- }
- }
-
public void editMagnifierSizeMode(boolean enable) {
setEditMagnifierSizeMode(enable);
updateSelectedButton(MagnificationSize.NONE);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 4b5c50f..5499d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -19,7 +19,6 @@
import android.annotation.RawRes
import android.content.Context
import android.content.Context.FINGERPRINT_SERVICE
-import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager
import android.view.DisplayInfo
import android.view.Surface
@@ -35,21 +34,18 @@
import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
-import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
-import com.android.systemui.unfold.updates.FoldProvider
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
iconView: LottieAnimationView,
protected val iconViewOverlay: LottieAnimationView
-) : AuthIconController(context, iconView), FoldProvider.FoldCallback {
+) : AuthIconController(context, iconView) {
- private var isDeviceFolded: Boolean = false
private val isSideFps: Boolean
private val isReverseDefaultRotation =
context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
- private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
+
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
@@ -77,20 +73,16 @@
if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) {
iconView.rotation = 180f
}
- screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
- screenSizeFoldProvider.onConfigurationChange(context.resources.configuration)
}
private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
val displayInfo = DisplayInfo()
context.display?.getDisplayInfo(displayInfo)
val rotation = getRotationFromDefault(displayInfo.rotation)
- val iconAnimation = getSideFpsAnimationForTransition(rotation)
val iconViewOverlayAnimation =
getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
- iconView.setAnimation(iconAnimation)
iconViewOverlay.setAnimation(iconViewOverlayAnimation)
}
@@ -132,10 +124,6 @@
LottieColorUtils.applyDynamicColors(context, iconView)
}
- override fun onConfigurationChanged(newConfig: Configuration) {
- screenSizeFoldProvider.onConfigurationChange(newConfig)
- }
-
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
if (isSideFps) {
updateIconSideFps(lastState, newState)
@@ -230,25 +218,6 @@
if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
@RawRes
- private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
- Surface.ROTATION_90 -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_topleft
- } else {
- R.raw.biometricprompt_portrait_base_topleft
- }
- Surface.ROTATION_270 -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_bottomright
- } else {
- R.raw.biometricprompt_portrait_base_bottomright
- }
- else -> if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_default
- } else {
- R.raw.biometricprompt_landscape_base
- }
- }
-
- @RawRes
private fun getSideFpsOverlayAnimationForTransition(
@BiometricState oldState: Int,
@BiometricState newState: Int,
@@ -357,8 +326,4 @@
)
}
}
-
- override fun onFoldUpdated(isFolded: Boolean) {
- isDeviceFolded = isFolded
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e04dd06..f330c34 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -124,6 +124,7 @@
protected final int mTextColorHint;
private AuthPanelController mPanelController;
+
private PromptInfo mPromptInfo;
private boolean mRequireConfirmation;
private int mUserId;
@@ -266,11 +267,9 @@
/** Create the controller for managing the icons transitions during the prompt.*/
@NonNull
protected abstract AuthIconController createIconController();
-
void setPanelController(AuthPanelController panelController) {
mPanelController = panelController;
}
-
void setPromptInfo(PromptInfo promptInfo) {
mPromptInfo = promptInfo;
}
@@ -463,9 +462,16 @@
return false;
}
+ /**
+ * Updates mIconView animation on updates to fold state, device rotation, or rear display mode
+ * @param animation new asset to use for iconw
+ */
+ public void updateIconViewAnimation(int animation) {
+ mIconView.setAnimation(animation);
+ }
+
public void updateState(@BiometricState int newState) {
Log.v(TAG, "newState: " + newState);
-
mIconController.updateState(mState, newState);
switch (newState) {
@@ -625,7 +631,6 @@
public void restoreState(@Nullable Bundle savedState) {
mSavedState = savedState;
}
-
private void setTextOrHide(TextView view, CharSequence charSequence) {
if (TextUtils.isEmpty(charSequence)) {
view.setVisibility(View.GONE);
@@ -658,7 +663,6 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mIconController.onConfigurationChanged(newConfig);
if (mSavedState != null) {
updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index aeebb01..b386bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -67,6 +67,8 @@
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.ui.CredentialView;
+import com.android.systemui.biometrics.ui.binder.AuthBiometricFingerprintViewBinder;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -126,6 +128,8 @@
// TODO: these should be migrated out once ready
private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+ private final Provider<AuthBiometricFingerprintViewModel>
+ mAuthBiometricFingerprintViewModelProvider;
private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@VisibleForTesting final BiometricCallback mBiometricCallback;
@@ -241,12 +245,14 @@
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider) {
mConfig.mSensorIds = sensorIds;
return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
panelInteractionDetector, userManager, lockPatternUtils, jankMonitor,
- biometricPromptInteractor, credentialViewModelProvider,
- new Handler(Looper.getMainLooper()), bgExecutor);
+ biometricPromptInteractor, authBiometricFingerprintViewModelProvider,
+ credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor);
}
}
@@ -339,6 +345,8 @@
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull Handler mainHandler,
@NonNull @Background DelayableExecutor bgExecutor) {
@@ -368,6 +376,7 @@
mBackgroundExecutor = bgExecutor;
mInteractionJankMonitor = jankMonitor;
mBiometricPromptInteractor = biometricPromptInteractor;
+ mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
// Inflate biometric view only if necessary.
@@ -386,6 +395,9 @@
fingerprintAndFaceView.updateOverrideIconLayoutParamsSize();
fingerprintAndFaceView.setFaceClass3(
faceProperties.sensorStrength == STRENGTH_STRONG);
+ final AuthBiometricFingerprintViewModel fpAndFaceViewModel =
+ mAuthBiometricFingerprintViewModelProvider.get();
+ AuthBiometricFingerprintViewBinder.bind(fingerprintAndFaceView, fpAndFaceViewModel);
mBiometricView = fingerprintAndFaceView;
} else if (fpProperties != null) {
final AuthBiometricFingerprintView fpView =
@@ -394,6 +406,9 @@
fpView.setSensorProperties(fpProperties);
fpView.setScaleFactorProvider(config.mScaleProvider);
fpView.updateOverrideIconLayoutParamsSize();
+ final AuthBiometricFingerprintViewModel fpViewModel =
+ mAuthBiometricFingerprintViewModelProvider.get();
+ AuthBiometricFingerprintViewBinder.bind(fpView, fpViewModel);
mBiometricView = fpView;
} else if (faceProperties != null) {
mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 6eb3c70..3579e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -73,6 +73,7 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -127,6 +128,9 @@
// TODO: these should be migrated out once ready
@NonNull private final Provider<BiometricPromptCredentialInteractor> mBiometricPromptInteractor;
+
+ @NonNull private final Provider<AuthBiometricFingerprintViewModel>
+ mAuthBiometricFingerprintViewModelProvider;
@NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@NonNull private final LogContextInteractor mLogContextInteractor;
@@ -722,7 +726,6 @@
}
onDialogDismissed(reason);
}
-
@Inject
public AuthController(Context context,
Execution execution,
@@ -741,6 +744,8 @@
@NonNull UdfpsLogger udfpsLogger,
@NonNull LogContextInteractor logContextInteractor,
@NonNull Provider<BiometricPromptCredentialInteractor> biometricPromptInteractor,
+ @NonNull Provider<AuthBiometricFingerprintViewModel>
+ authBiometricFingerprintViewModelProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull InteractionJankMonitor jankMonitor,
@Main Handler handler,
@@ -771,6 +776,7 @@
mLogContextInteractor = logContextInteractor;
mBiometricPromptInteractor = biometricPromptInteractor;
+ mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
mOrientationListener = new BiometricDisplayListener(
@@ -1299,7 +1305,7 @@
.build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle,
panelInteractionDetector, userManager, lockPatternUtils,
mInteractionJankMonitor, mBiometricPromptInteractor,
- mCredentialViewModelProvider);
+ mAuthBiometricFingerprintViewModelProvider, mCredentialViewModelProvider);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
index d6ad4da..f5f4640 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
@@ -18,7 +18,6 @@
import android.annotation.DrawableRes
import android.content.Context
-import android.content.res.Configuration
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@@ -94,8 +93,6 @@
/** Called during [onAnimationEnd] if the controller is not [deactivated]. */
open fun handleAnimationEnd(drawable: Drawable) {}
- open fun onConfigurationChanged(newConfig: Configuration) {}
-
// TODO(b/251476085): Migrate this to an extension at the appropriate level?
/** Load the given [rawResources] immediately so they are cached for use in the [context]. */
protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 4319f01..962140f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -55,6 +55,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -84,6 +85,7 @@
private val activityTaskManager: ActivityTaskManager,
overviewProxyService: OverviewProxyService,
displayManager: DisplayManager,
+ private val displayStateInteractor: DisplayStateInteractor,
@Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -203,14 +205,16 @@
request: SideFpsUiRequestSource,
@BiometricOverlayConstants.ShowReason reason: Int = BiometricOverlayConstants.REASON_UNKNOWN
) {
- requests.add(request)
- mainExecutor.execute {
- if (overlayView == null) {
- traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
- createOverlayForDisplay(reason)
+ if (!displayStateInteractor.isInRearDisplayMode.value) {
+ requests.add(request)
+ mainExecutor.execute {
+ if (overlayView == null) {
+ traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
+ createOverlayForDisplay(reason)
+ }
+ } else {
+ Log.v(TAG, "overlay already shown")
}
- } else {
- Log.v(TAG, "overlay already shown")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 3b50bbc..935de02 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -464,7 +464,12 @@
// Fade out the icon if we are animating an activity launch over the lockscreen and the
// activity didn't request the UDFPS.
if (isLaunchingActivity && !udfpsRequested) {
- alpha = (alpha * (1.0f - activityLaunchProgress)).toInt()
+ val udfpsActivityLaunchAlphaMultiplier =
+ 1f -
+ (activityLaunchProgress *
+ (ActivityLaunchAnimator.TIMINGS.totalDuration / 83))
+ .coerceIn(0f, 1f)
+ alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
}
// Fade out alpha when a dialog is shown
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 67d2f30..c831663 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -17,10 +17,16 @@
package com.android.systemui.biometrics.dagger
import com.android.settingslib.udfps.UdfpsUtils
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepositoryImpl
import com.android.systemui.biometrics.data.repository.PromptRepository
import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
import com.android.systemui.biometrics.domain.interactor.CredentialInteractor
import com.android.systemui.biometrics.domain.interactor.CredentialInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor
import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.dagger.SysUISingleton
@@ -41,10 +47,22 @@
@Binds
@SysUISingleton
+ fun fingerprintRepository(impl: FingerprintPropertyRepositoryImpl):
+ FingerprintPropertyRepository
+ @Binds
+ @SysUISingleton
+ fun rearDisplayStateRepository(impl: RearDisplayStateRepositoryImpl): RearDisplayStateRepository
+
+ @Binds
+ @SysUISingleton
fun providesCredentialInteractor(impl: CredentialInteractorImpl): CredentialInteractor
@Binds
@SysUISingleton
+ fun providesDisplayStateInteractor(impl: DisplayStateInteractorImpl): DisplayStateInteractor
+
+ @Binds
+ @SysUISingleton
fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
new file mode 100644
index 0000000..33fb36c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.biometrics.data.repository
+
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.shareIn
+
+/**
+ * A repository for the global state of FingerprintProperty.
+ *
+ * There is never more than one instance of the FingerprintProperty at any given time.
+ */
+interface FingerprintPropertyRepository {
+
+ /**
+ * If the repository is initialized or not. Other properties are defaults until this is true.
+ */
+ val isInitialized: Flow<Boolean>
+
+ /** The id of fingerprint sensor. */
+ val sensorId: StateFlow<Int>
+
+ /** The security strength of sensor (convenience, weak, strong). */
+ val strength: StateFlow<SensorStrength>
+
+ /** The types of fingerprint sensor (rear, ultrasonic, optical, etc.). */
+ val sensorType: StateFlow<FingerprintSensorType>
+
+ /** The primary sensor location relative to the default display. */
+ val sensorLocation: StateFlow<SensorLocationInternal>
+
+ // TODO(b/251476085): don't implement until we need it, but expose alternative locations as
+ // a map of display id -> location or similar.
+ /** The sensor location relative to each physical display. */
+ // val sensorLocations<Map<String, SensorLocationInternal>>
+}
+
+@SysUISingleton
+class FingerprintPropertyRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val fingerprintManager: FingerprintManager
+) : FingerprintPropertyRepository {
+
+ override val isInitialized: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ override fun onAllAuthenticatorsRegistered(
+ sensors: List<FingerprintSensorPropertiesInternal>
+ ) {
+ if (sensors.isNotEmpty()) {
+ setProperties(sensors[0])
+ trySendWithFailureLogging(true, TAG, "initialize properties")
+ }
+ }
+ }
+ fingerprintManager.addAuthenticatorsRegisteredCallback(callback)
+ trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
+ awaitClose {}
+ }
+ .shareIn(scope = applicationScope, started = SharingStarted.Eagerly, replay = 1)
+
+ private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
+ override val sensorId: StateFlow<Int> = _sensorId.asStateFlow()
+
+ private val _strength: MutableStateFlow<SensorStrength> =
+ MutableStateFlow(SensorStrength.CONVENIENCE)
+ override val strength = _strength.asStateFlow()
+
+ private val _sensorType: MutableStateFlow<FingerprintSensorType> =
+ MutableStateFlow(FingerprintSensorType.UNKNOWN)
+ override val sensorType = _sensorType.asStateFlow()
+
+ private val _sensorLocation: MutableStateFlow<SensorLocationInternal> =
+ MutableStateFlow(SensorLocationInternal.DEFAULT)
+ override val sensorLocation = _sensorLocation.asStateFlow()
+
+ private fun setProperties(prop: FingerprintSensorPropertiesInternal) {
+ _sensorId.value = prop.sensorId
+ _strength.value = sensorStrengthIntToObject(prop.sensorStrength)
+ _sensorType.value = sensorTypeIntToObject(prop.sensorType)
+ _sensorLocation.value = prop.location
+ }
+
+ companion object {
+ private const val TAG = "FingerprintPropertyRepositoryImpl"
+ }
+}
+
+private fun sensorStrengthIntToObject(value: Int): SensorStrength {
+ return when (value) {
+ 0 -> SensorStrength.CONVENIENCE
+ 1 -> SensorStrength.WEAK
+ 2 -> SensorStrength.STRONG
+ else -> throw IllegalArgumentException("Invalid SensorStrength value: $value")
+ }
+}
+
+private fun sensorTypeIntToObject(value: Int): FingerprintSensorType {
+ return when (value) {
+ 0 -> FingerprintSensorType.UNKNOWN
+ 1 -> FingerprintSensorType.REAR
+ 2 -> FingerprintSensorType.UDFPS_ULTRASONIC
+ 3 -> FingerprintSensorType.UDFPS_OPTICAL
+ 4 -> FingerprintSensorType.POWER_BUTTON
+ 5 -> FingerprintSensorType.HOME_BUTTON
+ else -> throw IllegalArgumentException("Invalid SensorType value: $value")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
new file mode 100644
index 0000000..d17d961
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepository.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.biometrics.data.repository
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.internal.util.ArrayUtils
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Provide current rear display state. */
+interface RearDisplayStateRepository {
+ /** Provides the current rear display state. */
+ val isInRearDisplayMode: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class RearDisplayStateRepositoryImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ @Application context: Context,
+ deviceStateManager: DeviceStateManager,
+ @Main mainExecutor: Executor
+) : RearDisplayStateRepository {
+ override val isInRearDisplayMode: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val sendRearDisplayStateUpdate = { state: Boolean ->
+ trySendWithFailureLogging(
+ state,
+ TAG,
+ "Error sending rear display state update to $state"
+ )
+ }
+
+ val callback =
+ DeviceStateManager.DeviceStateCallback { state ->
+ val isInRearDisplayMode =
+ ArrayUtils.contains(
+ context.resources.getIntArray(
+ com.android.internal.R.array.config_rearDisplayDeviceStates
+ ),
+ state
+ )
+ sendRearDisplayStateUpdate(isInRearDisplayMode)
+ }
+
+ sendRearDisplayStateUpdate(false)
+ deviceStateManager.registerCallback(mainExecutor, callback)
+ awaitClose { deviceStateManager.unregisterCallback(callback) }
+ }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ companion object {
+ const val TAG = "RearDisplayStateRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
new file mode 100644
index 0000000..c935aa2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Aggregates display state information. */
+interface DisplayStateInteractor {
+
+ /** Whether the device is currently in rear display mode. */
+ val isInRearDisplayMode: StateFlow<Boolean>
+
+ /** Whether the device is currently folded. */
+ val isFolded: Flow<Boolean>
+
+ /** Called on configuration changes, used to keep the display state in sync */
+ fun onConfigurationChanged(newConfig: Configuration)
+}
+
+/** Encapsulates logic for interacting with the display state. */
+class DisplayStateInteractorImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ @Application context: Context,
+ @Main mainExecutor: Executor,
+ rearDisplayStateRepository: RearDisplayStateRepository,
+) : DisplayStateInteractor {
+ private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
+
+ fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) {
+ screenSizeFoldProvider = foldProvider
+ }
+
+ override val isFolded: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val sendFoldStateUpdate = { state: Boolean ->
+ trySendWithFailureLogging(
+ state,
+ TAG,
+ "Error sending fold state update to $state"
+ )
+ }
+
+ val callback =
+ object : FoldProvider.FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ sendFoldStateUpdate(isFolded)
+ }
+ }
+ sendFoldStateUpdate(false)
+ screenSizeFoldProvider.registerCallback(callback, mainExecutor)
+ awaitClose { screenSizeFoldProvider.unregisterCallback(callback) }
+ }
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ )
+
+ override val isInRearDisplayMode: StateFlow<Boolean> =
+ rearDisplayStateRepository.isInRearDisplayMode
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ screenSizeFoldProvider.onConfigurationChange(newConfig)
+ }
+
+ companion object {
+ private const val TAG = "DisplayStateInteractor"
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
similarity index 61%
copy from services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
copy to packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 7c339d2..df5cefd 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.server.companion.datatransfer.contextsync;
+package com.android.systemui.biometrics.shared.model
-/** Callback for call metadata syncing. */
-public abstract class CallMetadataSyncCallback {
+import android.hardware.fingerprint.FingerprintSensorProperties
- abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
-
- abstract void requestCrossDeviceSync(int userId);
-
- abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
+/** Fingerprint sensor types. Represents [FingerprintSensorProperties.SensorType]. */
+enum class FingerprintSensorType {
+ UNKNOWN,
+ REAR,
+ UDFPS_ULTRASONIC,
+ UDFPS_OPTICAL,
+ POWER_BUTTON,
+ HOME_BUTTON,
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
similarity index 61%
copy from services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
copy to packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
index 7c339d2..2982d0b 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorStrength.kt
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.server.companion.datatransfer.contextsync;
+package com.android.systemui.biometrics.shared.model
-/** Callback for call metadata syncing. */
-public abstract class CallMetadataSyncCallback {
+import android.hardware.biometrics.SensorProperties
- abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
-
- abstract void requestCrossDeviceSync(int userId);
-
- abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
+/** Fingerprint sensor security strength. Represents [SensorProperties.Strength]. */
+enum class SensorStrength {
+ CONVENIENCE,
+ WEAK,
+ STRONG,
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index bcc0575..ede62ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -65,7 +65,7 @@
super.onLayout(changed, left, top, right, bottom)
val inputLeftBound: Int
- val inputTopBound: Int
+ var inputTopBound: Int
var headerRightBound = right
var headerTopBounds = top
val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
@@ -75,14 +75,23 @@
inputLeftBound = (right - left) / 2
headerRightBound = inputLeftBound
headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
+
+ if (descriptionView.bottom > bottomInset) {
+ credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
+ }
} else {
inputTopBound = descBottom + (bottom - descBottom - credentialInput.height) / 2
inputLeftBound = (right - left - credentialInput.width) / 2
+
+ if (bottom - inputTopBound < credentialInput.height) {
+ inputTopBound = bottom - credentialInput.height
+ }
+
+ if (descriptionView.bottom > inputTopBound) {
+ credentialHeader.layout(left, headerTopBounds, headerRightBound, inputTopBound)
+ }
}
- if (descriptionView.bottom > bottomInset) {
- credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
- }
credentialInput.layout(inputLeftBound, inputTopBound, right, bottom)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
new file mode 100644
index 0000000..e776ab4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.biometrics.ui.binder
+
+import android.view.Surface
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.biometrics.AuthBiometricFingerprintView
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.launch
+
+object AuthBiometricFingerprintViewBinder {
+
+ /** Binds a [AuthBiometricFingerprintView] to a [AuthBiometricFingerprintViewModel]. */
+ @JvmStatic
+ fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.onConfigurationChanged(view.context.resources.configuration)
+ viewModel.setRotation(view.context.display?.orientation ?: Surface.ROTATION_0)
+ launch {
+ viewModel.iconAsset.collect { iconAsset ->
+ view.updateIconViewAnimation(iconAsset)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
new file mode 100644
index 0000000..617d80c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.biometrics.ui.viewmodel
+
+import android.annotation.RawRes
+import android.content.res.Configuration
+import android.view.Surface
+import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Models UI of AuthBiometricFingerprintView to support rear display state changes. */
+class AuthBiometricFingerprintViewModel
+@Inject
+constructor(private val interactor: DisplayStateInteractor) {
+ /** Current device rotation. */
+ private var rotation: Int = Surface.ROTATION_0
+
+ /** Current AuthBiometricFingerprintView asset. */
+ val iconAsset: Flow<Int> =
+ combine(interactor.isFolded, interactor.isInRearDisplayMode) {
+ isFolded: Boolean,
+ isInRearDisplayMode: Boolean ->
+ getSideFpsAnimationAsset(isFolded, isInRearDisplayMode)
+ }
+
+ @RawRes
+ private fun getSideFpsAnimationAsset(
+ isDeviceFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ ): Int =
+ when (rotation) {
+ Surface.ROTATION_90 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_reverse_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_topleft
+ } else {
+ R.raw.biometricprompt_portrait_base_topleft
+ }
+ Surface.ROTATION_270 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_bottomright
+ } else {
+ R.raw.biometricprompt_portrait_base_bottomright
+ }
+ else ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_landscape_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
+ }
+ }
+
+ /** Called on configuration changes */
+ fun onConfigurationChanged(newConfig: Configuration) {
+ interactor.onConfigurationChanged(newConfig)
+ }
+
+ fun setRotation(newRotation: Int) {
+ rotation = newRotation
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
new file mode 100644
index 0000000..0542e13
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.clipboardoverlay
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.net.Uri
+import android.util.Log
+import android.util.Size
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.IOException
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+
+class ClipboardImageLoader
+@Inject
+constructor(
+ private val context: Context,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ @Application private val mainScope: CoroutineScope
+) {
+ private val TAG: String = "ClipboardImageLoader"
+
+ suspend fun load(uri: Uri, timeoutMs: Long = 300) =
+ withTimeoutOrNull(timeoutMs) {
+ withContext(bgDispatcher) {
+ try {
+ val size = context.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+ context.contentResolver.loadThumbnail(uri, Size(size, size * 4), null)
+ } catch (e: IOException) {
+ Log.e(TAG, "Thumbnail loading failed!", e)
+ null
+ }
+ }
+ }
+
+ fun loadAsync(uri: Uri, callback: Consumer<Bitmap?>) {
+ mainScope.launch { callback.accept(load(uri)) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 0aeab10..757ebf4 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -32,6 +32,7 @@
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
+import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -90,6 +91,7 @@
private final ClipboardOverlayUtils mClipboardUtils;
private final FeatureFlags mFeatureFlags;
private final Executor mBgExecutor;
+ private final ClipboardImageLoader mClipboardImageLoader;
private final ClipboardOverlayView mView;
@@ -109,6 +111,7 @@
private Runnable mOnUiUpdate;
+ private boolean mShowingUi;
private boolean mIsMinimized;
private ClipboardModel mClipboardModel;
@@ -175,9 +178,11 @@
FeatureFlags featureFlags,
ClipboardOverlayUtils clipboardUtils,
@Background Executor bgExecutor,
+ ClipboardImageLoader clipboardImageLoader,
UiEventLogger uiEventLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
+ mClipboardImageLoader = clipboardImageLoader;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
@@ -260,21 +265,42 @@
boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
mClipboardModel = model;
mClipboardLogger.setClipSource(mClipboardModel.getSource());
- if (shouldAnimate) {
- reset();
- mClipboardLogger.setClipSource(mClipboardModel.getSource());
- if (shouldShowMinimized(mWindow.getWindowInsets())) {
- mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED);
- mIsMinimized = true;
- mView.setMinimized(true);
- } else {
- mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED);
+ if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) {
+ if (shouldAnimate) {
+ reset();
+ mClipboardLogger.setClipSource(mClipboardModel.getSource());
+ if (shouldShowMinimized(mWindow.getWindowInsets())) {
+ mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED);
+ mIsMinimized = true;
+ mView.setMinimized(true);
+ } else {
+ mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED);
+ setExpandedView(this::animateIn);
+ }
+ mView.announceForAccessibility(
+ getAccessibilityAnnouncement(mClipboardModel.getType()));
+ } else if (!mIsMinimized) {
+ setExpandedView(() -> {
+ });
+ }
+ } else {
+ if (shouldAnimate) {
+ reset();
+ mClipboardLogger.setClipSource(mClipboardModel.getSource());
+ if (shouldShowMinimized(mWindow.getWindowInsets())) {
+ mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_MINIMIZED);
+ mIsMinimized = true;
+ mView.setMinimized(true);
+ } else {
+ mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED);
+ setExpandedView();
+ animateIn();
+ }
+ mView.announceForAccessibility(
+ getAccessibilityAnnouncement(mClipboardModel.getType()));
+ } else if (!mIsMinimized) {
setExpandedView();
}
- animateIn();
- mView.announceForAccessibility(getAccessibilityAnnouncement(mClipboardModel.getType()));
- } else if (!mIsMinimized) {
- setExpandedView();
}
if (mClipboardModel.isRemote()) {
mTimeoutHandler.cancelTimeout();
@@ -285,6 +311,58 @@
}
}
+ private void setExpandedView(Runnable onViewReady) {
+ final ClipboardModel model = mClipboardModel;
+ mView.setMinimized(false);
+ switch (model.getType()) {
+ case TEXT:
+ if (model.isRemote() || DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
+ if (model.getTextLinks() != null) {
+ classifyText(model);
+ }
+ }
+ if (model.isSensitive()) {
+ mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true);
+ } else {
+ mView.showTextPreview(model.getText().toString(), false);
+ }
+ mView.setEditAccessibilityAction(true);
+ mOnPreviewTapped = this::editText;
+ onViewReady.run();
+ break;
+ case IMAGE:
+ mView.setEditAccessibilityAction(true);
+ mOnPreviewTapped = () -> editImage(model.getUri());
+ if (model.isSensitive()) {
+ mView.showImagePreview(null);
+ onViewReady.run();
+ } else {
+ mClipboardImageLoader.loadAsync(model.getUri(), (bitmap) -> mView.post(() -> {
+ if (bitmap == null) {
+ mView.showDefaultTextPreview();
+ } else {
+ mView.showImagePreview(bitmap);
+ }
+ onViewReady.run();
+ }));
+ }
+ break;
+ case URI:
+ case OTHER:
+ mView.showDefaultTextPreview();
+ onViewReady.run();
+ break;
+ }
+ if (!model.isRemote()) {
+ maybeShowRemoteCopy(model.getClipData());
+ }
+ if (model.getType() != ClipboardModel.Type.OTHER) {
+ mOnShareTapped = () -> shareContent(model.getClipData());
+ mView.showShareChip();
+ }
+ }
+
private void setExpandedView() {
final ClipboardModel model = mClipboardModel;
mView.setMinimized(false);
@@ -350,8 +428,12 @@
mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED);
mIsMinimized = false;
}
- setExpandedView();
- animateIn();
+ if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) {
+ setExpandedView(() -> animateIn());
+ } else {
+ setExpandedView();
+ animateIn();
+ }
}
});
mEnterAnimator.start();
@@ -412,7 +494,8 @@
mInputMonitor.getInputChannel(), Looper.getMainLooper()) {
@Override
public void onInputEvent(InputEvent event) {
- if (event instanceof MotionEvent) {
+ if ((!mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT) || mShowingUi)
+ && event instanceof MotionEvent) {
MotionEvent motionEvent = (MotionEvent) event;
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
if (!mView.isInTouchRegion(
@@ -452,6 +535,12 @@
mEnterAnimator = mView.getEnterAnimation();
mEnterAnimator.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mShowingUi = true;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mOnUiUpdate != null) {
@@ -518,6 +607,7 @@
mOnRemoteCopyTapped = null;
mOnShareTapped = null;
mOnPreviewTapped = null;
+ mShowingUi = false;
mView.reset();
mTimeoutHandler.cancelTimeout();
mClipboardLogger.reset();
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
index 016891d..a334c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
@@ -16,8 +16,6 @@
package com.android.systemui.complication;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
-
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
@@ -25,6 +23,7 @@
import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.condition.ConditionalCoreStartable;
@@ -33,7 +32,6 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* {@link ComplicationTypesUpdater} observes the state of available complication types set by the
@@ -53,7 +51,7 @@
@Main Executor executor,
SecureSettings secureSettings,
DreamOverlayStateController dreamOverlayStateController,
- @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ @SystemUser Monitor monitor) {
super(monitor);
mDreamBackend = dreamBackend;
mExecutor = executor;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
index 5020480..9c3448b 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamClockTimeComplication.java
@@ -18,11 +18,11 @@
import static com.android.systemui.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.view.View;
import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.condition.ConditionalCoreStartable;
@@ -74,7 +74,7 @@
public Registrant(
DreamOverlayStateController dreamOverlayStateController,
DreamClockTimeComplication dreamClockTimeComplication,
- @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ @SystemUser Monitor monitor) {
super(monitor);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = dreamClockTimeComplication;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index 8f192de..f973aee 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -21,7 +21,6 @@
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +39,7 @@
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsActivity;
import com.android.systemui.controls.ui.ControlsUiController;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.shared.condition.Monitor;
@@ -108,7 +108,7 @@
public Registrant(DreamHomeControlsComplication complication,
DreamOverlayStateController dreamOverlayStateController,
ControlsComponent controlsComponent,
- @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ @SystemUser Monitor monitor) {
super(monitor);
mComplication = complication;
mControlsComponent = controlsComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
index 2f5ef6d..b98794e 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
@@ -17,7 +17,6 @@
package com.android.systemui.complication;
import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_SMARTSPACE_LAYOUT_PARAMS;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.Context;
import android.os.Parcelable;
@@ -26,6 +25,7 @@
import android.widget.FrameLayout;
import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.flags.FeatureFlags;
@@ -88,7 +88,7 @@
DreamOverlayStateController dreamOverlayStateController,
SmartSpaceComplication smartSpaceComplication,
DreamSmartspaceController smartSpaceController,
- @Named(DREAM_PRETEXT_MONITOR) Monitor monitor,
+ @SystemUser Monitor monitor,
FeatureFlags featureFlags) {
super(monitor);
mDreamOverlayStateController = dreamOverlayStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 3d6d335..18bd467 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -41,7 +41,8 @@
private val context: Context,
@Background private val backgroundExecutor: DelayableExecutor,
private val lazyController: Lazy<ControlsController>,
- userTracker: UserTracker
+ private val packageUpdateMonitorFactory: PackageUpdateMonitor.Factory,
+ userTracker: UserTracker,
) : ControlsBindingController {
companion object {
@@ -93,7 +94,8 @@
backgroundExecutor,
actionCallbackService,
currentUser,
- component
+ component,
+ packageUpdateMonitorFactory
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 217f4d8..cb2476c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.controller
+import android.annotation.WorkerThread
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -23,7 +24,6 @@
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
-import android.os.RemoteException
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
@@ -38,6 +38,7 @@
import com.android.internal.annotations.GuardedBy
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
/**
* Manager for the lifecycle of the connection to a given [ControlsProviderService].
@@ -45,6 +46,9 @@
* This class handles binding and unbinding and requests to the service. The class will queue
* requests until the service is connected and dispatch them then.
*
+ * If the provider app is updated, and we are currently bound to it, it will try to rebind after
+ * update is completed.
+ *
* @property context A SystemUI context for binding to the services
* @property executor A delayable executor for posting timeouts
* @property actionCallbackService a callback interface to hand the remote service for sending
@@ -59,22 +63,22 @@
private val executor: DelayableExecutor,
private val actionCallbackService: IControlsActionCallback.Stub,
val user: UserHandle,
- val componentName: ComponentName
-) : IBinder.DeathRecipient {
+ val componentName: ComponentName,
+ packageUpdateMonitorFactory: PackageUpdateMonitor.Factory,
+) {
val token: IBinder = Binder()
private var requiresBound = false
@GuardedBy("queuedServiceMethods")
private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
private var wrapper: ServiceWrapper? = null
- private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
+ private var lastForPanel = false
+
companion object {
- private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT_SECONDS = 20L // seconds
- private const val MAX_BIND_RETRIES = 5
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_NOT_PERCEPTIBLE
@@ -91,60 +95,56 @@
})
}
- private fun bindService(bind: Boolean, forPanel: Boolean = false) {
- executor.execute {
- requiresBound = bind
- if (bind) {
- if (bindTryCount != MAX_BIND_RETRIES && wrapper == null) {
- if (DEBUG) {
- Log.d(TAG, "Binding service $intent")
- }
- bindTryCount++
- try {
- val flags = if (forPanel) BIND_FLAGS_PANEL else BIND_FLAGS
- val bound = context
- .bindServiceAsUser(intent, serviceConnection, flags, user)
- if (!bound) {
- context.unbindService(serviceConnection)
- }
- } catch (e: SecurityException) {
- Log.e(TAG, "Failed to bind to service", e)
- }
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "Unbinding service $intent")
- }
- bindTryCount = 0
- wrapper?.run {
- context.unbindService(serviceConnection)
- }
- wrapper = null
+ private val packageUpdateMonitor = packageUpdateMonitorFactory.create(
+ user,
+ componentName.packageName,
+ ) {
+ if (requiresBound) {
+ // Let's unbind just in case. onBindingDied should have been called and unbound before.
+ executor.execute {
+ unbindAndCleanup("package updated")
+ bindService(true, lastForPanel)
}
}
}
+ private fun bindService(bind: Boolean, forPanel: Boolean = false) {
+ executor.execute {
+ bindServiceBackground(bind, forPanel)
+ }
+ }
+
private val serviceConnection = object : ServiceConnection {
+
+ val connected = AtomicBoolean(false)
+
override fun onServiceConnected(name: ComponentName, service: IBinder) {
if (DEBUG) Log.d(TAG, "onServiceConnected $name")
- bindTryCount = 0
wrapper = ServiceWrapper(IControlsProvider.Stub.asInterface(service))
- try {
- service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
- } catch (_: RemoteException) {}
+ packageUpdateMonitor.startMonitoring()
handlePendingServiceMethods()
}
override fun onServiceDisconnected(name: ComponentName?) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected $name")
wrapper = null
- bindService(false)
+ // No need to call unbind. We may get a new `onServiceConnected`
}
override fun onNullBinding(name: ComponentName?) {
if (DEBUG) Log.d(TAG, "onNullBinding $name")
wrapper = null
- context.unbindService(this)
+ executor.execute {
+ unbindAndCleanup("null binding")
+ }
+ }
+
+ override fun onBindingDied(name: ComponentName?) {
+ super.onBindingDied(name)
+ if (DEBUG) Log.d(TAG, "onBindingDied $name")
+ executor.execute {
+ unbindAndCleanup("binder died")
+ }
}
}
@@ -159,14 +159,55 @@
}
}
- override fun binderDied() {
- if (wrapper == null) return
- wrapper = null
- if (requiresBound) {
- if (DEBUG) {
- Log.d(TAG, "binderDied")
+ @WorkerThread
+ private fun bindServiceBackground(bind: Boolean, forPanel: Boolean = true) {
+ requiresBound = bind
+ if (bind) {
+ if (wrapper == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Binding service $intent")
+ }
+ try {
+ lastForPanel = forPanel
+ val flags = if (forPanel) BIND_FLAGS_PANEL else BIND_FLAGS
+ var bound = false
+ if (serviceConnection.connected.compareAndSet(false, true)) {
+ bound = context
+ .bindServiceAsUser(intent, serviceConnection, flags, user)
+ }
+ if (!bound) {
+ Log.d(TAG, "Couldn't bind to $intent")
+ doUnbind()
+ }
+ } catch (e: SecurityException) {
+ Log.e(TAG, "Failed to bind to service", e)
+ // Couldn't even bind. Let's reset the connected value
+ serviceConnection.connected.set(false)
+ }
}
- // Try rebinding some time later
+ } else {
+ unbindAndCleanup("unbind requested")
+ packageUpdateMonitor.stopMonitoring()
+ }
+ }
+
+ @WorkerThread
+ private fun unbindAndCleanup(reason: String) {
+ if (DEBUG) {
+ Log.d(TAG, "Unbinding service $intent. Reason: $reason")
+ }
+ wrapper = null
+ try {
+ doUnbind()
+ } catch (e: IllegalArgumentException) {
+ Log.e(TAG, "Failed to unbind service", e)
+ }
+ }
+
+ @WorkerThread
+ private fun doUnbind() {
+ if (serviceConnection.connected.compareAndSet(true, false)) {
+ context.unbindService(serviceConnection)
}
}
@@ -313,7 +354,7 @@
fun run() {
if (!callWrapper()) {
queueServiceMethod(this)
- binderDied()
+ executor.execute { unbindAndCleanup("couldn't call through binder") }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt
new file mode 100644
index 0000000..1973b62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.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.controls.controller
+
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import com.android.internal.content.PackageMonitor
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.atomic.AtomicBoolean
+
+/** [PackageMonitor] that tracks when [packageName] has finished updating for user [user]. */
+class PackageUpdateMonitor
+@AssistedInject
+constructor(
+ @Assisted private val user: UserHandle,
+ @Assisted private val packageName: String,
+ @Assisted private val callback: Runnable,
+ @Background private val bgHandler: Handler,
+ @Application private val context: Context,
+) : PackageMonitor() {
+
+ private val monitoring = AtomicBoolean(false)
+
+ @AssistedFactory
+ fun interface Factory {
+ /**
+ * Create a [PackageUpdateMonitor] for a given [user] and [packageName]. It will run
+ * [callback] every time the package finishes updating.
+ */
+ fun create(user: UserHandle, packageName: String, callback: Runnable): PackageUpdateMonitor
+ }
+
+ /** Start monitoring for package updates. No-op if already monitoring. */
+ fun startMonitoring() {
+ if (monitoring.compareAndSet(/* expected */ false, /* new */ true)) {
+ register(context, user, false, bgHandler)
+ }
+ }
+
+ /** Stop monitoring for package updates. No-op if not monitoring. */
+ fun stopMonitoring() {
+ if (monitoring.compareAndSet(/* expected */ true, /* new */ false)) {
+ unregister()
+ }
+ }
+
+ /**
+ * If the package and the user match the ones for this [PackageUpdateMonitor], it will run
+ * [callback].
+ */
+ override fun onPackageUpdateFinished(packageName: String?, uid: Int) {
+ super.onPackageUpdateFinished(packageName, uid)
+ if (packageName == this.packageName && UserHandle.getUserHandleForUid(uid) == user) {
+ callback.run()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index 2c717f5..45cb13b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -16,11 +16,11 @@
package com.android.systemui.controls.controller
-import android.service.controls.actions.ControlAction
import android.service.controls.IControlsActionCallback
import android.service.controls.IControlsProvider
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
+import android.service.controls.actions.ControlAction
import android.service.controls.actions.ControlActionWrapper
import android.util.Log
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 5d608c3..7cbd1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -16,9 +16,11 @@
package com.android.systemui.controls.ui
+import android.app.Activity
import android.app.ActivityOptions
import android.app.ActivityTaskManager
import android.app.ActivityTaskManager.INVALID_TASK_ID
+import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
@@ -96,7 +98,9 @@
activityContext,
0 /* enterResId */,
0 /* exitResId */
- )
+ ).setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ options.isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+
taskView.startActivity(
pendingIntent,
fillInIntent,
@@ -214,6 +218,12 @@
if (!isShowing()) return
taskView.release()
+ val isActivityFinishing =
+ (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
+ if (isActivityFinishing == true) {
+ // Don't dismiss the dialog if the activity is finishing, it will get removed
+ return
+ }
super.dismiss()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index dff2c0e..8e6e0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -42,6 +42,7 @@
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
@@ -60,6 +61,7 @@
import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.privacy.PrivacyModule;
+import com.android.systemui.process.condition.SystemProcessCondition;
import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
import com.android.systemui.qs.FgsManagerController;
import com.android.systemui.qs.FgsManagerControllerImpl;
@@ -74,6 +76,7 @@
import com.android.systemui.shade.ShadeModule;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl;
+import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.smartspace.dagger.SmartspaceModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -126,6 +129,7 @@
import dagger.Module;
import dagger.Provides;
+import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -232,6 +236,17 @@
return state;
}
+ /**
+ * Provides the monitor for SystemUI that requires the process running as the system user.
+ */
+ @SysUISingleton
+ @Provides
+ @SystemUser
+ static Monitor provideSystemUserMonitor(@Main Executor executor,
+ SystemProcessCondition systemProcessCondition) {
+ return new Monitor(executor, Collections.singleton(systemProcessCondition));
+ }
+
@BindsOptionalOf
abstract CommandQueue optionalCommandQueue();
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/SystemUser.kt
similarity index 61%
copy from services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
copy to packages/SystemUI/src/com/android/systemui/dagger/qualifiers/SystemUser.kt
index 7c339d2..6878a52 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/SystemUser.kt
@@ -13,15 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.systemui.dagger.qualifiers
-package com.android.server.companion.datatransfer.contextsync;
+import javax.inject.Qualifier
-/** Callback for call metadata syncing. */
-public abstract class CallMetadataSyncCallback {
-
- abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
-
- abstract void requestCrossDeviceSync(int userId);
-
- abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
-}
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SystemUser
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/AssistantAttentionMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/AssistantAttentionMonitor.java
index 49d7f78..822cfb8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/AssistantAttentionMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/AssistantAttentionMonitor.java
@@ -19,6 +19,7 @@
import android.util.Log;
import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.callbacks.AssistantAttentionCallback;
import com.android.systemui.dreams.conditions.AssistantAttentionCondition;
import com.android.systemui.shared.condition.Monitor;
@@ -38,7 +39,7 @@
@Inject
public AssistantAttentionMonitor(
- Monitor monitor,
+ @SystemUser Monitor monitor,
AssistantAttentionCondition assistantAttentionCondition,
AssistantAttentionCallback callback) {
mConditionMonitor = monitor;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
index 7f567aa..e9ebd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -16,11 +16,10 @@
package com.android.systemui.dreams;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
-
import android.util.Log;
import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
import com.android.systemui.dreams.conditions.DreamCondition;
import com.android.systemui.flags.RestartDozeListener;
@@ -28,7 +27,6 @@
import com.android.systemui.util.condition.ConditionalCoreStartable;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* A {@link CoreStartable} to retain a monitor for tracking dreaming.
@@ -42,13 +40,11 @@
private final DreamStatusBarStateCallback mCallback;
private RestartDozeListener mRestartDozeListener;
-
@Inject
- public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
- @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
+ public DreamMonitor(@SystemUser Monitor monitor, DreamCondition dreamCondition,
DreamStatusBarStateCallback callback,
RestartDozeListener restartDozeListener) {
- super(pretextMonitor);
+ super(monitor);
mConditionMonitor = monitor;
mDreamCondition = dreamCondition;
mCallback = callback;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index a2dcdf5..80e68cf 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -17,7 +17,6 @@
package com.android.systemui.dreams;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT;
-import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +34,7 @@
import android.util.Log;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.condition.ConditionalCoreStartable;
@@ -105,7 +105,7 @@
@Inject
public DreamOverlayRegistrant(Context context, @Main Resources resources,
@Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent,
- @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ @SystemUser Monitor monitor) {
super(monitor);
mContext = context;
mResources = resources;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 1271645d..c61b4775 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -31,22 +31,14 @@
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
-import com.android.systemui.process.condition.SystemProcessCondition;
-import com.android.systemui.shared.condition.Condition;
-import com.android.systemui.shared.condition.Monitor;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.Executor;
import javax.inject.Named;
-
/**
* Dagger Module providing Dream-related functionality.
*/
@@ -65,11 +57,8 @@
String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled";
String DREAM_SUPPORTED = "dream_supported";
- String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions";
- String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor";
String DREAM_OVERLAY_WINDOW_TITLE = "dream_overlay_window_title";
-
/**
* Provides the dream component
*/
@@ -129,21 +118,6 @@
}
/** */
- @Binds
- @IntoSet
- @Named(DREAM_PRETEXT_CONDITIONS)
- Condition bindSystemProcessCondition(SystemProcessCondition condition);
-
- /** */
- @Provides
- @Named(DREAM_PRETEXT_MONITOR)
- static Monitor providesDockerPretextMonitor(
- @Main Executor executor,
- @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) {
- return new Monitor(executor, pretextConditions);
- }
-
- /** */
@Provides
@Named(DREAM_OVERLAY_WINDOW_TITLE)
static String providesDreamOverlayWindowTitle(@Main Resources resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 9427303..6ca409f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -61,6 +61,9 @@
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply")
+ // TODO(b/279735475): Tracking Bug
+ @JvmField val NEW_LIGHT_BAR_LOGIC = unreleasedFlag(279735475, "new_light_bar_logic")
+
/**
* This flag is server-controlled and should stay as [unreleasedFlag] since we never want to
* enable it on release builds.
@@ -68,8 +71,6 @@
val NOTIFICATION_MEMORY_LOGGING_ENABLED =
unreleasedFlag(119, "notification_memory_logging_enabled")
- @JvmField val USE_ROUNDNESS_SOURCETYPES = releasedFlag(116, "use_roundness_sourcetype")
-
@JvmField
val SIMPLIFIED_APPEAR_FRACTION =
releasedFlag(259395680, "simplified_appear_fraction")
@@ -81,7 +82,8 @@
// TODO(b/278873737): Tracking Bug
@JvmField
val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE =
- unreleasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete")
+ unreleasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete",
+ teamfood = true)
// TODO(b/277338665): Tracking Bug
@JvmField
@@ -116,7 +118,7 @@
// TODO(b/275694445): Tracking Bug
@JvmField
- val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208,
+ val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208,
"lockscreen_without_secure_lock_when_dreaming")
/**
@@ -204,11 +206,6 @@
)
/** Whether to inflate the bouncer view on a background thread. */
- // TODO(b/272091103): Tracking Bug
- @JvmField
- val ASYNC_INFLATE_BOUNCER = releasedFlag(229, "async_inflate_bouncer")
-
- /** Whether to inflate the bouncer view on a background thread. */
// TODO(b/273341787): Tracking Bug
@JvmField
val PREVENT_BYPASS_KEYGUARD = releasedFlag(230, "prevent_bypass_keyguard")
@@ -223,6 +220,22 @@
val LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP =
unreleasedFlag(232, "lock_screen_long_press_directly_opens_wallpaper_picker")
+ /** Whether to run the new udfps keyguard refactor code. */
+ // TODO(b/279440316): Tracking bug.
+ @JvmField
+ val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag(233, "refactor_udfps_keyguard_views")
+
+ /** Provide new auth messages on the bouncer. */
+ // TODO(b/277961132): Tracking bug.
+ @JvmField
+ val REVAMPED_BOUNCER_MESSAGES =
+ unreleasedFlag(234, "revamped_bouncer_messages")
+
+ /** Whether to delay showing bouncer UI when face auth or active unlock are enrolled. */
+ // TODO(b/279794160): Tracking bug.
+ @JvmField
+ val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -261,7 +274,7 @@
)
@JvmField
- val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = false)
+ val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = true)
// TODO(b/278068252): Tracking Bug
@JvmField
@@ -378,7 +391,7 @@
val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
// TODO(b/266157412): Tracking Bug
- val MEDIA_RETAIN_SESSIONS = releasedFlag(913, "media_retain_sessions")
+ val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
// TODO(b/266739309): Tracking Bug
@JvmField
@@ -388,10 +401,10 @@
val MEDIA_RESUME_PROGRESS = releasedFlag(915, "media_resume_progress")
// TODO(b/267166152) : Tracking Bug
- val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations")
+ val MEDIA_RETAIN_RECOMMENDATIONS = unreleasedFlag(916, "media_retain_recommendations")
// TODO(b/270437894): Tracking Bug
- val MEDIA_REMOTE_RESUME = releasedFlag(917, "media_remote_resume")
+ val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume")
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
@@ -587,6 +600,8 @@
// 1700 - clipboard
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
+ // TODO(b/278714186) Tracking Bug
+ @JvmField val CLIPBOARD_IMAGE_TIMEOUT = unreleasedFlag(1702, "clipboard_image_timeout")
// 1800 - shade container
// TODO(b/265944639): Tracking Bug
@@ -610,12 +625,14 @@
// TODO(b/269132640): Tracking Bug
@JvmField
val APP_PANELS_REMOVE_APPS_ALLOWED =
- unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = true)
+ releasedFlag(2003, "app_panels_remove_apps_allowed")
- // 2200 - udfps
+ // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
// TODO(b/259264861): Tracking Bug
@JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag(2200, "udfps_new_touch_detection")
@JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
+ // TODO(b/278622168): Tracking Bug
+ @JvmField val BIOMETRIC_BP_STRONG = unreleasedFlag(2202, "biometric_bp_strong")
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
@@ -679,8 +696,8 @@
// TODO(b/272805037): Tracking Bug
@JvmField
- val ADVANCED_VPN_ENABLED = unreleasedFlag(2800, name = "AdvancedVpn__enable_feature",
- namespace = "vpn", teamfood = true)
+ val ADVANCED_VPN_ENABLED = releasedFlag(2800, name = "AdvancedVpn__enable_feature",
+ namespace = "vpn")
// TODO(b/278761837): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index d3b6fc2..f64ed60 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -128,6 +128,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -240,6 +241,7 @@
private final ScreenshotHelper mScreenshotHelper;
private final SysuiColorExtractor mSysuiColorExtractor;
private final IStatusBarService mStatusBarService;
+ protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
@@ -349,6 +351,7 @@
MetricsLogger metricsLogger,
SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
+ LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
@@ -381,6 +384,7 @@
mUiEventLogger = uiEventLogger;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
+ mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
@@ -694,6 +698,7 @@
ActionsDialogLite dialog = new ActionsDialogLite(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
+ mLightBarController,
mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor,
mLockPatternUtils);
@@ -2192,6 +2197,7 @@
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
protected float mScrimAlpha;
+ protected final LightBarController mLightBarController;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
@@ -2267,6 +2273,7 @@
ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
+ LightBarController lightBarController,
NotificationShadeWindowController notificationShadeWindowController,
Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
@@ -2282,6 +2289,7 @@
mPowerOptionsAdapter = powerAdapter;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
+ mLightBarController = lightBarController;
mNotificationShadeWindowController = notificationShadeWindowController;
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
@@ -2474,6 +2482,7 @@
@Override
protected void start() {
mGlobalActionsLayout.updateList();
+ mLightBarController.setGlobalActionsVisible(true);
if (mBackgroundDrawable instanceof ScrimDrawable) {
mColorExtractor.addOnColorsChangedListener(this);
@@ -2504,6 +2513,7 @@
@Override
protected void stop() {
+ mLightBarController.setGlobalActionsVisible(false);
mColorExtractor.removeOnColorsChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index b92499e..b7ba201 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -56,11 +56,11 @@
tscl.registerTaskStackListener(mLockListener);
}
- private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info) {
+ private void startWorkChallengeInTask(ActivityManager.RunningTaskInfo info, int userId) {
String packageName = info.baseActivity != null ? info.baseActivity.getPackageName() : "";
Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
.setComponent(new ComponentName(mContext, WorkLockActivity.class))
- .putExtra(Intent.EXTRA_USER_ID, info.userId)
+ .putExtra(Intent.EXTRA_USER_ID, userId)
.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -76,10 +76,11 @@
} else {
// Starting the activity inside the task failed. We can't be sure why, so to be
// safe just remove the whole task if it still exists.
+ Log.w(TAG, "Failed to start work lock activity, will remove task=" + info.taskId);
try {
mIatm.removeTask(info.taskId);
} catch (RemoteException e) {
- Log.w(TAG, "Failed to get description for task=" + info.taskId);
+ Log.e(TAG, "Failed to remove task=" + info.taskId);
}
}
}
@@ -112,8 +113,8 @@
private final TaskStackChangeListener mLockListener = new TaskStackChangeListener() {
@Override
- public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info) {
- startWorkChallengeInTask(info);
+ public void onTaskProfileLocked(ActivityManager.RunningTaskInfo info, int userId) {
+ startWorkChallengeInTask(info, userId);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
index 9b94cdb..148d425 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
@@ -41,17 +41,6 @@
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
- private val keyguardStateControllerCallback: KeyguardStateController.Callback =
- object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- maybeHide()
- }
- }
-
- init {
- keyguardStateController.addCallback(keyguardStateControllerCallback)
- }
-
/**
* Sets the correct bouncer states to show the alternate bouncer if it can show.
*
@@ -102,11 +91,18 @@
return (systemClock.uptimeMillis() - bouncerRepository.lastAlternateBouncerVisibleTime) >
MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS
}
-
- private fun maybeHide() {
+ /**
+ * Should only be called through StatusBarKeyguardViewManager which propagates the source of
+ * truth to other concerned controllers. Will hide the alternate bouncer if it's no longer
+ * allowed to show.
+ *
+ * @return true if the alternate bouncer was newly hidden, else false.
+ */
+ fun maybeHide(): Boolean {
if (isVisibleState() && !canShowAlternateBouncerForFingerprint()) {
- hide()
+ return hide()
}
+ return false
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 6bbc6f6..c1aefc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -112,8 +112,6 @@
viewModel.isShowing.collect { isShowing ->
view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE
if (isShowing) {
- // Reset Security Container entirely.
- view.visibility = View.VISIBLE
securityContainerController.reinflateViewFlipper {
// Reset Security Container entirely.
securityContainerController.onBouncerVisibilityChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index a98a7d8..951f0bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.IBinder
@@ -33,6 +34,7 @@
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
import com.android.systemui.R
+import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -61,6 +63,7 @@
private val clockRegistry: ClockRegistry,
private val broadcastDispatcher: BroadcastDispatcher,
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
@Assisted bundle: Bundle,
) {
@@ -112,7 +115,9 @@
setUpBottomArea(rootView)
- setupSmartspace(rootView)
+ setUpSmartspace(rootView)
+
+ setUpUdfps(rootView)
setUpClock(rootView)
@@ -169,49 +174,53 @@
/**
* This sets up and shows a non-interactive smart space
*
- * The top padding is as follows:
- * Status bar height + clock top margin + keyguard smart space top offset
+ * The top padding is as follows: Status bar height + clock top margin + keyguard smart space
+ * top offset
*
- * The start padding is as follows:
- * Clock padding start + Below clock padding start
+ * The start padding is as follows: Clock padding start + Below clock padding start
*
- * The end padding is as follows:
- * Below clock padding end
+ * The end padding is as follows: Below clock padding end
*/
- private fun setupSmartspace(parentView: ViewGroup) {
- if (!lockscreenSmartspaceController.isEnabled() ||
- !lockscreenSmartspaceController.isDateWeatherDecoupled()) {
+ private fun setUpSmartspace(parentView: ViewGroup) {
+ if (
+ !lockscreenSmartspaceController.isEnabled() ||
+ !lockscreenSmartspaceController.isDateWeatherDecoupled()
+ ) {
return
}
smartSpaceView = lockscreenSmartspaceController.buildAndConnectDateView(parentView)
- val topPadding: Int = with(context.resources) {
- getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
+ val topPadding: Int =
+ with(context.resources) {
+ getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)
- }
+ }
- val startPadding: Int = with(context.resources) {
- getDimensionPixelSize(R.dimen.clock_padding_start) +
+ val startPadding: Int =
+ with(context.resources) {
+ getDimensionPixelSize(R.dimen.clock_padding_start) +
getDimensionPixelSize(R.dimen.below_clock_padding_start)
- }
+ }
- val endPadding: Int = context.resources
- .getDimensionPixelSize(R.dimen.below_clock_padding_end)
+ val endPadding: Int =
+ context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
it.isClickable = false
parentView.addView(
- it,
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- ),
+ it,
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ ),
)
}
+
+ smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
private fun setUpBottomArea(parentView: ViewGroup) {
@@ -234,6 +243,33 @@
)
}
+ private fun setUpUdfps(parentView: ViewGroup) {
+ val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds
+
+ // If sensorBounds are default rect, then there is no UDFPS
+ if (sensorBounds == Rect()) {
+ return
+ }
+
+ // Place the UDFPS view in the proper sensor location
+ val fingerprintLayoutParams =
+ FrameLayout.LayoutParams(sensorBounds.width(), sensorBounds.height())
+ fingerprintLayoutParams.setMarginsRelative(
+ sensorBounds.left,
+ sensorBounds.top,
+ sensorBounds.right,
+ sensorBounds.bottom
+ )
+ val finger =
+ LayoutInflater.from(context)
+ .inflate(
+ R.layout.udfps_keyguard_view_internal,
+ parentView,
+ false,
+ ) as View
+ parentView.addView(finger, fingerprintLayoutParams)
+ }
+
private fun setUpClock(parentView: ViewGroup) {
val clockChangeListener =
object : ClockRegistry.ClockChangeListener {
@@ -266,8 +302,6 @@
disposables.add(DisposableHandle { broadcastDispatcher.unregisterReceiver(receiver) })
onClockChanged(parentView)
-
- updateSmartspaceWithSetupClock()
}
private fun onClockChanged(parentView: ViewGroup) {
@@ -281,33 +315,22 @@
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
clockView?.let { parentView.removeView(it) }
- clockView = largeClock?.view?.apply {
- if (shouldHighlightSelectedAffordance) {
- alpha = DIM_ALPHA
+ clockView =
+ largeClock?.view?.apply {
+ if (shouldHighlightSelectedAffordance) {
+ alpha = DIM_ALPHA
+ }
+ parentView.addView(this)
+ visibility = View.VISIBLE
}
- parentView.addView(this)
- visibility = View.VISIBLE
- }
} else {
clockView?.visibility = View.GONE
}
- }
- /**
- * Updates smart space after clock is set up. Used to show or hide smartspace with the right
- * opacity based on the clock after setup.
- */
- private fun updateSmartspaceWithSetupClock() {
+ // Hide smart space if the clock has weather display; otherwise show it
val hasCustomWeatherDataDisplay =
- clockController
- .clock
- ?.largeClock
- ?.config
- ?.hasCustomWeatherDataDisplay == true
-
+ clockController.clock?.largeClock?.config?.hasCustomWeatherDataDisplay == true
hideSmartspace(hasCustomWeatherDataDisplay)
-
- smartSpaceView?.alpha = if (shouldHighlightSelectedAffordance) DIM_ALPHA else 1.0f
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/LogProxy.kt b/packages/SystemUI/src/com/android/systemui/log/table/LogProxy.kt
new file mode 100644
index 0000000..53886aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/table/LogProxy.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+import android.util.Log
+import javax.inject.Inject
+
+/** Dagger-friendly interface so we can inject a fake [android.util.Log] in tests */
+interface LogProxy {
+ /** verbose log */
+ fun v(tag: String, message: String)
+
+ /** debug log */
+ fun d(tag: String, message: String)
+
+ /** info log */
+ fun i(tag: String, message: String)
+
+ /** warning log */
+ fun w(tag: String, message: String)
+
+ /** error log */
+ fun e(tag: String, message: String)
+
+ /** wtf log */
+ fun wtf(tag: String, message: String)
+}
+
+class LogProxyDefault @Inject constructor() : LogProxy {
+ override fun v(tag: String, message: String) {
+ Log.v(tag, message)
+ }
+
+ override fun d(tag: String, message: String) {
+ Log.d(tag, message)
+ }
+
+ override fun i(tag: String, message: String) {
+ Log.i(tag, message)
+ }
+
+ override fun w(tag: String, message: String) {
+ Log.w(tag, message)
+ }
+
+ override fun e(tag: String, message: String) {
+ Log.e(tag, message)
+ }
+
+ override fun wtf(tag: String, message: String) {
+ Log.wtf(tag, message)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index faaa205..9d883cc 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -19,10 +19,17 @@
import android.os.Trace
import com.android.systemui.Dumpable
import com.android.systemui.common.buffer.RingBuffer
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.plugins.log.LogcatEchoTracker
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Locale
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.launch
/**
* A logger that logs changes in table format.
@@ -73,12 +80,18 @@
maxSize: Int,
private val name: String,
private val systemClock: SystemClock,
+ private val logcatEchoTracker: LogcatEchoTracker,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val coroutineScope: CoroutineScope,
+ private val localLogcat: LogProxy = LogProxyDefault(),
) : Dumpable {
init {
if (maxSize <= 0) {
throw IllegalArgumentException("maxSize must be > 0")
}
}
+ // For local logcat, send messages across this channel so the background job can process them
+ private val logMessageChannel = Channel<TableChange>(capacity = 10)
private val buffer = RingBuffer(maxSize) { TableChange() }
@@ -105,6 +118,16 @@
tableLogBuffer = this,
)
+ /** Start this log buffer logging in the background */
+ internal fun init() {
+ coroutineScope.launch(bgDispatcher) {
+ while (!logMessageChannel.isClosedForReceive) {
+ val log = logMessageChannel.receive()
+ echoToDesiredEndpoints(log)
+ }
+ }
+ }
+
/**
* Log the differences between [prevVal] and [newVal].
*
@@ -189,6 +212,7 @@
Trace.beginSection("TableLogBuffer#logChange(string)")
val change = obtain(timestamp, prefix, columnName, isInitial)
change.set(value)
+ tryAddMessage(change)
Trace.endSection()
}
@@ -202,6 +226,7 @@
Trace.beginSection("TableLogBuffer#logChange(boolean)")
val change = obtain(timestamp, prefix, columnName, isInitial)
change.set(value)
+ tryAddMessage(change)
Trace.endSection()
}
@@ -215,9 +240,14 @@
Trace.beginSection("TableLogBuffer#logChange(int)")
val change = obtain(timestamp, prefix, columnName, isInitial)
change.set(value)
+ tryAddMessage(change)
Trace.endSection()
}
+ private fun tryAddMessage(change: TableChange) {
+ logMessageChannel.trySend(change)
+ }
+
// TODO(b/259454430): Add additional change types here.
@Synchronized
@@ -258,6 +288,17 @@
Trace.endSection()
}
+ private fun echoToDesiredEndpoints(change: TableChange) {
+ if (
+ logcatEchoTracker.isBufferLoggable(bufferName = name, LogLevel.DEBUG) ||
+ logcatEchoTracker.isTagLoggable(change.columnName, LogLevel.DEBUG)
+ ) {
+ if (change.hasData()) {
+ localLogcat.d(name, change.logcatRepresentation())
+ }
+ }
+ }
+
@Synchronized
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println(HEADER_PREFIX + name)
@@ -284,6 +325,12 @@
pw.println()
}
+ /** Transforms an individual [TableChange] into a String for logcat */
+ private fun TableChange.logcatRepresentation(): String {
+ val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(timestamp)
+ return "$formattedTimestamp$SEPARATOR${getName()}$SEPARATOR${getVal()}"
+ }
+
/**
* A private implementation of [TableRowLogger].
*
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index 06668d3..42e742d 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -17,10 +17,15 @@
package com.android.systemui.log.table
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
+import com.android.systemui.plugins.log.LogcatEchoTracker
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
@SysUISingleton
class TableLogBufferFactory
@@ -28,6 +33,9 @@
constructor(
private val dumpManager: DumpManager,
private val systemClock: SystemClock,
+ private val logcatEchoTracker: LogcatEchoTracker,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ @Application private val coroutineScope: CoroutineScope,
) {
private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
@@ -44,8 +52,17 @@
name: String,
maxSize: Int,
): TableLogBuffer {
- val tableBuffer = TableLogBuffer(adjustMaxSize(maxSize), name, systemClock)
+ val tableBuffer =
+ TableLogBuffer(
+ adjustMaxSize(maxSize),
+ name,
+ systemClock,
+ logcatEchoTracker,
+ bgDispatcher,
+ coroutineScope,
+ )
dumpManager.registerNormalDumpable(name, tableBuffer)
+ tableBuffer.init()
return tableBuffer
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 0860c20..aec7b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -259,7 +259,7 @@
putExtra(Intent.EXTRA_INTENT, queryIntent)
// Update the title of the chooser
- val title = resources.getString(R.string.media_projection_permission_app_selector_title)
+ val title = resources.getString(R.string.screen_share_permission_app_selector_title)
putExtra(Intent.EXTRA_TITLE, title)
// Select host app's profile tab by default
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index e217e36..28adfbb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -157,8 +157,8 @@
CharSequence dialogTitle = null;
String appName = null;
if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
- dialogText = getString(R.string.media_projection_dialog_service_text);
- dialogTitle = getString(R.string.media_projection_dialog_service_title);
+ dialogText = getString(R.string.media_projection_sys_service_dialog_warning);
+ dialogTitle = getString(R.string.media_projection_sys_service_dialog_title);
} else {
String label = aInfo.loadLabel(packageManager).toString();
@@ -188,7 +188,7 @@
paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
- String actionText = getString(R.string.media_projection_dialog_text, appName);
+ String actionText = getString(R.string.media_projection_dialog_warning, appName);
SpannableString message = new SpannableString(actionText);
int appNameIndex = actionText.indexOf(appName);
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 2a8168b..05e04a1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -88,18 +88,9 @@
private final IBinder mToken;
private final Ringtone mRingtone;
- public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) {
- this(token, uri, user, aa, null);
- }
-
- Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa,
- @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+ Client(IBinder token, Ringtone ringtone) {
mToken = token;
-
- mRingtone = new Ringtone(getContextForUser(user), false);
- mRingtone.setAudioAttributesField(aa);
- mRingtone.setUri(uri, volumeShaperConfig);
- mRingtone.createLocalMediaPlayer();
+ mRingtone = ringtone;
}
@Override
@@ -129,11 +120,28 @@
Client client;
synchronized (mClients) {
client = mClients.get(token);
- if (client == null) {
- final UserHandle user = Binder.getCallingUserHandle();
- client = new Client(token, uri, user, aa, volumeShaperConfig);
- token.linkToDeath(client, 0);
- mClients.put(token, client);
+ }
+ // Don't hold the lock while constructing the ringtone, since it can be slow. The caller
+ // shouldn't call play on the same ringtone from 2 threads, so this shouldn't race and
+ // waste the build.
+ if (client == null) {
+ final UserHandle user = Binder.getCallingUserHandle();
+ Ringtone ringtone = new Ringtone(getContextForUser(user), false);
+ ringtone.setAudioAttributesField(aa);
+ ringtone.setUri(uri, volumeShaperConfig);
+ ringtone.createLocalMediaPlayer();
+ synchronized (mClients) {
+ client = mClients.get(token);
+ if (client == null) {
+ client = new Client(token, ringtone);
+ token.linkToDeath(client, 0);
+ mClients.put(token, client);
+ ringtone = null; // "owned" by the client now.
+ }
+ }
+ // Clean up ringtone if it was abandoned (a client already existed).
+ if (ringtone != null) {
+ ringtone.stop();
}
}
client.mRingtone.setLooping(looping);
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 9997730..fa42114 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -43,7 +43,6 @@
import android.net.Uri
import android.os.Parcelable
import android.os.Process
-import android.os.RemoteException
import android.os.UserHandle
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -53,7 +52,6 @@
import android.util.Pair as APair
import androidx.media.utils.MediaConstants
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -139,8 +137,6 @@
expiryTimeMs = 0,
)
-const val MEDIA_TITLE_ERROR_MESSAGE = "Invalid media data: title is null or blank."
-
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification()
}
@@ -185,7 +181,6 @@
private val logger: MediaUiEventLogger,
private val smartspaceManager: SmartspaceManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val statusBarService: IStatusBarService,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -257,7 +252,6 @@
mediaFlags: MediaFlags,
logger: MediaUiEventLogger,
smartspaceManager: SmartspaceManager,
- statusBarService: IStatusBarService,
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : this(
context,
@@ -283,7 +277,6 @@
logger,
smartspaceManager,
keyguardUpdateMonitor,
- statusBarService,
)
private val appChangeReceiver =
@@ -385,21 +378,21 @@
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
if (useQsMediaPlayer && isMediaNotification(sbn)) {
- var isNewlyActiveEntry = false
+ var logEvent = false
Assert.isMainThread()
val oldKey = findExistingEntry(key, sbn.packageName)
if (oldKey == null) {
val instanceId = logger.getNewInstanceId()
val temp = LOADING.copy(packageName = sbn.packageName, instanceId = instanceId)
mediaEntries.put(key, temp)
- isNewlyActiveEntry = true
+ logEvent = true
} else if (oldKey != key) {
// Resume -> active conversion; move to new key
val oldData = mediaEntries.remove(oldKey)!!
- isNewlyActiveEntry = true
+ logEvent = true
mediaEntries.put(key, oldData)
}
- loadMediaData(key, sbn, oldKey, isNewlyActiveEntry)
+ loadMediaData(key, sbn, oldKey, logEvent)
} else {
onNotificationRemoved(key)
}
@@ -482,9 +475,9 @@
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
- backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+ backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, logEvent) }
}
/** Add a listener for changes in this class */
@@ -608,11 +601,9 @@
}
}
- private fun removeEntry(key: String, logEvent: Boolean = true) {
+ private fun removeEntry(key: String) {
mediaEntries.remove(key)?.let {
- if (logEvent) {
- logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
- }
+ logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
}
notifyMediaDataRemoved(key)
}
@@ -760,7 +751,7 @@
key: String,
sbn: StatusBarNotification,
oldKey: String?,
- isNewlyActiveEntry: Boolean = false,
+ logEvent: Boolean = false
) {
val token =
sbn.notification.extras.getParcelable(
@@ -774,34 +765,6 @@
val metadata = mediaController.metadata
val notif: Notification = sbn.notification
- // Song name
- var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
- if (song == null) {
- song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
- }
- if (song == null) {
- song = HybridGroupManager.resolveTitle(notif)
- }
- // Media data must have a title.
- if (song.isNullOrBlank()) {
- try {
- statusBarService.onNotificationError(
- sbn.packageName,
- sbn.tag,
- sbn.id,
- sbn.uid,
- sbn.initialPid,
- MEDIA_TITLE_ERROR_MESSAGE,
- sbn.user.identifier
- )
- } catch (e: RemoteException) {
- Log.e(TAG, "cancelNotification failed: $e")
- }
- // Only add log for media removed if active media is updated with invalid title.
- foregroundExecutor.execute { removeEntry(key, !isNewlyActiveEntry) }
- return
- }
-
val appInfo =
notif.extras.getParcelable(
Notification.EXTRA_BUILDER_APPLICATION_INFO,
@@ -830,6 +793,15 @@
// App Icon
val smallIcon = sbn.notification.smallIcon
+ // Song name
+ var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+ if (song == null) {
+ song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
+ }
+ if (song == null) {
+ song = HybridGroupManager.resolveTitle(notif)
+ }
+
// Explicit Indicator
var isExplicit = false
if (mediaFlags.isExplicitIndicatorEnabled()) {
@@ -901,7 +873,7 @@
val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
val appUid = appInfo?.uid ?: Process.INVALID_UID
- if (isNewlyActiveEntry) {
+ if (logEvent) {
logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
} else if (playbackLocation != currentEntry?.playbackLocation) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 120704c..3fc3ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -154,6 +154,7 @@
get() = controller?.sessionToken
private var started = false
private var playbackType = PLAYBACK_TYPE_UNKNOWN
+ private var playbackVolumeControlId: String? = null
private var current: MediaDeviceData? = null
set(value) {
val sameWithoutIcon = value != null && value.equalsWithoutIcon(field)
@@ -181,6 +182,7 @@
localMediaManager.startScan()
muteAwaitConnectionManager?.startListening()
playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
controller?.registerCallback(this)
updateCurrent()
started = true
@@ -209,6 +211,8 @@
println(" current device is ${current?.name}")
val type = controller?.playbackInfo?.playbackType
println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType")
+ val volumeControlId = controller?.playbackInfo?.volumeControlId
+ println(" volumeControlId=$volumeControlId cached= $playbackVolumeControlId")
println(" routingSession=$routingSession")
println(" selectedRoutes=$selectedRoutes")
}
@@ -217,10 +221,15 @@
@WorkerThread
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
- if (newPlaybackType == playbackType) {
+ val newPlaybackVolumeControlId = info?.volumeControlId
+ if (
+ newPlaybackType == playbackType &&
+ newPlaybackVolumeControlId == playbackVolumeControlId
+ ) {
return
}
playbackType = newPlaybackType
+ playbackVolumeControlId = newPlaybackVolumeControlId
updateCurrent()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 9ebc8e4..8e014c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -126,10 +126,11 @@
private final LocalBluetoothManager mLocalBluetoothManager;
private final ActivityStarter mActivityStarter;
private final DialogLaunchAnimator mDialogLaunchAnimator;
- private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final CommonNotifCollection mNotifCollection;
private final Object mMediaDevicesLock = new Object();
@VisibleForTesting
+ final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
+ @VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
private final List<MediaItem> mMediaItemList = new CopyOnWriteArrayList<>();
@@ -713,7 +714,7 @@
dividerItems.forEach((key, item) -> {
finalMediaItems.add(key, item);
});
- finalMediaItems.add(new MediaItem());
+ attachConnectNewDeviceItemIfNeeded(finalMediaItems);
mMediaItemList.clear();
mMediaItemList.addAll(finalMediaItems);
}
@@ -749,7 +750,7 @@
finalMediaItems.add(new MediaItem(device));
}
}
- finalMediaItems.add(new MediaItem());
+ attachConnectNewDeviceItemIfNeeded(finalMediaItems);
mMediaItemList.clear();
mMediaItemList.addAll(finalMediaItems);
}
@@ -760,6 +761,13 @@
new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER));
}
+ private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
+ // Attach "Connect a device" item only when current output is not remote and not a group
+ if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) {
+ mediaItems.add(new MediaItem());
+ }
+ }
+
private void attachRangeInfo(List<MediaDevice> devices) {
for (MediaDevice mediaDevice : devices) {
if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
@@ -1251,7 +1259,8 @@
return null;
}
- private final MediaController.Callback mCb = new MediaController.Callback() {
+ @VisibleForTesting
+ final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
mCallback.onMediaChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 96e9756..91bb7898 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -60,15 +60,21 @@
private const val MIN_DURATION_COMMITTED_ANIMATION = 80L
private const val MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION = 120L
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
-private const val MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION = 80L
+private const val MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION = 160F
+private const val MIN_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION = 10F
+internal const val MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION = 100F
private const val MIN_DURATION_FLING_ANIMATION = 160L
private const val MIN_DURATION_ENTRY_TO_ACTIVE_CONSIDERED_AS_FLING = 100L
private const val MIN_DURATION_INACTIVE_TO_ACTIVE_CONSIDERED_AS_FLING = 400L
private const val POP_ON_FLING_DELAY = 60L
-private const val POP_ON_FLING_SCALE = 2f
-private const val POP_ON_COMMITTED_SCALE = 3f
+private const val POP_ON_FLING_VELOCITY = 2f
+private const val POP_ON_COMMITTED_VELOCITY = 3f
+private const val POP_ON_ACTIVE_MAX_VELOCITY = 2.5f
+private const val POP_ON_ACTIVE_MIN_VELOCITY = 4.5f
+private const val POP_ON_INACTIVE_MAX_VELOCITY = -1.05f
+private const val POP_ON_INACTIVE_MIN_VELOCITY = -1.5f
internal val VIBRATE_ACTIVATED_EFFECT =
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
@@ -156,13 +162,22 @@
private var gestureEntryTime = 0L
private var gestureInactiveTime = 0L
- private var gesturePastActiveThresholdWhileInactiveTime = 0L
private val elapsedTimeSinceInactive
get() = SystemClock.uptimeMillis() - gestureInactiveTime
private val elapsedTimeSinceEntry
get() = SystemClock.uptimeMillis() - gestureEntryTime
+
+ private var pastThresholdWhileEntryOrInactiveTime = 0L
+ private var entryToActiveDelay = 0F
+ private val entryToActiveDelayCalculation = {
+ convertVelocityToAnimationFactor(
+ valueOnFastVelocity = MIN_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION,
+ valueOnSlowVelocity = MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION,
+ )
+ }
+
// Whether the current gesture has moved a sufficiently large amount,
// so that we can unambiguously start showing the ENTRY animation
private var hasPassedDragSlop = false
@@ -306,7 +321,9 @@
MotionEvent.ACTION_UP -> {
when (currentState) {
GestureState.ENTRY -> {
- if (isFlungAwayFromEdge(endX = event.x)) {
+ if (isFlungAwayFromEdge(endX = event.x) ||
+ previousXTranslation > params.staticTriggerThreshold
+ ) {
updateArrowState(GestureState.ACTIVE)
updateArrowState(GestureState.FLUNG)
} else {
@@ -394,12 +411,29 @@
}
private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
-
val isWithinYActivationThreshold = xTranslation * 2 >= yTranslation
-
+ val isPastStaticThreshold = xTranslation > params.staticTriggerThreshold
when (currentState) {
GestureState.ENTRY -> {
- if (xTranslation > params.staticTriggerThreshold) {
+ if (isPastThresholdToActive(
+ isPastThreshold = isPastStaticThreshold,
+ dynamicDelay = entryToActiveDelayCalculation
+ )
+ ) {
+ updateArrowState(GestureState.ACTIVE)
+ }
+ }
+ GestureState.INACTIVE -> {
+ val isPastDynamicReactivationThreshold =
+ totalTouchDeltaInactive >= params.reactivationTriggerThreshold
+
+ if (isPastThresholdToActive(
+ isPastThreshold = isPastStaticThreshold &&
+ isPastDynamicReactivationThreshold &&
+ isWithinYActivationThreshold,
+ delay = MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION
+ )
+ ) {
updateArrowState(GestureState.ACTIVE)
}
}
@@ -408,43 +442,12 @@
totalTouchDeltaActive <= params.deactivationTriggerThreshold
val isMinDurationElapsed =
elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION
-
- if (isMinDurationElapsed && (!isWithinYActivationThreshold ||
- isPastDynamicDeactivationThreshold)
- ) {
+ val isPastAllThresholds =
+ !isWithinYActivationThreshold || isPastDynamicDeactivationThreshold
+ if (isPastAllThresholds && isMinDurationElapsed) {
updateArrowState(GestureState.INACTIVE)
}
}
- GestureState.INACTIVE -> {
- val isPastStaticThreshold =
- xTranslation > params.staticTriggerThreshold
- val isPastDynamicReactivationThreshold =
- totalTouchDeltaInactive >= params.reactivationTriggerThreshold
- val isPastAllThresholds = isPastStaticThreshold &&
- isPastDynamicReactivationThreshold &&
- isWithinYActivationThreshold
- val isPastAllThresholdsForFirstTime = isPastAllThresholds &&
- gesturePastActiveThresholdWhileInactiveTime == 0L
-
- gesturePastActiveThresholdWhileInactiveTime = when {
- isPastAllThresholdsForFirstTime -> SystemClock.uptimeMillis()
- isPastAllThresholds -> gesturePastActiveThresholdWhileInactiveTime
- else -> 0L
- }
-
- val elapsedTimePastAllThresholds =
- SystemClock.uptimeMillis() - gesturePastActiveThresholdWhileInactiveTime
-
- val isPastMinimumInactiveToActiveDuration =
- elapsedTimePastAllThresholds > MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION
-
- if (isPastAllThresholds && isPastMinimumInactiveToActiveDuration) {
- // The minimum duration adds the 'edge stickiness'
- // factor before pulling it off edge
- updateArrowState(GestureState.ACTIVE)
- }
- }
-
else -> {}
}
}
@@ -672,6 +675,28 @@
return flingDistance > minFlingDistance && isPastFlingVelocityThreshold
}
+ private fun isPastThresholdToActive(
+ isPastThreshold: Boolean,
+ delay: Float? = null,
+ dynamicDelay: () -> Float = { delay ?: 0F }
+ ): Boolean {
+ val resetValue = 0L
+ val isPastThresholdForFirstTime = pastThresholdWhileEntryOrInactiveTime == resetValue
+
+ if (!isPastThreshold) {
+ pastThresholdWhileEntryOrInactiveTime = resetValue
+ return false
+ }
+
+ if (isPastThresholdForFirstTime) {
+ pastThresholdWhileEntryOrInactiveTime = SystemClock.uptimeMillis()
+ entryToActiveDelay = dynamicDelay()
+ }
+ val timePastThreshold = SystemClock.uptimeMillis() - pastThresholdWhileEntryOrInactiveTime
+
+ return timePastThreshold > entryToActiveDelay
+ }
+
private fun playWithBackgroundWidthAnimation(
onEnd: DelayedOnAnimationEndListener,
delay: Long = 0L
@@ -886,33 +911,16 @@
}
GestureState.ACTIVE -> {
previousXTranslationOnActiveOffset = previousXTranslation
-
updateRestingArrowDimens()
-
vibratorHelper.cancel()
mainHandler.postDelayed(10L) {
vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
}
-
- val minimumPop = 2f
- val maximumPop = 4.5f
-
- when (previousState) {
- GestureState.ENTRY -> {
- val startingVelocity = convertVelocityToSpringStartingVelocity(
- valueOnFastVelocity = minimumPop,
- valueOnSlowVelocity = maximumPop,
- fastVelocityBound = 1f,
- slowVelocityBound = 0.5f,
- )
- mView.popOffEdge(startingVelocity)
- }
- GestureState.INACTIVE -> {
- mView.popOffEdge(maximumPop)
- }
-
- else -> {}
- }
+ val startingVelocity = convertVelocityToAnimationFactor(
+ valueOnFastVelocity = POP_ON_ACTIVE_MAX_VELOCITY,
+ valueOnSlowVelocity = POP_ON_ACTIVE_MIN_VELOCITY,
+ )
+ mView.popOffEdge(startingVelocity)
}
GestureState.INACTIVE -> {
@@ -925,9 +933,9 @@
// so that gesture progress in this state is consistent regardless of entry
totalTouchDeltaInactive = params.deactivationTriggerThreshold
- val startingVelocity = convertVelocityToSpringStartingVelocity(
- valueOnFastVelocity = -1.05f,
- valueOnSlowVelocity = -1.50f
+ val startingVelocity = convertVelocityToAnimationFactor(
+ valueOnFastVelocity = POP_ON_INACTIVE_MAX_VELOCITY,
+ valueOnSlowVelocity = POP_ON_INACTIVE_MIN_VELOCITY
)
mView.popOffEdge(startingVelocity)
@@ -935,7 +943,9 @@
updateRestingArrowDimens()
}
GestureState.FLUNG -> {
- mainHandler.postDelayed(POP_ON_FLING_DELAY) { mView.popScale(POP_ON_FLING_SCALE) }
+ mainHandler.postDelayed(POP_ON_FLING_DELAY) {
+ mView.popScale(POP_ON_FLING_VELOCITY)
+ }
updateRestingArrowDimens()
mainHandler.postDelayed(onEndSetCommittedStateListener.runnable,
MIN_DURATION_FLING_ANIMATION)
@@ -951,7 +961,7 @@
mainHandler.postDelayed(onEndSetGoneStateListener.runnable,
MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION)
} else {
- mView.popScale(POP_ON_COMMITTED_SCALE)
+ mView.popScale(POP_ON_COMMITTED_VELOCITY)
mainHandler.postDelayed(onAlphaEndSetGoneStateListener.runnable,
MIN_DURATION_COMMITTED_ANIMATION)
}
@@ -968,11 +978,11 @@
}
}
- private fun convertVelocityToSpringStartingVelocity(
+ private fun convertVelocityToAnimationFactor(
valueOnFastVelocity: Float,
valueOnSlowVelocity: Float,
- fastVelocityBound: Float = 3f,
- slowVelocityBound: Float = 0f,
+ fastVelocityBound: Float = 1f,
+ slowVelocityBound: Float = 0.5f,
): Float {
val factor = velocityTracker?.run {
computeCurrentVelocity(PX_PER_MS)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 41e3e6d..9a1efc3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static android.view.InputDevice.SOURCE_MOUSE;
+import static android.view.InputDevice.SOURCE_TOUCHPAD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
@@ -937,10 +939,12 @@
mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
- boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
// Trackpad back gestures don't have zones, so we don't need to check if the down event
- // is within insets.
+ // is within insets. Also we don't allow back for button press from the trackpad, and
+ // yet we do with a mouse.
+ boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
+ && !isButtonPressFromTrackpad(ev)
&& (isTrackpadMultiFingerSwipe || isWithinInsets)
&& !mGestureBlockingActivityRunning
&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
@@ -1047,6 +1051,11 @@
mProtoTracer.scheduleFrameUpdate();
}
+ private boolean isButtonPressFromTrackpad(MotionEvent ev) {
+ int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources();
+ return (sources & (SOURCE_MOUSE | SOURCE_TOUCHPAD)) == sources && ev.getButtonState() != 0;
+ }
+
private void dispatchToBackAnimation(MotionEvent event) {
if (mBackAnimation != null) {
mVelocityTracker.addMovement(event);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 876c74a..182ece7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -153,7 +153,7 @@
horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
- horizontalTranslationSpring = createSpring(500f, 0.76f),
+ horizontalTranslationSpring = createSpring(800f, 0.76f),
verticalTranslationSpring = createSpring(30000f, 1f),
scaleSpring = createSpring(120f, 0.8f),
arrowDimens = ArrowDimens(
@@ -205,8 +205,8 @@
activeIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
- horizontalTranslationSpring = createSpring(1000f, 0.7f),
- scaleSpring = createSpring(450f, 0.39f),
+ horizontalTranslationSpring = createSpring(1000f, 0.8f),
+ scaleSpring = createSpring(325f, 0.55f),
scalePivotX = getDimen(R.dimen.navigation_edge_active_background_width),
arrowDimens = ArrowDimens(
length = getDimen(R.dimen.navigation_edge_active_arrow_length),
@@ -253,7 +253,7 @@
getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
farCornerRadius =
getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
- widthSpring = createSpring(400f, 0.65f),
+ widthSpring = createSpring(650f, 1f),
heightSpring = createSpring(1500f, 0.45f),
farCornerRadiusSpring = createSpring(300f, 1f),
edgeCornerRadiusSpring = createSpring(250f, 0.5f),
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt
new file mode 100644
index 0000000..c209a00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivity.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.notetask
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import javax.inject.Inject
+
+/**
+ * An internal proxy activity that starts the notes role setting.
+ *
+ * This activity is introduced mainly for the error handling of the notes app lock screen shortcut
+ * picker, which only supports package + action but not extras. See
+ * [KeyguardQuickAffordanceConfig.PickerScreenState.Disabled.actionComponentName].
+ */
+class LaunchNotesRoleSettingsTrampolineActivity
+@Inject
+constructor(
+ private val controller: NoteTaskController,
+) : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val entryPoint =
+ if (intent?.action == ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) {
+ NoteTaskEntryPoint.QUICK_AFFORDANCE
+ } else {
+ null
+ }
+ controller.startNotesRoleSetting(this, entryPoint)
+ finish()
+ }
+
+ companion object {
+ const val ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE =
+ "com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 8aec0c6..7e9b346 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -31,18 +31,16 @@
import android.content.pm.PackageManager
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
-import android.os.Build
import android.os.UserHandle
import android.os.UserManager
-import android.util.Log
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
-import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
@@ -93,10 +91,10 @@
if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
if (isExpanding) {
- logDebug { "onBubbleExpandChanged - expanding: $info" }
+ debugLog { "onBubbleExpandChanged - expanding: $info" }
eventLogger.logNoteTaskOpened(info)
} else {
- logDebug { "onBubbleExpandChanged - collapsing: $info" }
+ debugLog { "onBubbleExpandChanged - collapsing: $info" }
eventLogger.logNoteTaskClosed(info)
}
}
@@ -113,6 +111,43 @@
)
}
+ /** Starts the notes role setting. */
+ fun startNotesRoleSetting(activityContext: Context, entryPoint: NoteTaskEntryPoint?) {
+ val user =
+ if (entryPoint == null) {
+ userTracker.userHandle
+ } else {
+ getUserForHandlingNotesTaking(entryPoint)
+ }
+ activityContext.startActivityAsUser(
+ Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
+ putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NOTES)
+ },
+ user
+ )
+ }
+
+ /**
+ * Returns the [UserHandle] of an android user that should handle the notes taking [entryPoint].
+ *
+ * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the
+ * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work
+ * profile user will always be launched.
+ *
+ * On non managed devices or devices with other management modes, the current [UserHandle] is
+ * returned.
+ */
+ fun getUserForHandlingNotesTaking(entryPoint: NoteTaskEntryPoint): UserHandle =
+ if (
+ entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES &&
+ devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile
+ ) {
+ userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) }?.userHandle
+ ?: userTracker.userHandle
+ } else {
+ userTracker.userHandle
+ }
+
/**
* Shows a note task. How the task is shown will depend on when the method is invoked.
*
@@ -123,30 +158,13 @@
* bubble is already opened.
*
* That will let users open other apps in full screen, and take contextual notes.
- *
- * On company owned personally enabled (COPE) devices, if the given [entryPoint] is in the
- * [FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES] list, the default notes app in the work
- * profile user will always be launched.
*/
fun showNoteTask(
entryPoint: NoteTaskEntryPoint,
) {
if (!isEnabled) return
- val user: UserHandle =
- if (
- entryPoint in FORCE_WORK_NOTE_APPS_ENTRY_POINTS_ON_COPE_DEVICES &&
- devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile
- ) {
- userTracker.userProfiles
- .firstOrNull { userManager.isManagedProfile(it.id) }
- ?.userHandle
- ?: userTracker.userHandle
- } else {
- userTracker.userHandle
- }
-
- showNoteTaskAsUser(entryPoint, user)
+ showNoteTaskAsUser(entryPoint, getUserForHandlingNotesTaking(entryPoint))
}
/** A variant of [showNoteTask] which launches note task in the given [user]. */
@@ -169,14 +187,14 @@
isKeyguardLocked &&
devicePolicyManager.areKeyguardShortcutsDisabled(userId = user.identifier)
) {
- logDebug { "Enterprise policy disallows launching note app when the screen is locked." }
+ debugLog { "Enterprise policy disallows launching note app when the screen is locked." }
return
}
val info = resolver.resolveInfo(entryPoint, isKeyguardLocked, user)
if (info == null) {
- logDebug { "Default notes app isn't set" }
+ debugLog { "Default notes app isn't set" }
showNoDefaultNotesAppToast()
return
}
@@ -185,7 +203,7 @@
try {
// TODO(b/266686199): We should handle when app not available. For now, we log.
- logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
+ debugLog { "onShowNoteTask - start: $info on user#${user.identifier}" }
when (info.launchMode) {
is NoteTaskLaunchMode.AppBubble -> {
val intent = createNoteTaskIntent(info)
@@ -193,7 +211,7 @@
Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
bubbles.showOrHideAppBubble(intent, user, icon)
// App bubble logging happens on `onBubbleExpandChanged`.
- logDebug { "onShowNoteTask - opened as app bubble: $info" }
+ debugLog { "onShowNoteTask - opened as app bubble: $info" }
}
is NoteTaskLaunchMode.Activity -> {
if (activityManager.isInForeground(info.packageName)) {
@@ -201,20 +219,20 @@
val intent = createHomeIntent()
context.startActivityAsUser(intent, user)
eventLogger.logNoteTaskClosed(info)
- logDebug { "onShowNoteTask - closed as activity: $info" }
+ debugLog { "onShowNoteTask - closed as activity: $info" }
} else {
val intent = createNoteTaskIntent(info)
context.startActivityAsUser(intent, user)
eventLogger.logNoteTaskOpened(info)
- logDebug { "onShowNoteTask - opened as activity: $info" }
+ debugLog { "onShowNoteTask - opened as activity: $info" }
}
}
}
- logDebug { "onShowNoteTask - success: $info" }
+ debugLog { "onShowNoteTask - success: $info" }
} catch (e: ActivityNotFoundException) {
- logDebug { "onShowNoteTask - failed: $info" }
+ debugLog { "onShowNoteTask - failed: $info" }
}
- logDebug { "onShowNoteTask - completed: $info" }
+ debugLog { "onShowNoteTask - completed: $info" }
}
@VisibleForTesting
@@ -231,8 +249,6 @@
* Widget Picker to all users.
*/
fun setNoteTaskShortcutEnabled(value: Boolean, user: UserHandle) {
- val componentName = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
-
val enabledState =
if (value) {
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
@@ -249,13 +265,14 @@
} else {
context.createContextAsUser(user, /* flags= */ 0)
}
+
userContext.packageManager.setComponentEnabledSetting(
- componentName,
+ SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT,
enabledState,
PackageManager.DONT_KILL_APP,
)
- logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
+ debugLog { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
}
/**
@@ -298,6 +315,19 @@
companion object {
val TAG = NoteTaskController::class.simpleName.orEmpty()
+ /**
+ * IMPORTANT! The shortcut package name and class should be synchronized with Settings:
+ * [com.android.settings.notetask.shortcut.CreateNoteTaskShortcutActivity].
+ *
+ * Changing the package name or class is a breaking change.
+ */
+ @VisibleForTesting
+ val SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT =
+ ComponentName(
+ "com.android.settings",
+ "com.android.settings.notetask.shortcut.CreateNoteTaskShortcutActivity",
+ )
+
const val SHORTCUT_ID = "note_task_shortcut_id"
/**
@@ -341,11 +371,6 @@
}
}
-/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */
-private inline fun Any.logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message())
-}
-
/** Creates an [Intent] which forces the current app to background by calling home. */
private fun createHomeIntent(): Intent =
Intent(Intent.ACTION_MAIN).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index a166393..4d5173a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -24,7 +24,6 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
-import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
import dagger.Binds
@@ -46,8 +45,9 @@
@[Binds IntoMap ClassKey(LaunchNoteTaskManagedProfileProxyActivity::class)]
fun LaunchNoteTaskManagedProfileProxyActivity.bindNoteTaskLauncherProxyActivity(): Activity
- @[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
- fun CreateNoteTaskShortcutActivity.bindNoteTaskShortcutActivity(): Activity
+ @[Binds IntoMap ClassKey(LaunchNotesRoleSettingsTrampolineActivity::class)]
+ fun LaunchNotesRoleSettingsTrampolineActivity.bindLaunchNotesRoleSettingsTrampolineActivity():
+ Activity
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index 2da5b76..444407c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -16,9 +16,12 @@
package com.android.systemui.notetask.quickaffordance
+import android.app.role.OnRoleHoldersChangedListener
+import android.app.role.RoleManager
import android.content.Context
import android.hardware.input.InputSettings
import android.os.Build
+import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor
@@ -27,17 +30,22 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEnabledKey
-import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR
import com.android.systemui.stylus.StylusManager
import dagger.Lazy
+import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
@@ -49,13 +57,16 @@
class NoteTaskQuickAffordanceConfig
@Inject
constructor(
- context: Context,
+ private val context: Context,
private val controller: NoteTaskController,
+ private val noteTaskInfoResolver: NoteTaskInfoResolver,
private val stylusManager: StylusManager,
+ private val roleManager: RoleManager,
private val keyguardMonitor: KeyguardUpdateMonitor,
private val userManager: UserManager,
private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
@NoteTaskEnabledKey private val isEnabled: Boolean,
+ @Background private val backgroundExecutor: Executor,
) : KeyguardQuickAffordanceConfig {
override val key = BuiltInKeyguardQuickAffordanceKeys.CREATE_NOTE
@@ -73,15 +84,24 @@
val configSelectedFlow = repository.createConfigSelectedFlow(key)
val stylusEverUsedFlow = stylusManager.createStylusEverUsedFlow(context)
val userUnlockedFlow = userManager.createUserUnlockedFlow(keyguardMonitor)
- combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow) {
+ val defaultNotesAppFlow =
+ roleManager.createNotesRoleFlow(backgroundExecutor, controller, noteTaskInfoResolver)
+ combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow, defaultNotesAppFlow) {
isUserUnlocked,
isStylusEverUsed,
- isConfigSelected ->
+ isConfigSelected,
+ isDefaultNotesAppSet ->
logDebug { "lockScreenState:isUserUnlocked=$isUserUnlocked" }
logDebug { "lockScreenState:isStylusEverUsed=$isStylusEverUsed" }
logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" }
+ logDebug { "lockScreenState:isDefaultNotesAppSet=$isDefaultNotesAppSet" }
- if (isEnabled && isUserUnlocked && (isConfigSelected || isStylusEverUsed)) {
+ if (
+ isEnabled &&
+ isUserUnlocked &&
+ isDefaultNotesAppSet &&
+ (isConfigSelected || isStylusEverUsed)
+ ) {
val contentDescription = ContentDescription.Resource(pickerNameResourceId)
val icon = Icon.Resource(pickerIconResourceId, contentDescription)
LockScreenState.Visible(icon)
@@ -92,15 +112,34 @@
.onEach { state -> logDebug { "lockScreenState=$state" } }
}
- override suspend fun getPickerScreenState() =
- if (isEnabled) {
- PickerScreenState.Default()
- } else {
- PickerScreenState.UnavailableOnDevice
+ override suspend fun getPickerScreenState(): PickerScreenState {
+ val isDefaultNotesAppSet =
+ noteTaskInfoResolver.resolveInfo(
+ QUICK_AFFORDANCE,
+ user = controller.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ ) != null
+ return when {
+ isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default()
+ isEnabled -> {
+ PickerScreenState.Disabled(
+ listOf(
+ context.getString(
+ R.string.keyguard_affordance_enablement_dialog_notes_app_instruction
+ )
+ ),
+ context.getString(
+ R.string.keyguard_affordance_enablement_dialog_notes_app_action
+ ),
+ "${context.packageName}$COMPONENT_NAME_SEPARATOR" +
+ "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE",
+ )
+ }
+ else -> PickerScreenState.UnavailableOnDevice
}
+ }
override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
- controller.showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ controller.showNoteTask(entryPoint = QUICK_AFFORDANCE)
return OnTriggeredResult.Handled
}
}
@@ -129,6 +168,27 @@
awaitClose { unregisterCallback(callback) }
}
+private fun RoleManager.createNotesRoleFlow(
+ executor: Executor,
+ noteTaskController: NoteTaskController,
+ noteTaskInfoResolver: NoteTaskInfoResolver,
+) = callbackFlow {
+ fun isDefaultNotesAppSetForUser() =
+ noteTaskInfoResolver.resolveInfo(
+ QUICK_AFFORDANCE,
+ user = noteTaskController.getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+ ) != null
+
+ trySendBlocking(isDefaultNotesAppSetForUser())
+ val callback = OnRoleHoldersChangedListener { roleName, _ ->
+ if (roleName == RoleManager.ROLE_NOTES) {
+ trySendBlocking(isDefaultNotesAppSetForUser())
+ }
+ }
+ addOnRoleHoldersChangedListenerAsUser(executor, callback, UserHandle.ALL)
+ awaitClose { removeOnRoleHoldersChangedListenerAsUser(callback, UserHandle.ALL) }
+}
+
private fun KeyguardQuickAffordanceRepository.createConfigSelectedFlow(key: String) =
selections.map { selected ->
selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
deleted file mode 100644
index 0cfb0a5..0000000
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(InternalNoteTaskApi::class)
-
-package com.android.systemui.notetask.shortcut
-
-import android.app.Activity
-import android.app.role.RoleManager
-import android.content.pm.ShortcutManager
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import com.android.systemui.notetask.InternalNoteTaskApi
-import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
-import javax.inject.Inject
-
-/**
- * Activity responsible for create a shortcut for notes action. If the shortcut is enabled, a new
- * shortcut will appear in the widget picker. If the shortcut is selected, the Activity here will be
- * launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish.
- *
- * @see <a
- * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
- * a custom shortcut activity</a>
- */
-class CreateNoteTaskShortcutActivity
-@Inject
-constructor(
- private val roleManager: RoleManager,
- private val shortcutManager: ShortcutManager,
-) : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- val shortcutInfo = roleManager.createNoteShortcutInfoAsUser(context = this, user)
- val shortcutIntent = shortcutManager.createShortcutResultIntent(shortcutInfo)
- setResult(Activity.RESULT_OK, shortcutIntent)
-
- finish()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
index 0f38d32..8ca13b9 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -18,12 +18,11 @@
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.os.Bundle
import android.os.UserHandle
import android.os.UserManager
-import android.util.Log
import androidx.activity.ComponentActivity
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
import com.android.systemui.settings.UserTracker
@@ -68,7 +67,7 @@
val mainUser: UserHandle? = userManager.mainUser
if (userManager.isManagedProfile) {
if (mainUser == null) {
- logDebug { "Can't find the main user. Skipping the notes app launch." }
+ debugLog { "Can't find the main user. Skipping the notes app launch." }
} else {
controller.startNoteTaskProxyActivityForUser(mainUser)
}
@@ -89,8 +88,3 @@
}
}
}
-
-/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */
-private inline fun Any.logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message())
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 79167f2..166ba9f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -15,6 +15,7 @@
package com.android.systemui.privacy
import android.content.Context
+import android.content.res.Configuration
import android.util.AttributeSet
import android.view.Gravity.CENTER_VERTICAL
import android.view.Gravity.END
@@ -102,6 +103,11 @@
R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
}
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
private fun updateResources() {
iconMargin = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
@@ -110,8 +116,11 @@
iconColor =
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ val height = context.resources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height)
val padding = context.resources
.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
+ iconsContainer.layoutParams.height = height
iconsContainer.setPaddingRelative(padding, 0, padding, 0)
iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 6f645b5..995c6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,7 +26,9 @@
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import javax.inject.Named
interface ChipVisibilityListener {
fun onChipVisibilityRefreshed(visible: Boolean)
@@ -45,10 +47,10 @@
class HeaderPrivacyIconsController @Inject constructor(
private val privacyItemController: PrivacyItemController,
private val uiEventLogger: UiEventLogger,
- private val privacyChip: OngoingPrivacyChip,
+ @Named(SHADE_HEADER) private val privacyChip: OngoingPrivacyChip,
private val privacyDialogController: PrivacyDialogController,
private val privacyLogger: PrivacyLogger,
- private val iconContainer: StatusIconContainer,
+ @Named(SHADE_HEADER) private val iconContainer: StatusIconContainer,
private val permissionManager: PermissionManager,
@Background private val backgroundExecutor: Executor,
@Main private val uiExecutor: Executor,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index b7f9f6b..1afc885 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -38,7 +38,9 @@
*/
public class QSContainerImpl extends FrameLayout implements Dumpable {
+ private int mFancyClippingLeftInset;
private int mFancyClippingTop;
+ private int mFancyClippingRightInset;
private int mFancyClippingBottom;
private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
private final Path mFancyClippingPath = new Path();
@@ -53,6 +55,7 @@
private boolean mQsDisabled;
private int mContentHorizontalPadding = -1;
private boolean mClippingEnabled;
+ private boolean mIsFullWidth;
public QSContainerImpl(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -237,7 +240,8 @@
/**
* Clip QS bottom using a concave shape.
*/
- public void setFancyClipping(int top, int bottom, int radius, boolean enabled) {
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom, int radius,
+ boolean enabled, boolean fullWidth) {
boolean updatePath = false;
if (mFancyClippingRadii[0] != radius) {
mFancyClippingRadii[0] = radius;
@@ -246,10 +250,18 @@
mFancyClippingRadii[3] = radius;
updatePath = true;
}
+ if (mFancyClippingLeftInset != leftInset) {
+ mFancyClippingLeftInset = leftInset;
+ updatePath = true;
+ }
if (mFancyClippingTop != top) {
mFancyClippingTop = top;
updatePath = true;
}
+ if (mFancyClippingRightInset != rightInset) {
+ mFancyClippingRightInset = rightInset;
+ updatePath = true;
+ }
if (mFancyClippingBottom != bottom) {
mFancyClippingBottom = bottom;
updatePath = true;
@@ -258,6 +270,10 @@
mClippingEnabled = enabled;
updatePath = true;
}
+ if (mIsFullWidth != fullWidth) {
+ mIsFullWidth = fullWidth;
+ updatePath = true;
+ }
if (updatePath) {
updateClippingPath();
@@ -281,15 +297,21 @@
return;
}
- mFancyClippingPath.addRoundRect(0, mFancyClippingTop, getWidth(),
+ int clippingLeft = mIsFullWidth ? -mFancyClippingLeftInset : 0;
+ int clippingRight = mIsFullWidth ? getWidth() + mFancyClippingRightInset : getWidth();
+ mFancyClippingPath.addRoundRect(clippingLeft, mFancyClippingTop, clippingRight,
mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW);
invalidate();
}
@Override
public void dump(PrintWriter pw, String[] args) {
- pw.println(getClass().getSimpleName() + " updateClippingPath: top("
- + mFancyClippingTop + ") bottom(" + mFancyClippingBottom + ") mClippingEnabled("
- + mClippingEnabled + ")");
+ pw.println(getClass().getSimpleName() + " updateClippingPath: "
+ + "leftInset(" + mFancyClippingLeftInset + ") "
+ + "top(" + mFancyClippingTop + ") "
+ + "rightInset(" + mFancyClippingRightInset + ") "
+ + "bottom(" + mFancyClippingBottom + ") "
+ + "mClippingEnabled(" + mClippingEnabled + ") "
+ + "mIsFullWidth(" + mIsFullWidth + ")");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d806afa..fd3f701 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,9 +18,10 @@
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
@@ -395,9 +396,11 @@
}
@Override
- public void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible) {
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom,
+ int cornerRadius, boolean visible, boolean fullWidth) {
if (getView() instanceof QSContainerImpl) {
- ((QSContainerImpl) getView()).setFancyClipping(top, bottom, cornerRadius, visible);
+ ((QSContainerImpl) getView()).setFancyClipping(leftInset, top, rightInset, bottom,
+ cornerRadius, visible, fullWidth);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index 0bce1f7..b70b94b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -719,7 +719,8 @@
String name = vpnName != null ? vpnName : vpnNameWorkProfile;
String namedVp = mDpm.getResources().getString(
QS_DIALOG_MANAGEMENT_NAMED_VPN,
- () -> mContext.getString(R.string.monitoring_description_named_vpn, name),
+ () -> mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, name),
name);
message.append(namedVp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 4a31998..b806683 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -385,6 +385,11 @@
super.onInitializeAccessibilityNodeInfo(info)
// Clear selected state so it is not announce by talkback.
info.isSelected = false
+ info.text = if (TextUtils.isEmpty(secondaryLabel.text)) {
+ "${label.text}"
+ } else {
+ "${label.text}, ${secondaryLabel.text}"
+ }
if (lastDisabledByPolicy) {
info.addAction(
AccessibilityNodeInfo.AccessibilityAction(
@@ -402,12 +407,6 @@
accessibilityClass
}
if (Switch::class.java.name == accessibilityClass) {
- val label = resources.getString(
- if (tileState) R.string.switch_bar_on else R.string.switch_bar_off)
- // Set the text here for tests in
- // android.platform.test.scenario.sysui.quicksettings. Can be removed when
- // UiObject2 has a new getStateDescription() API and tests are updated.
- info.text = label
info.isChecked = tileState
info.isCheckable = true
if (isLongClickable) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index f63bf07..b340043 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -57,8 +57,8 @@
setContentView(R.layout.screen_share_dialog)
dialogTitle = findViewById(R.id.screen_share_dialog_title)
warning = findViewById(R.id.text_warning)
- startButton = findViewById(R.id.button_start)
- cancelButton = findViewById(R.id.button_cancel)
+ startButton = findViewById(android.R.id.button1)
+ cancelButton = findViewById(android.R.id.button2)
updateIcon()
initScreenShareOptions()
createOptionsView(getOptionsViewLayoutId())
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index 201557c..f4f5f66 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -28,12 +28,14 @@
) : BaseScreenSharePermissionDialog(context, createOptionList(appName), appName) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
if (appName == null) {
- setDialogTitle(R.string.media_projection_permission_dialog_system_service_title)
+ setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title)
+ setStartButtonText(R.string.media_projection_entry_cast_permission_dialog_continue)
} else {
- setDialogTitle(R.string.media_projection_permission_dialog_title)
+ setDialogTitle(R.string.media_projection_entry_app_permission_dialog_title)
+ setStartButtonText(R.string.media_projection_entry_app_permission_dialog_continue)
}
- setStartButtonText(R.string.media_projection_permission_dialog_continue)
setStartButtonOnClickListener {
// Note that it is important to run this callback before dismissing, so that the
// callback can disable the dialog exit animation if it wants to.
@@ -50,26 +52,26 @@
private fun createOptionList(appName: String?): List<ScreenShareOption> {
val singleAppWarningText =
if (appName == null) {
- R.string.media_projection_permission_dialog_system_service_warning_single_app
+ R.string.media_projection_entry_cast_permission_dialog_warning_single_app
} else {
- R.string.media_projection_permission_dialog_warning_single_app
+ R.string.media_projection_entry_app_permission_dialog_warning_single_app
}
val entireScreenWarningText =
if (appName == null) {
- R.string.media_projection_permission_dialog_system_service_warning_entire_screen
+ R.string.media_projection_entry_cast_permission_dialog_warning_entire_screen
} else {
- R.string.media_projection_permission_dialog_warning_entire_screen
+ R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
}
return listOf(
ScreenShareOption(
mode = ENTIRE_SCREEN,
- spinnerText = R.string.media_projection_permission_dialog_option_entire_screen,
+ spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
warningText = entireScreenWarningText
),
ScreenShareOption(
mode = SINGLE_APP,
- spinnerText = R.string.media_projection_permission_dialog_option_single_app,
+ spinnerText = R.string.screen_share_permission_dialog_option_single_app,
warningText = singleAppWarningText
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 4349bd7..84f358c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -33,6 +33,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -60,11 +61,10 @@
public static final int REQUEST_CODE = 2;
private static final int USER_ID_NOT_SPECIFIED = -1;
- private static final int NOTIFICATION_RECORDING_ID = 4274;
- private static final int NOTIFICATION_PROCESSING_ID = 4275;
- private static final int NOTIFICATION_VIEW_ID = 4273;
+ private static final int NOTIF_BASE_ID = 4273;
private static final String TAG = "RecordingService";
private static final String CHANNEL_ID = "screen_record";
+ private static final String GROUP_KEY = "screen_record_saved";
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
private static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
@@ -89,6 +89,7 @@
private final UiEventLogger mUiEventLogger;
private final NotificationManager mNotificationManager;
private final UserContextProvider mUserContextTracker;
+ private int mNotificationId = NOTIF_BASE_ID;
@Inject
public RecordingService(RecordingController controller, @LongRunning Executor executor,
@@ -134,14 +135,23 @@
}
String action = intent.getAction();
Log.d(TAG, "onStartCommand " + action);
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID,
+ getString(R.string.screenrecord_title),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription(getString(R.string.screenrecord_channel_description));
+ channel.enableVibration(true);
+ mNotificationManager.createNotificationChannel(channel);
int currentUserId = mUserContextTracker.getUserContext().getUserId();
UserHandle currentUser = new UserHandle(currentUserId);
switch (action) {
case ACTION_START:
+ // Get a unique ID for this recording's notifications
+ mNotificationId = NOTIF_BASE_ID + (int) SystemClock.uptimeMillis();
mAudioSource = ScreenRecordingAudioSource
.values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
- Log.d(TAG, "recording with audio source" + mAudioSource);
+ Log.d(TAG, "recording with audio source " + mAudioSource);
mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
MediaProjectionCaptureTarget captureTarget =
intent.getParcelableExtra(EXTRA_CAPTURE_TARGET,
@@ -169,7 +179,7 @@
} else {
updateState(false);
createErrorNotification();
- stopForeground(true);
+ stopForeground(STOP_FOREGROUND_DETACH);
stopSelf();
return Service.START_NOT_STICKY;
}
@@ -200,7 +210,7 @@
startActivity(Intent.createChooser(shareIntent, shareLabel)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
// Remove notification
- mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
+ mNotificationManager.cancelAsUser(null, mNotificationId, currentUser);
return false;
}, false, false);
@@ -260,24 +270,16 @@
@VisibleForTesting
protected void createErrorNotification() {
Resources res = getResources();
- NotificationChannel channel = new NotificationChannel(
- CHANNEL_ID,
- getString(R.string.screenrecord_name),
- NotificationManager.IMPORTANCE_DEFAULT);
- channel.setDescription(getString(R.string.screenrecord_channel_description));
- channel.enableVibration(true);
- mNotificationManager.createNotificationChannel(channel);
-
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- res.getString(R.string.screenrecord_name));
+ res.getString(R.string.screenrecord_title));
String notificationTitle = res.getString(R.string.screenrecord_start_error);
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(notificationTitle)
.addExtras(extras);
- startForeground(NOTIFICATION_RECORDING_ID, builder.build());
+ startForeground(mNotificationId, builder.build());
}
@VisibleForTesting
@@ -288,17 +290,9 @@
@VisibleForTesting
protected void createRecordingNotification() {
Resources res = getResources();
- NotificationChannel channel = new NotificationChannel(
- CHANNEL_ID,
- getString(R.string.screenrecord_name),
- NotificationManager.IMPORTANCE_DEFAULT);
- channel.setDescription(getString(R.string.screenrecord_channel_description));
- channel.enableVibration(true);
- mNotificationManager.createNotificationChannel(channel);
-
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- res.getString(R.string.screenrecord_name));
+ res.getString(R.string.screenrecord_title));
String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
? res.getString(R.string.screenrecord_ongoing_screen_only)
@@ -323,7 +317,7 @@
.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
.addAction(stopAction)
.addExtras(extras);
- startForeground(NOTIFICATION_RECORDING_ID, builder.build());
+ startForeground(mNotificationId, builder.build());
}
@VisibleForTesting
@@ -335,13 +329,14 @@
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- res.getString(R.string.screenrecord_name));
+ res.getString(R.string.screenrecord_title));
- Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
+ Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setContentTitle(notificationTitle)
.setContentText(
getResources().getString(R.string.screenrecord_background_processing_label))
.setSmallIcon(R.drawable.ic_screenrecord)
+ .setGroup(GROUP_KEY)
.addExtras(extras);
return builder.build();
}
@@ -365,7 +360,7 @@
Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- getResources().getString(R.string.screenrecord_name));
+ getResources().getString(R.string.screenrecord_title));
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
@@ -378,6 +373,7 @@
PendingIntent.FLAG_IMMUTABLE))
.addAction(shareAction)
.setAutoCancel(true)
+ .setGroup(GROUP_KEY)
.addExtras(extras);
// Add thumbnail if available
@@ -391,6 +387,24 @@
return builder.build();
}
+ /**
+ * Adds a group notification so that save notifications from multiple recordings are
+ * grouped together, and the foreground service recording notification is not
+ */
+ private void postGroupNotification(UserHandle currentUser) {
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ getResources().getString(R.string.screenrecord_title));
+ Notification groupNotif = new Notification.Builder(this, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_screenrecord)
+ .setContentTitle(getResources().getString(R.string.screenrecord_save_title))
+ .setGroup(GROUP_KEY)
+ .setGroupSummary(true)
+ .setExtras(extras)
+ .build();
+ mNotificationManager.notifyAsUser(TAG, NOTIF_BASE_ID, groupNotif, currentUser);
+ }
+
private void stopService() {
stopService(USER_ID_NOT_SPECIFIED);
}
@@ -423,27 +437,26 @@
Log.e(TAG, "stopRecording called, but recorder was null");
}
updateState(false);
+ stopForeground(STOP_FOREGROUND_DETACH);
stopSelf();
}
private void saveRecording(int userId) {
UserHandle currentUser = new UserHandle(userId);
- mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID,
+ mNotificationManager.notifyAsUser(null, mNotificationId,
createProcessingNotification(), currentUser);
mLongExecutor.execute(() -> {
try {
Log.d(TAG, "saving recording");
Notification notification = createSaveNotification(getRecorder().save());
- if (!mController.isRecording()) {
- mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification,
- currentUser);
- }
+ postGroupNotification(currentUser);
+ mNotificationManager.notifyAsUser(null, mNotificationId, notification,
+ currentUser);
} catch (IOException e) {
Log.e(TAG, "Error saving screen recording: " + e.getMessage());
showErrorToast(R.string.screenrecord_delete_error);
- } finally {
- mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser);
+ mNotificationManager.cancelAsUser(null, mNotificationId, currentUser);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index efa45a4..2a21aaa 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -100,7 +100,7 @@
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setGravity(Gravity.CENTER);
- setTitle(R.string.screenrecord_name);
+ setTitle(R.string.screenrecord_title);
setContentView(R.layout.screen_record_dialog);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 30509e2..bfaf3d0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -62,8 +62,8 @@
private lateinit var options: Spinner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setDialogTitle(R.string.screenrecord_start_label)
- setStartButtonText(R.string.screenrecord_start_recording)
+ setDialogTitle(R.string.screenrecord_permission_dialog_title)
+ setStartButtonText(R.string.screenrecord_permission_dialog_continue)
setStartButtonOnClickListener { v: View? ->
onStartRecordingClicked?.run()
if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
@@ -186,13 +186,13 @@
return listOf(
ScreenShareOption(
ENTIRE_SCREEN,
- R.string.screenrecord_option_entire_screen,
- R.string.screenrecord_warning_entire_screen
+ R.string.screen_share_permission_dialog_option_entire_screen,
+ R.string.screenrecord_permission_dialog_warning_entire_screen
),
ScreenShareOption(
SINGLE_APP,
- R.string.screenrecord_option_single_app,
- R.string.screenrecord_warning_single_app
+ R.string.screen_share_permission_dialog_option_single_app,
+ R.string.screenrecord_permission_dialog_warning_single_app
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
index b3d31f2..7e0f504 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
@@ -19,12 +19,12 @@
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.dagger.SysUISingleton
/**
* Standard implementation of [CombinedShadeHeadersConstraintManager].
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstraintManager {
override fun privacyChipVisibilityConstraints(visible: Boolean): ConstraintsChanges {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 5117915..4b44ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1513,6 +1513,8 @@
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
+ mKeyguardStatusViewController.setLockscreenClockY(
+ mClockPositionAlgorithm.getExpandedPreferredClockY());
mKeyguardBottomAreaInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
@@ -4474,7 +4476,7 @@
mDisplayTopInset = combinedInsets.top;
mDisplayRightInset = combinedInsets.right;
mDisplayLeftInset = combinedInsets.left;
- mQsController.setDisplayInsets(mDisplayRightInset, mDisplayLeftInset);
+ mQsController.setDisplayInsets(mDisplayLeftInset, mDisplayRightInset);
mNavigationBarBottomHeight = insets.getStableInsetBottom();
updateMaxHeadsUpTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index ef14d1c..a1fa8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -70,6 +70,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -84,11 +85,13 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
@@ -115,6 +118,7 @@
private final PulseExpansionHandler mPulseExpansionHandler;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final LightBarController mLightBarController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final NotificationShadeDepthController mDepthController;
@@ -134,8 +138,10 @@
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final CastController mCastController;
private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private final ShadeRepository mShadeRepository;
private final FalsingManager mFalsingManager;
private final AccessibilityManager mAccessibilityManager;
private final MetricsLogger mMetricsLogger;
@@ -300,6 +306,7 @@
NotificationRemoteInputManager remoteInputManager,
ShadeExpansionStateManager shadeExpansionStateManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ LightBarController lightBarController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationShadeDepthController notificationShadeDepthController,
@@ -321,7 +328,9 @@
FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
ShadeLogger shadeLog,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ ShadeRepository shadeRepository,
+ CastController castController
) {
mPanelViewControllerLazy = panelViewControllerLazy;
mPanelView = panelView;
@@ -340,6 +349,7 @@
mRemoteInputManager = remoteInputManager;
mShadeExpansionStateManager = shadeExpansionStateManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mLightBarController = lightBarController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mDepthController = notificationShadeDepthController;
@@ -361,8 +371,10 @@
mMetricsLogger = metricsLogger;
mShadeLog = shadeLog;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mCastController = castController;
mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
+ mShadeRepository = shadeRepository;
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
}
@@ -875,7 +887,9 @@
}
void setOverScrollAmount(int overExpansion) {
- mQs.setOverScrollAmount(overExpansion);
+ if (mQs != null) {
+ mQs.setOverScrollAmount(overExpansion);
+ }
}
private void setOverScrolling(boolean overscrolling) {
@@ -1001,6 +1015,7 @@
mDepthController.setQsPanelExpansion(qsExpansionFraction);
mStatusBarKeyguardViewManager.setQsExpansion(qsExpansionFraction);
+ mShadeRepository.setQsExpansion(qsExpansionFraction);
// TODO (b/265193930): remove dependency on NPVC
float shadeExpandedFraction = mBarState == KEYGUARD
@@ -1009,6 +1024,9 @@
mShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mShadeHeaderController.setQsVisible(mVisible);
+
+ // Update the light bar
+ mLightBarController.setQsExpanded(mFullyExpanded);
}
float getLockscreenShadeDragProgress() {
@@ -1183,7 +1201,9 @@
mLastClipBounds.set(left, top, right, bottom);
if (mIsFullWidth) {
clipStatusView = qsVisible;
- float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
+ float screenCornerRadius =
+ mRecordingController.isRecording() || mCastController.hasConnectedCastDevice()
+ ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
mScrimController.setNotificationBottomRadius(radius);
@@ -1212,10 +1232,13 @@
mVisible = qsVisible;
mQs.setQsVisible(qsVisible);
mQs.setFancyClipping(
+ mDisplayLeftInset,
clipTop,
+ mDisplayRightInset,
clipBottom,
radius,
- qsVisible && !mSplitShadeEnabled);
+ qsVisible && !mSplitShadeEnabled,
+ mIsFullWidth);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index f4b1cc5..20313c3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import android.annotation.IntDef
+import android.os.Trace
+import android.os.Trace.TRACE_TAG_APP as TRACE_TAG
import android.util.Log
import androidx.annotation.FloatRange
import com.android.systemui.dagger.SysUISingleton
@@ -153,6 +155,14 @@
if (fullyClosed) " fullyClosed" else ""
)
+ if (Trace.isTagEnabled(TRACE_TAG)) {
+ Trace.traceCounter(TRACE_TAG, "panel_expansion", (fraction * 100).toInt())
+ if (state != oldState) {
+ Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK_NAME, 0)
+ Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK_NAME, state.panelStateToString(), 0)
+ }
+ }
+
val expansionChangeEvent =
ShadeExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount)
expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) }
@@ -211,6 +221,10 @@
if (!DEBUG) return
Log.v(TAG, msg)
}
+
+ companion object {
+ private const val TRACK_NAME = "ShadeExpansionState"
+ }
}
/** Enum for the current state of the panel. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index f0815e9..86ae4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -41,6 +41,7 @@
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
@@ -52,14 +53,13 @@
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
+import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
-import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SHADE_HEADER
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.NextAlarmController
@@ -79,7 +79,7 @@
* * [LARGE_SCREEN_HEADER_TRANSITION_ID]: [LARGE_SCREEN_HEADER_CONSTRAINT] for all other
* configurations
*/
-@CentralSurfacesScope
+@SysUISingleton
class ShadeHeaderController
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index 9cd8c54..b7551cf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -16,21 +16,36 @@
package com.android.systemui.shade
+import android.content.ContentResolver
+import android.os.Handler
import android.view.LayoutInflater
+import android.view.ViewStub
+import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.keyguard.LockIconView
import com.android.systemui.CoreStartable
import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.biometrics.AuthRippleView
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tuner.TunerService
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import javax.inject.Named
/** Module for classes related to the notification shade. */
@Module
@@ -42,6 +57,8 @@
abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
companion object {
+ const val SHADE_HEADER = "large_screen_shade_header"
+
@Provides
@SysUISingleton
// TODO(b/277762009): Do something similar to
@@ -109,5 +126,75 @@
): TapAgainView {
return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
}
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesShadeHeaderView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): MotionLayout {
+ val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
+ val layoutId = R.layout.combined_qs_header
+ stub.layoutResource = layoutId
+ return stub.inflate() as MotionLayout
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
+ return CombinedShadeHeadersConstraintManagerImpl
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
+ return view.findViewById(R.id.batteryRemainingIcon)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterViewController(
+ @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
+ userTracker: UserTracker,
+ configurationController: ConfigurationController,
+ tunerService: TunerService,
+ @Main mainHandler: Handler,
+ contentResolver: ContentResolver,
+ featureFlags: FeatureFlags,
+ batteryController: BatteryController,
+ ): BatteryMeterViewController {
+ return BatteryMeterViewController(
+ batteryMeterView,
+ userTracker,
+ configurationController,
+ tunerService,
+ mainHandler,
+ contentResolver,
+ featureFlags,
+ batteryController,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesOngoingPrivacyChip(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): OngoingPrivacyChip {
+ return header.findViewById(R.id.privacy_chip)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesStatusIconContainer(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): StatusIconContainer {
+ return header.findViewById(R.id.statusIcons)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index bf7a2be..44c8e48 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -25,11 +25,23 @@
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
interface ShadeRepository {
/** ShadeModel information regarding shade expansion events */
val shadeModel: Flow<ShadeModel>
+
+ /** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */
+ val qsExpansion: StateFlow<Float>
+
+ /** Amount shade has expanded with regard to the UDFPS location */
+ val udfpsTransitionToFullShadeProgress: StateFlow<Float>
+
+ fun setQsExpansion(qsExpansion: Float)
+ fun setUdfpsTransitionToFullShadeProgress(progress: Float)
}
/** Business logic for shade interactions */
@@ -62,6 +74,20 @@
}
.distinctUntilChanged()
+ private val _qsExpansion = MutableStateFlow(0f)
+ override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
+
+ private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
+ override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
+ _udfpsTransitionToFullShadeProgress.asStateFlow()
+ override fun setQsExpansion(qsExpansion: Float) {
+ _qsExpansion.value = qsExpansion
+ }
+
+ override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
+ _udfpsTransitionToFullShadeProgress.value = progress
+ }
+
companion object {
private const val TAG = "ShadeRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 07b6869..b6970ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -20,6 +20,7 @@
import android.content.res.Resources
import android.os.SystemProperties
import android.os.Trace
+import android.os.Trace.TRACE_TAG_APP
import android.util.IndentingPrintWriter
import android.util.MathUtils
import android.view.CrossWindowBlurListeners
@@ -43,8 +44,8 @@
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
- private val traceCookie = System.identityHashCode(this)
private var lastAppliedBlur = 0
+ private var earlyWakeupEnabled = false
init {
dumpManager.registerDumpable(javaClass.name, this)
@@ -72,6 +73,26 @@
}
/**
+ * This method should be called before [applyBlur] so that, if needed, we can set the
+ * early-wakeup flag in SurfaceFlinger.
+ */
+ fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
+ if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
+ !supportsBlursOnWindows() || earlyWakeupEnabled
+ ) {
+ return
+ }
+ if (lastAppliedBlur == 0 && radius != 0) {
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, EARLY_WAKEUP_SLICE_NAME, 0)
+ earlyWakeupEnabled = true
+ createTransaction().use {
+ it.setEarlyWakeupStart()
+ it.apply()
+ }
+ }
+ }
+
+ /**
* Applies background blurs to a {@link ViewRootImpl}.
*
* @param viewRootImpl The window root.
@@ -85,14 +106,20 @@
createTransaction().use {
if (supportsBlursOnWindows()) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
- if (lastAppliedBlur == 0 && radius != 0) {
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME,
- EARLY_WAKEUP_SLICE_NAME, traceCookie)
+ if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
+ Trace.asyncTraceForTrackBegin(
+ TRACE_TAG_APP,
+ TRACK_NAME,
+ EARLY_WAKEUP_SLICE_NAME,
+ 0
+ )
it.setEarlyWakeupStart()
+ earlyWakeupEnabled = true
}
- if (lastAppliedBlur != 0 && radius == 0) {
+ if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) {
it.setEarlyWakeupEnd()
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, traceCookie)
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0)
+ earlyWakeupEnabled = false
}
lastAppliedBlur = radius
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ef2063..dcd9dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -52,7 +52,6 @@
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
- mView.useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
mView.setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index c5f64b0..7f016f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -32,6 +32,7 @@
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -74,6 +75,7 @@
falsingManager: FalsingManager,
dumpManager: DumpManager,
qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory,
+ private val shadeRepository: ShadeRepository,
) : Dumpable {
private var pulseHeight: Float = 0f
@get:VisibleForTesting
@@ -449,6 +451,7 @@
}
val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance)
+ shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress)
udfpsKeyguardViewController?.setTransitionToFullShadeProgress(udfpsProgress)
val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8dc7842..a3bd247 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -189,12 +189,7 @@
scheduleUpdate()
}
- /**
- * Callback that updates the window blur value and is called only once per frame.
- */
- @VisibleForTesting
- val updateBlurCallback = Choreographer.FrameCallback {
- updateScheduled = false
+ private fun computeBlurAndZoomOut(): Pair<Int, Float> {
val animationRadius = MathUtils.constrain(shadeAnimation.radius,
blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat())
val expansionRadius = blurUtils.blurRadiusOfRatio(
@@ -232,6 +227,16 @@
// Brightness slider removes blur, but doesn't affect zooms
blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
+ return Pair(blur, zoomOut)
+ }
+
+ /**
+ * Callback that updates the window blur value and is called only once per frame.
+ */
+ @VisibleForTesting
+ val updateBlurCallback = Choreographer.FrameCallback {
+ updateScheduled = false
+ val (blur, zoomOut) = computeBlurAndZoomOut()
val opaque = scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
@@ -441,6 +446,8 @@
return
}
updateScheduled = true
+ val (blur, _) = computeBlurAndZoomOut()
+ blurUtils.prepareBlur(root.viewRootImpl, blur)
choreographer.postFrameCallback(updateBlurCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7eb63da..49c7950 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -44,7 +44,6 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
-import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -128,13 +127,6 @@
setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false);
requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false);
-
- if (!mUseRoundnessSourceTypes) {
- // Setting this to first in section to get the clipping to the top roundness correct.
- // This value determines the way we are clipping to the top roundness of the overall
- // shade
- setFirstInSection(true);
- }
updateResources();
}
@@ -569,17 +561,8 @@
* mAmbientState.getExpansionFraction();
final float cornerAnimationTop = shelfStart - cornerAnimationDistance;
- final SourceType sourceType;
- if (mUseRoundnessSourceTypes) {
- sourceType = SHELF_SCROLL;
- } else {
- sourceType = LegacySourceType.OnScroll;
- }
-
final float topValue;
- if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) {
- topValue = 1f;
- } else if (viewStart >= cornerAnimationTop) {
+ if (viewStart >= cornerAnimationTop) {
// Round top corners within animation bounds
topValue = MathUtils.saturate(
(viewStart - cornerAnimationTop) / cornerAnimationDistance);
@@ -588,12 +571,10 @@
// Reset top and bottom corners outside of animation bounds.
topValue = 0f;
}
- anv.requestTopRoundness(topValue, sourceType, /* animate = */ false);
+ anv.requestTopRoundness(topValue, SHELF_SCROLL, /* animate = */ false);
final float bottomValue;
- if (!mUseRoundnessSourceTypes && anv.isLastInSection()) {
- bottomValue = 1f;
- } else if (viewEnd >= cornerAnimationTop) {
+ if (viewEnd >= cornerAnimationTop) {
// Round bottom corners within animation bounds
bottomValue = MathUtils.saturate(
(viewEnd - cornerAnimationTop) / cornerAnimationDistance);
@@ -602,7 +583,7 @@
// Reset top and bottom corners outside of animation bounds.
bottomValue = 0f;
}
- anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false);
+ anv.requestBottomRoundness(bottomValue, SHELF_SCROLL, /* animate = */ false);
}
private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
@@ -1100,15 +1081,6 @@
child.requestRoundnessReset(SHELF_SCROLL);
}
- /**
- * This method resets the OnScroll roundness of a view to 0f
- * <p>
- * Note: This should be the only class that handles roundness {@code SourceType.OnScroll}
- */
- public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) {
- expandableView.requestRoundnessReset(LegacySourceType.OnScroll);
- }
-
@Override
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 976924a..821a172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -87,13 +87,8 @@
bypassController.isPulseExpanding = value
if (changed) {
if (value) {
- val topEntry = headsUpManager.topEntry
- topEntry?.let {
- roundnessManager.setTrackingHeadsUp(it.row)
- }
lockscreenShadeTransitionController.onPulseExpansionStarted()
} else {
- roundnessManager.setTrackingHeadsUp(null)
if (!leavingLockscreen) {
bypassController.maybePerformPendingUnlock()
pulseExpandAbortListener?.run()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 7755003..129c859 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -26,6 +26,7 @@
import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -75,9 +76,9 @@
*/
private static final float DARK_ALPHA_BOOST = 0.67f;
/**
- * Status icons are currently drawn with the intention of being 17dp tall, but we
- * want to scale them (in a way that doesn't require an asset dump) down 2dp. So
- * 17dp * (15 / 17) = 15dp, the new height. After the first call to {@link #reloadDimens} all
+ * Status icons are currently drawn with the intention of being 17sp tall, but we
+ * want to scale them (in a way that doesn't require an asset dump) down 2sp. So
+ * 17sp * (15 / 17) = 15sp, the new height. After the first call to {@link #reloadDimens} all
* values will be in px.
*/
private float mSystemIconDesiredHeight = 15f;
@@ -144,7 +145,7 @@
private String mNumberText;
private StatusBarNotification mNotification;
private final boolean mBlocked;
- private int mDensity;
+ private Configuration mConfiguration;
private boolean mNightMode;
private float mIconScale = 1.0f;
private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -198,9 +199,8 @@
mNumberPain.setAntiAlias(true);
setNotification(sbn);
setScaleType(ScaleType.CENTER);
- mDensity = context.getResources().getDisplayMetrics().densityDpi;
- Configuration configuration = context.getResources().getConfiguration();
- mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
+ mNightMode = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
initializeDecorColor();
reloadDimens();
@@ -214,7 +214,7 @@
mAlwaysScaleIcon = true;
reloadDimens();
maybeUpdateIconScaleDimens();
- mDensity = context.getResources().getDisplayMetrics().densityDpi;
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
}
/** Should always be preceded by {@link #reloadDimens()} */
@@ -231,12 +231,17 @@
private void updateIconScaleForNotifications() {
final float imageBounds = mIncreasedSize ?
mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize;
- final int outerBounds = mStatusBarIconSize;
- mIconScale = imageBounds / (float)outerBounds;
+ float iconHeight = getIconHeight();
+ if (iconHeight != 0) {
+ mIconScale = imageBounds / iconHeight;
+ } else {
+ final int outerBounds = mStatusBarIconSize;
+ mIconScale = imageBounds / (float) outerBounds;
+ }
updatePivot();
}
- // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height
+ // Makes sure that all icons are scaled to the same height (15sp). If we cannot get a height
// for the icon, it uses the default SCALE (15f / 17f) which is the old behavior
private void updateIconScaleForSystemIcons() {
float iconHeight = getIconHeight();
@@ -267,12 +272,10 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDensity = density;
- reloadDimens();
- updateDrawable();
- maybeUpdateIconScaleDimens();
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) {
+ updateIconDimens();
}
boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
@@ -282,6 +285,15 @@
}
}
+ /**
+ * Update the icon dimens and drawable with current resources
+ */
+ public void updateIconDimens() {
+ reloadDimens();
+ updateDrawable();
+ maybeUpdateIconScaleDimens();
+ }
+
private void reloadDimens() {
boolean applyRadius = mDotRadius == mStaticDotRadius;
Resources res = getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 76ff97d..212f2c215 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -448,10 +448,3 @@
}
}
}
-
-@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()"))
-enum class LegacySourceType : SourceType {
- DefaultValue,
- OnDismissAnimation,
- OnScroll,
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 766ad88..66d4c3a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -110,7 +110,6 @@
protected Point mTargetPoint;
private boolean mDismissed;
private boolean mRefocusOnDismiss;
- protected boolean mUseRoundnessSourceTypes;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -709,18 +708,10 @@
mTouchHandler = touchHandler;
}
- /**
- * Enable the support for rounded corner based on the SourceType
- * @param enabled true if is supported
- */
- public void useRoundnessSourceTypes(boolean enabled) {
- mUseRoundnessSourceTypes = enabled;
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) {
+ if (!mOnDetachResetRoundness.isEmpty()) {
for (SourceType sourceType : mOnDetachResetRoundness) {
requestRoundnessReset(sourceType);
}
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 e468a59..5978133 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
@@ -87,7 +87,6 @@
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -866,9 +865,6 @@
}
onAttachedChildrenCountChanged();
row.setIsChildInGroup(false, null);
- if (!mUseRoundnessSourceTypes) {
- row.requestBottomRoundness(0.0f, LegacySourceType.DefaultValue, /* animate = */ false);
- }
}
/**
@@ -884,10 +880,6 @@
if (child.keepInParentForDismissAnimation()) {
mChildrenContainer.removeNotification(child);
child.setIsChildInGroup(false, null);
- if (!mUseRoundnessSourceTypes) {
- LegacySourceType sourceType = LegacySourceType.DefaultValue;
- child.requestBottomRoundness(0f, sourceType, /* animate = */ false);
- }
child.setKeepInParentForDismissAnimation(false);
logKeepInParentChildDetached(child);
childCountChanged = true;
@@ -942,9 +934,7 @@
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
- if (mUseRoundnessSourceTypes) {
- updateBaseRoundness();
- }
+ updateBaseRoundness();
}
@Override
@@ -1054,15 +1044,13 @@
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
- if (mUseRoundnessSourceTypes) {
- if (pinned) {
- // Should be animated if someone explicitly set it to 0 and the row is shown.
- boolean animated = mAnimatePinnedRoundness && isShown();
- requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated);
- } else {
- requestRoundnessReset(PINNED);
- mAnimatePinnedRoundness = true;
- }
+ if (pinned) {
+ // Should be animated if someone explicitly set it to 0 and the row is shown.
+ boolean animated = mAnimatePinnedRoundness && isShown();
+ requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated);
+ } else {
+ requestRoundnessReset(PINNED);
+ mAnimatePinnedRoundness = true;
}
}
@@ -1879,7 +1867,6 @@
mChildrenContainer.setIsLowPriority(mIsLowPriority);
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
- mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
mTranslateableViews.add(mChildrenContainer);
});
@@ -2308,24 +2295,6 @@
mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
}
- @Override
- public float getTopRoundness() {
- if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
- return mTopRoundnessDuringLaunchAnimation;
- }
-
- return super.getTopRoundness();
- }
-
- @Override
- public float getBottomRoundness() {
- if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
- return mBottomRoundnessDuringLaunchAnimation;
- }
-
- return super.getBottomRoundness();
- }
-
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
if (expandAnimationRunning) {
setAboveShelf(true);
@@ -3481,18 +3450,11 @@
private void applyChildrenRoundness() {
if (mIsSummaryWithChildren) {
- if (mUseRoundnessSourceTypes) {
- mChildrenContainer.requestRoundness(
- /* top = */ getTopRoundness(),
- /* bottom = */ getBottomRoundness(),
- /* sourceType = */ FROM_PARENT,
- /* animate = */ false);
- } else {
- mChildrenContainer.requestBottomRoundness(
- getBottomRoundness(),
- LegacySourceType.DefaultValue,
- /* animate = */ false);
- }
+ mChildrenContainer.requestRoundness(
+ /* top = */ getTopRoundness(),
+ /* bottom = */ getBottomRoundness(),
+ /* sourceType = */ FROM_PARENT,
+ /* animate = */ false);
}
}
@@ -3709,24 +3671,10 @@
}
}
- /**
- * Enable the support for rounded corner based on the SourceType
- * @param enabled true if is supported
- */
- @Override
- public void useRoundnessSourceTypes(boolean enabled) {
- super.useRoundnessSourceTypes(enabled);
- if (mChildrenContainer != null) {
- mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
- }
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (mUseRoundnessSourceTypes) {
- updateBaseRoundness();
- }
+ updateBaseRoundness();
}
/** Set whether this notification may show a snooze action. */
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 9bf05cb..1acc9f9 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
@@ -261,8 +261,6 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
});
- mView.useRoundnessSourceTypes(
- mFeatureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
}
private final StatusBarStateController.StateListener mStatusBarStateListener =
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 9a777ea..2c59c2e 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
@@ -72,7 +72,6 @@
private View mFeedbackIcon;
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
- private boolean mUseRoundnessSourceTypes;
private RoundnessChangedListener mRoundnessChangedListener;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
@@ -122,7 +121,7 @@
@Override
public void applyRoundnessAndInvalidate() {
- if (mUseRoundnessSourceTypes && mRoundnessChangedListener != null) {
+ if (mRoundnessChangedListener != null) {
// We cannot apply the rounded corner to this View, so our parents (in drawChild()) will
// clip our canvas. So we should invalidate our parent.
mRoundnessChangedListener.applyRoundnessAndInvalidate();
@@ -377,15 +376,6 @@
}
/**
- * Enable the support for rounded corner based on the SourceType
- *
- * @param enabled true if is supported
- */
- public void useRoundnessSourceTypes(boolean enabled) {
- mUseRoundnessSourceTypes = enabled;
- }
-
- /**
* Interface that handle the Roundness changes
*/
public interface RoundnessChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 99c6ddb..12956ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -81,7 +81,6 @@
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
setRefactorFlagEnabled(true)
- useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES))
setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
// TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
notificationIconAreaController.setShelfIcons(shelfIcons)
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 160a230..d18757d 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
@@ -45,9 +45,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
-import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FeedbackIcon;
-import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.Roundable;
@@ -133,7 +131,6 @@
private int mUntruncatedChildCount;
private boolean mContainingNotificationIsFaded = false;
private RoundableState mRoundableState;
- private boolean mUseRoundnessSourceTypes;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -328,13 +325,6 @@
row.setContentTransformationAmount(0, false /* isLastChild */);
row.setNotificationFaded(mContainingNotificationIsFaded);
- if (!mUseRoundnessSourceTypes) {
- // This is a workaround, the NotificationShelf should be the owner of `OnScroll`
- // roundness.
- // Here we should reset the `OnScroll` roundness only on top-level rows.
- NotificationShelf.resetLegacyOnScrollRoundness(row);
- }
-
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
@@ -342,9 +332,7 @@
row.cancelAppearDrawing();
}
- if (mUseRoundnessSourceTypes) {
- applyRoundnessAndInvalidate();
- }
+ applyRoundnessAndInvalidate();
}
private void ensureRemovedFromTransientContainer(View v) {
@@ -379,10 +367,8 @@
mGroupingUtil.restoreChildNotification(row);
}
- if (mUseRoundnessSourceTypes) {
- row.requestRoundnessReset(FROM_PARENT, /* animate = */ false);
- applyRoundnessAndInvalidate();
- }
+ row.requestRoundnessReset(FROM_PARENT, /* animate = */ false);
+ applyRoundnessAndInvalidate();
}
/**
@@ -409,10 +395,7 @@
getContext(),
mNotificationHeader,
mContainingNotification);
- mNotificationHeaderWrapper.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
- if (mUseRoundnessSourceTypes) {
- mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
- }
+ mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
addView(mNotificationHeader, 0);
invalidate();
} else {
@@ -450,12 +433,7 @@
getContext(),
mNotificationHeaderLowPriority,
mContainingNotification);
- mNotificationHeaderWrapperLowPriority.useRoundnessSourceTypes(
- mUseRoundnessSourceTypes
- );
- if (mUseRoundnessSourceTypes) {
- mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
- }
+ mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
addView(mNotificationHeaderLowPriority, 0);
invalidate();
} else {
@@ -891,7 +869,7 @@
isCanvasChanged = true;
canvas.save();
- if (mUseRoundnessSourceTypes && translation != 0f) {
+ if (translation != 0f) {
clipPath.offset(translation, 0f);
canvas.clipPath(clipPath);
clipPath.offset(-translation, 0f);
@@ -1444,40 +1422,30 @@
@Override
public void applyRoundnessAndInvalidate() {
boolean last = true;
- if (mUseRoundnessSourceTypes) {
- if (mNotificationHeaderWrapper != null) {
- mNotificationHeaderWrapper.requestTopRoundness(
- /* value = */ getTopRoundness(),
- /* sourceType = */ FROM_PARENT,
- /* animate = */ false
- );
- }
- if (mNotificationHeaderWrapperLowPriority != null) {
- mNotificationHeaderWrapperLowPriority.requestTopRoundness(
- /* value = */ getTopRoundness(),
- /* sourceType = */ FROM_PARENT,
- /* animate = */ false
- );
- }
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.requestTopRoundness(
+ /* value = */ getTopRoundness(),
+ /* sourceType = */ FROM_PARENT,
+ /* animate = */ false
+ );
+ }
+ if (mNotificationHeaderWrapperLowPriority != null) {
+ mNotificationHeaderWrapperLowPriority.requestTopRoundness(
+ /* value = */ getTopRoundness(),
+ /* sourceType = */ FROM_PARENT,
+ /* animate = */ false
+ );
}
for (int i = mAttachedChildren.size() - 1; i >= 0; i--) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
if (child.getVisibility() == View.GONE) {
continue;
}
- if (mUseRoundnessSourceTypes) {
- child.requestRoundness(
- /* top = */ 0f,
- /* bottom = */ last ? getBottomRoundness() : 0f,
- /* sourceType = */ FROM_PARENT,
- /* animate = */ false);
- } else {
- child.requestRoundness(
- /* top = */ 0f,
- /* bottom = */ last ? getBottomRoundness() : 0f,
- LegacySourceType.DefaultValue,
- /* animate = */ isShown());
- }
+ child.requestRoundness(
+ /* top = */ 0f,
+ /* bottom = */ last ? getBottomRoundness() : 0f,
+ /* sourceType = */ FROM_PARENT,
+ /* animate = */ false);
last = false;
}
Roundable.super.applyRoundnessAndInvalidate();
@@ -1537,15 +1505,6 @@
return mNotificationHeaderWrapper;
}
- /**
- * Enable the support for rounded corner based on the SourceType
- *
- * @param enabled true if is supported
- */
- public void useRoundnessSourceTypes(boolean enabled) {
- mUseRoundnessSourceTypes = enabled;
- }
-
public String debugString() {
return TAG + " { "
+ "visibility: " + getVisibility()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index fde8c4d..fa1843e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,24 +16,13 @@
package com.android.systemui.statusbar.notification.stack;
-import android.content.res.Resources;
-import android.util.MathUtils;
-
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
-import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.statusbar.notification.LegacySourceType;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.SourceType;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import java.io.PrintWriter;
@@ -50,64 +39,27 @@
private static final String TAG = "NotificationRoundnessManager";
private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation");
- private final ExpandableView[] mFirstInSectionViews;
- private final ExpandableView[] mLastInSectionViews;
- private final ExpandableView[] mTmpFirstInSectionViews;
- private final ExpandableView[] mTmpLastInSectionViews;
- private final NotificationRoundnessLogger mNotifLogger;
private final DumpManager mDumpManager;
- private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
- private Runnable mRoundingChangedCallback;
- private ExpandableNotificationRow mTrackedHeadsUp;
- private float mAppearFraction;
private boolean mRoundForPulsingViews;
private boolean mIsClearAllInProgress;
private ExpandableView mSwipedView = null;
private Roundable mViewBeforeSwipedView = null;
private Roundable mViewAfterSwipedView = null;
- private boolean mUseRoundnessSourceTypes;
@Inject
- NotificationRoundnessManager(
- NotificationSectionsFeatureManager sectionsFeatureManager,
- NotificationRoundnessLogger notifLogger,
- DumpManager dumpManager,
- FeatureFlags featureFlags) {
- int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
- mFirstInSectionViews = new ExpandableView[numberOfSections];
- mLastInSectionViews = new ExpandableView[numberOfSections];
- mTmpFirstInSectionViews = new ExpandableView[numberOfSections];
- mTmpLastInSectionViews = new ExpandableView[numberOfSections];
- mNotifLogger = notifLogger;
+ NotificationRoundnessManager(DumpManager dumpManager) {
mDumpManager = dumpManager;
- mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
-
mDumpManager.registerDumpable(TAG, this);
}
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mFirstInSectionViews: length=" + mFirstInSectionViews.length);
- pw.println(dumpViews(mFirstInSectionViews));
- pw.println("mLastInSectionViews: length=" + mLastInSectionViews.length);
- pw.println(dumpViews(mFirstInSectionViews));
- if (mTrackedHeadsUp != null) {
- pw.println("trackedHeadsUp=" + mTrackedHeadsUp.getEntry());
- }
pw.println("roundForPulsingViews=" + mRoundForPulsingViews);
pw.println("isClearAllInProgress=" + mIsClearAllInProgress);
}
- public void updateView(ExpandableView view, boolean animate) {
- if (mUseRoundnessSourceTypes) return;
- boolean changed = updateViewWithoutCallback(view, animate);
- if (changed) {
- mRoundingChangedCallback.run();
- }
- }
-
public boolean isViewAffectedBySwipe(ExpandableView expandableView) {
return expandableView != null
&& (expandableView == mSwipedView
@@ -115,58 +67,6 @@
|| expandableView == mViewAfterSwipedView);
}
- boolean updateViewWithoutCallback(
- ExpandableView view,
- boolean animate) {
- if (mUseRoundnessSourceTypes) return false;
- if (view == null
- || view == mViewBeforeSwipedView
- || view == mViewAfterSwipedView) {
- return false;
- }
-
- final boolean isTopChanged = view.requestTopRoundness(
- getRoundnessDefaultValue(view, true /* top */),
- LegacySourceType.DefaultValue,
- animate);
-
- final boolean isBottomChanged = view.requestBottomRoundness(
- getRoundnessDefaultValue(view, /* top = */ false),
- LegacySourceType.DefaultValue,
- animate);
-
- final boolean isFirstInSection = isFirstInSection(view);
- final boolean isLastInSection = isLastInSection(view);
-
- view.setFirstInSection(isFirstInSection);
- view.setLastInSection(isLastInSection);
-
- mNotifLogger.onCornersUpdated(view, isFirstInSection,
- isLastInSection, isTopChanged, isBottomChanged);
-
- return (isFirstInSection || isLastInSection) && (isTopChanged || isBottomChanged);
- }
-
- private boolean isFirstInSection(ExpandableView view) {
- if (mUseRoundnessSourceTypes) return false;
- for (int i = 0; i < mFirstInSectionViews.length; i++) {
- if (view == mFirstInSectionViews[i]) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isLastInSection(ExpandableView view) {
- if (mUseRoundnessSourceTypes) return false;
- for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
- if (view == mLastInSectionViews[i]) {
- return true;
- }
- }
- return false;
- }
-
void setViewsAffectedBySwipe(
Roundable viewBefore,
ExpandableView viewSwiped,
@@ -180,34 +80,27 @@
if (mSwipedView != null) oldViews.add(mSwipedView);
if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView);
- final SourceType source;
- if (mUseRoundnessSourceTypes) {
- source = DISMISS_ANIMATION;
- } else {
- source = LegacySourceType.OnDismissAnimation;
- }
-
mViewBeforeSwipedView = viewBefore;
if (viewBefore != null) {
oldViews.remove(viewBefore);
- viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source);
+ viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, DISMISS_ANIMATION);
}
mSwipedView = viewSwiped;
if (viewSwiped != null) {
oldViews.remove(viewSwiped);
- viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source);
+ viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, DISMISS_ANIMATION);
}
mViewAfterSwipedView = viewAfter;
if (viewAfter != null) {
oldViews.remove(viewAfter);
- viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source);
+ viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, DISMISS_ANIMATION);
}
// After setting the current Views, reset the views that are still present in the set.
for (Roundable oldView : oldViews) {
- oldView.requestRoundnessReset(source);
+ oldView.requestRoundnessReset(DISMISS_ANIMATION);
}
}
@@ -229,143 +122,6 @@
return mRoundForPulsingViews;
}
- private float getRoundnessDefaultValue(Roundable view, boolean top) {
- if (mUseRoundnessSourceTypes) return 0f;
-
- if (view == null) {
- return 0f;
- }
- if (view == mViewBeforeSwipedView
- || view == mSwipedView
- || view == mViewAfterSwipedView) {
- return 1f;
- }
- if (view instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) view).canViewBeCleared()
- && mIsClearAllInProgress) {
- return 1.0f;
- }
- if (view instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) view;
- if ((expandableView.isPinned()
- || (expandableView.isHeadsUpAnimatingAway()) && !mExpanded)) {
- return 1.0f;
- }
- if (isFirstInSection(expandableView) && top) {
- return 1.0f;
- }
- if (isLastInSection(expandableView) && !top) {
- return 1.0f;
- }
-
- if (view == mTrackedHeadsUp) {
- // If we're pushing up on a headsup the appear fraction is < 0 and it needs to
- // still be rounded.
- return MathUtils.saturate(1.0f - mAppearFraction);
- }
- if (expandableView.showingPulsing() && mRoundForPulsingViews) {
- return 1.0f;
- }
- if (expandableView.isChildInGroup()) {
- return 0f;
- }
- final Resources resources = expandableView.getResources();
- return resources.getDimension(R.dimen.notification_corner_radius_small)
- / resources.getDimension(R.dimen.notification_corner_radius);
- }
- return 0f;
- }
-
- public void setExpanded(float expandedHeight, float appearFraction) {
- if (mUseRoundnessSourceTypes) return;
- mExpanded = expandedHeight != 0.0f;
- mAppearFraction = appearFraction;
- if (mTrackedHeadsUp != null) {
- updateView(mTrackedHeadsUp, false /* animate */);
- }
- }
-
- public void updateRoundedChildren(NotificationSection[] sections) {
- if (mUseRoundnessSourceTypes) return;
- boolean anyChanged = false;
- for (int i = 0; i < sections.length; i++) {
- mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
- mTmpLastInSectionViews[i] = mLastInSectionViews[i];
- mFirstInSectionViews[i] = sections[i].getFirstVisibleChild();
- mLastInSectionViews[i] = sections[i].getLastVisibleChild();
- }
- anyChanged |= handleRemovedOldViews(sections, mTmpFirstInSectionViews, true);
- anyChanged |= handleRemovedOldViews(sections, mTmpLastInSectionViews, false);
- anyChanged |= handleAddedNewViews(sections, mTmpFirstInSectionViews, true);
- anyChanged |= handleAddedNewViews(sections, mTmpLastInSectionViews, false);
- if (anyChanged) {
- mRoundingChangedCallback.run();
- }
-
- mNotifLogger.onSectionCornersUpdated(sections, anyChanged);
- }
-
- private boolean handleRemovedOldViews(
- NotificationSection[] sections,
- ExpandableView[] oldViews,
- boolean first) {
- if (mUseRoundnessSourceTypes) return false;
- boolean anyChanged = false;
- for (ExpandableView oldView : oldViews) {
- if (oldView != null) {
- boolean isStillPresent = false;
- boolean adjacentSectionChanged = false;
- for (NotificationSection section : sections) {
- ExpandableView newView =
- (first ? section.getFirstVisibleChild()
- : section.getLastVisibleChild());
- if (newView == oldView) {
- isStillPresent = true;
- if (oldView.isFirstInSection() != isFirstInSection(oldView)
- || oldView.isLastInSection() != isLastInSection(oldView)) {
- adjacentSectionChanged = true;
- }
- break;
- }
- }
- if (!isStillPresent || adjacentSectionChanged) {
- anyChanged = true;
- if (!oldView.isRemoved()) {
- updateViewWithoutCallback(oldView, oldView.isShown());
- }
- }
- }
- }
- return anyChanged;
- }
-
- private boolean handleAddedNewViews(
- NotificationSection[] sections,
- ExpandableView[] oldViews,
- boolean first) {
- if (mUseRoundnessSourceTypes) return false;
- boolean anyChanged = false;
- for (NotificationSection section : sections) {
- ExpandableView newView =
- (first ? section.getFirstVisibleChild() : section.getLastVisibleChild());
- if (newView != null) {
- boolean wasAlreadyPresent = false;
- for (ExpandableView oldView : oldViews) {
- if (oldView == newView) {
- wasAlreadyPresent = true;
- break;
- }
- }
- if (!wasAlreadyPresent) {
- anyChanged = true;
- updateViewWithoutCallback(newView,
- newView.isShown() && !mAnimatedChildren.contains(newView));
- }
- }
- }
- return anyChanged;
- }
-
public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
mAnimatedChildren = animatedChildren;
}
@@ -379,51 +135,7 @@
return mAnimatedChildren.contains(view);
}
- public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
- mRoundingChangedCallback = roundingChangedCallback;
- }
-
- public void setTrackingHeadsUp(ExpandableNotificationRow row) {
- ExpandableNotificationRow previous = mTrackedHeadsUp;
- mTrackedHeadsUp = row;
- if (previous != null) {
- updateView(previous, true /* animate */);
- }
- }
-
public void setShouldRoundPulsingViews(boolean shouldRoundPulsingViews) {
mRoundForPulsingViews = shouldRoundPulsingViews;
}
-
- private String dumpViews(ExpandableView[] views) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < views.length; i++) {
- if (views[i] == null) continue;
-
- sb.append("\t")
- .append("[").append(i).append("] ")
- .append("isPinned=").append(views[i].isPinned()).append(" ")
- .append("isFirstInSection=").append(views[i].isFirstInSection()).append(" ")
- .append("isLastInSection=").append(views[i].isLastInSection()).append(" ");
-
- if (views[i] instanceof ExpandableNotificationRow) {
- sb.append("entry=");
- dumpEntry(((ExpandableNotificationRow) views[i]).getEntry(), sb);
- }
-
- sb.append("\n");
- }
- return sb.toString();
- }
-
- private void dumpEntry(NotificationEntry entry, StringBuilder sb) {
- sb.append("NotificationEntry{key=").append(entry.getKey()).append(" ");
-
- if (entry.getSection() != null) {
- sb.append(" section=")
- .append(entry.getSection().getLabel());
- }
-
- sb.append("}");
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 070b439..fd064ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -19,8 +19,6 @@
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.ui.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
@@ -51,12 +49,9 @@
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
- @SilentHeader private val silentHeaderController: SectionHeaderController,
- featureFlags: FeatureFlags
+ @SilentHeader private val silentHeaderController: SectionHeaderController
) : SectionProvider {
- private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
-
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
reinflateViews()
@@ -196,35 +191,33 @@
isSectionChanged || changed
}
- if (useRoundnessSourceTypes) {
- val newFirstChildren = sections.mapNotNull { it.firstVisibleChild }
- val newLastChildren = sections.mapNotNull { it.lastVisibleChild }
+ val newFirstChildren = sections.mapNotNull { it.firstVisibleChild }
+ val newLastChildren = sections.mapNotNull { it.lastVisibleChild }
- // Update the roundness of Views that weren't already in the first/last position
- newFirstChildren.forEach { firstChild ->
- val wasFirstChild = oldFirstChildren.remove(firstChild)
- if (!wasFirstChild) {
- val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild)
- val animated = firstChild.isShown && notAnimatedChild
- firstChild.requestTopRoundness(1f, SECTION, animated)
- }
+ // Update the roundness of Views that weren't already in the first/last position
+ newFirstChildren.forEach { firstChild ->
+ val wasFirstChild = oldFirstChildren.remove(firstChild)
+ if (!wasFirstChild) {
+ val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild)
+ val animated = firstChild.isShown && notAnimatedChild
+ firstChild.requestTopRoundness(1f, SECTION, animated)
}
- newLastChildren.forEach { lastChild ->
- val wasLastChild = oldLastChildren.remove(lastChild)
- if (!wasLastChild) {
- val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild)
- val animated = lastChild.isShown && notAnimatedChild
- lastChild.requestBottomRoundness(1f, SECTION, animated)
- }
+ }
+ newLastChildren.forEach { lastChild ->
+ val wasLastChild = oldLastChildren.remove(lastChild)
+ if (!wasLastChild) {
+ val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild)
+ val animated = lastChild.isShown && notAnimatedChild
+ lastChild.requestBottomRoundness(1f, SECTION, animated)
}
+ }
- // The Views left in the set are no longer in the first/last position
- oldFirstChildren.forEach { noMoreFirstChild ->
- noMoreFirstChild.requestTopRoundness(0f, SECTION)
- }
- oldLastChildren.forEach { noMoreLastChild ->
- noMoreLastChild.requestBottomRoundness(0f, SECTION)
- }
+ // The Views left in the set are no longer in the first/last position
+ oldFirstChildren.forEach { noMoreFirstChild ->
+ noMoreFirstChild.requestTopRoundness(0f, SECTION)
+ }
+ oldLastChildren.forEach { noMoreLastChild ->
+ noMoreLastChild.requestBottomRoundness(0f, SECTION)
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3bc2066..edff877 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -200,7 +200,6 @@
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSimplifiedAppearFraction;
- private final boolean mUseRoundnessSourceTypes;
private final boolean mSensitiveRevealAnimEndabled;
private boolean mAnimatedInsets;
@@ -625,7 +624,6 @@
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION);
- mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
@@ -3134,10 +3132,6 @@
mAnimateNextSectionBoundsChange = false;
}
mAmbientState.setLastVisibleBackgroundChild(lastChild);
- if (!mUseRoundnessSourceTypes) {
- // TODO: Refactor SectionManager and put the RoundnessManager there.
- mController.getNotificationRoundnessManager().updateRoundedChildren(mSections);
- }
mAnimateBottomOnLayout = false;
invalidate();
}
@@ -3584,9 +3578,7 @@
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- StackScrollAlgorithm stackScrollAlgorithm = new StackScrollAlgorithm(context, this);
- stackScrollAlgorithm.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
- return stackScrollAlgorithm;
+ return new StackScrollAlgorithm(context, this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 4751fb6..7b046d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -61,7 +61,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -126,8 +125,6 @@
import com.android.systemui.util.Compile;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Unit;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -195,7 +192,6 @@
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags;
- private final boolean mUseRoundnessSourceTypes;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -594,36 +590,12 @@
}
@Override
- public void onHeadsUpPinned(NotificationEntry entry) {
- if (!mUseRoundnessSourceTypes) {
- mNotificationRoundnessManager.updateView(
- entry.getRow(),
- /* animate = */ false);
- }
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
- if (!mUseRoundnessSourceTypes) {
- ExpandableNotificationRow row = entry.getRow();
- // update the roundedness posted, because we might be animating away the
- // headsup soon, so no need to set the roundedness to 0 and then back to 1.
- row.post(() -> mNotificationRoundnessManager.updateView(row,
- true /* animate */));
- }
- }
-
- @Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
long numEntries = mHeadsUpManager.getAllEntries().count();
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
mView.setNumHeadsUp(numEntries);
mView.setTopHeadsUpEntry(topEntry);
generateHeadsUpAnimation(entry, isHeadsUp);
- if (!mUseRoundnessSourceTypes) {
- ExpandableNotificationRow row = entry.getRow();
- mNotificationRoundnessManager.updateView(row, true /* animate */);
- }
}
};
@@ -723,7 +695,6 @@
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
- mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -791,11 +762,6 @@
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
- if (!mUseRoundnessSourceTypes) {
- mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
- mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
- }
-
mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation);
mTunerService.addTunable(
@@ -822,7 +788,7 @@
mView.generateRemoveAnimation(mKeyguardMediaController.getSinglePaneContainer());
}
mView.requestChildrenUpdate();
- return Unit.INSTANCE;
+ return kotlin.Unit.INSTANCE;
});
// attach callback, and then call it to update mView immediately
@@ -961,7 +927,6 @@
public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
mView.setTrackingHeadsUp(expandableNotificationRow);
- mNotificationRoundnessManager.setTrackingHeadsUp(expandableNotificationRow);
}
public void wakeUpFromPulse() {
@@ -1779,9 +1744,6 @@
@Override
public void bindRow(ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAwayListener(animatingAway -> {
- if (!mUseRoundnessSourceTypes) {
- mNotificationRoundnessManager.updateView(row, false);
- }
NotificationEntry entry = row.getEntry();
mHeadsUpAppearanceController.updateHeader(entry);
mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 91f53b6..993c3801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -35,7 +35,6 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -69,7 +68,6 @@
private boolean mIsExpanded;
private boolean mPulsing;
private final NotificationRoundnessManager mNotificationRoundnessManager;
- private final boolean mUseRoundnessSourceTypes;
NotificationSwipeHelper(
Resources resources,
@@ -81,7 +79,6 @@
NotificationRoundnessManager notificationRoundnessManager) {
super(callback, resources, viewConfiguration, falsingManager, featureFlags);
mNotificationRoundnessManager = notificationRoundnessManager;
- mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mMenuListener = menuListener;
mCallback = callback;
mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
@@ -323,8 +320,7 @@
protected void prepareDismissAnimation(View view, Animator anim) {
super.prepareDismissAnimation(view, anim);
- if (mUseRoundnessSourceTypes
- && view instanceof ExpandableNotificationRow
+ if (view instanceof ExpandableNotificationRow
&& mNotificationRoundnessManager.isClearAllInProgress()) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
index 8b6d6a4f..02662f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
@@ -4,7 +4,6 @@
import androidx.core.view.isVisible
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.Roundable
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -20,7 +19,6 @@
constructor(
featureFlags: FeatureFlags,
) {
- private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
/**
* This method looks for views that can be rounded (and implement [Roundable]) during a
@@ -48,10 +46,6 @@
if (notificationParent != null && childrenContainer != null) {
// We are inside a notification group
- if (!useRoundnessSourceTypes) {
- return RoundableTargets(null, null, null)
- }
-
val visibleGroupChildren = childrenContainer.attachedChildren.filter { it.isVisible }
val indexOfParentSwipedView = visibleGroupChildren.indexOf(viewSwiped)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 3060473..92d767a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -34,7 +34,6 @@
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -75,7 +74,6 @@
private float mQuickQsOffsetHeight;
private float mSmallCornerRadius;
private float mLargeCornerRadius;
- private boolean mUseRoundnessSourceTypes;
public StackScrollAlgorithm(
Context context,
@@ -836,12 +834,8 @@
row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius);
final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius);
- if (mUseRoundnessSourceTypes) {
- row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO);
- row.addOnDetachResetRoundness(STACK_SCROLL_ALGO);
- } else {
- row.requestBottomRoundness(bottomValue, LegacySourceType.OnScroll);
- }
+ row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO);
+ row.addOnDetachResetRoundness(STACK_SCROLL_ALGO);
}
@VisibleForTesting
@@ -979,14 +973,6 @@
this.mIsExpanded = isExpanded;
}
- /**
- * Enable the support for rounded corner based on the SourceType
- * @param enabled true if is supported
- */
- public void useRoundnessSourceTypes(boolean enabled) {
- mUseRoundnessSourceTypes = enabled;
- }
-
public static class StackScrollAlgorithmState {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index e705afc..f15dcc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -27,8 +27,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -78,7 +76,6 @@
private final DarkIconDispatcher mDarkIconDispatcher;
private final ShadeViewController mShadeViewController;
private final NotificationRoundnessManager mNotificationRoundnessManager;
- private final boolean mUseRoundnessSourceTypes;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
@@ -120,14 +117,12 @@
NotificationStackScrollLayoutController stackScrollerController,
ShadeViewController shadeViewController,
NotificationRoundnessManager notificationRoundnessManager,
- FeatureFlags featureFlags,
HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
mNotificationRoundnessManager = notificationRoundnessManager;
- mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mHeadsUpManager = headsUpManager;
// We may be mid-HUN-expansion when this controller is re-created (for example, if the user
@@ -408,21 +403,19 @@
* @param entry target notification
*/
public void updateHeadsUpAndPulsingRoundness(NotificationEntry entry) {
- if (mUseRoundnessSourceTypes) {
- ExpandableNotificationRow row = entry.getRow();
- boolean isTrackedChild = row == mTrackedChild;
- if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) {
- float roundness = MathUtils.saturate(1f - mAppearFraction);
- row.requestRoundness(roundness, roundness, HEADS_UP);
+ ExpandableNotificationRow row = entry.getRow();
+ boolean isTrackedChild = row == mTrackedChild;
+ if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) {
+ float roundness = MathUtils.saturate(1f - mAppearFraction);
+ row.requestRoundness(roundness, roundness, HEADS_UP);
+ } else {
+ row.requestRoundnessReset(HEADS_UP);
+ }
+ if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) {
+ if (row.showingPulsing()) {
+ row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING);
} else {
- row.requestRoundnessReset(HEADS_UP);
- }
- if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) {
- if (row.showingPulsing()) {
- row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING);
- } else {
- row.requestRoundnessReset(PULSING);
- }
+ row.requestRoundnessReset(PULSING);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 90a6d0f..eba04f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -239,7 +239,11 @@
}
}
- private int getExpandedPreferredClockY() {
+ /**
+ * give the static topMargin, used for lockscreen clocks to get the initial translationY
+ * to do counter translation
+ */
+ public int getExpandedPreferredClockY() {
if (mIsSplitShade) {
return mSplitShadeTargetTopMargin;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index c17366a..74ab47f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -115,7 +115,9 @@
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
- val shouldListen = (onKeyguard || bouncerVisible) && keyguardUpdateMonitor.isFaceEnrolled
+ val userId = KeyguardUpdateMonitor.getCurrentUser()
+ val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
+ val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
if (shouldListen != isListening) {
isListening = shouldListen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 534edb9..a058bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -25,6 +25,7 @@
import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Rect;
+import android.util.Log;
import android.view.InsetsFlags;
import android.view.ViewDebug;
import android.view.WindowInsetsController.Appearance;
@@ -35,13 +36,17 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.Compile;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Date;
import javax.inject.Inject;
@@ -51,10 +56,14 @@
@SysUISingleton
public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
+ private static final String TAG = "LightBarController";
+ private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+
private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
private final SysuiDarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
+ private final boolean mUseNewLightBarLogic;
private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
@@ -67,13 +76,17 @@
private final int mLightIconColor;
/**
- * Whether the navigation bar should be light factoring in already how much alpha the scrim has
+ * Whether the navigation bar should be light factoring in already how much alpha the scrim has.
+ * "Light" refers to the background color of the navigation bar, so when this is true,
+ * it's referring to a state where the navigation bar icons are tinted dark.
*/
private boolean mNavigationLight;
/**
- * Whether the flags indicate that a light status bar is requested. This doesn't factor in the
- * scrim alpha yet.
+ * Whether the flags indicate that a light navigation bar is requested.
+ * "Light" refers to the background color of the navigation bar, so when this is true,
+ * it's referring to a state where the navigation bar icons would be tinted dark.
+ * This doesn't factor in the scrim alpha yet.
*/
private boolean mHasLightNavigationBar;
@@ -82,22 +95,34 @@
* {@link #mNavigationLight} {@code false}.
*/
private boolean mForceDarkForScrim;
+ /**
+ * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make
+ * {@link #mNavigationLight} {@code true}.
+ */
+ private boolean mForceLightForScrim;
private boolean mQsCustomizing;
+ private boolean mQsExpanded;
+ private boolean mGlobalActionsVisible;
private boolean mDirectReplying;
private boolean mNavbarColorManagedByIme;
private boolean mIsCustomizingForBackNav;
+ private String mLastSetScrimStateLog;
+ private String mLastNavigationBarAppearanceChangedLog;
+
@Inject
public LightBarController(
Context ctx,
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ FeatureFlags featureFlags,
DumpManager dumpManager,
DisplayTracker displayTracker) {
+ mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC);
mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
@@ -159,9 +184,42 @@
final boolean last = mNavigationLight;
mHasLightNavigationBar = isLight(appearance, navigationBarMode,
APPEARANCE_LIGHT_NAVIGATION_BARS);
- mNavigationLight = mHasLightNavigationBar
- && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
- && !mQsCustomizing;
+ if (mUseNewLightBarLogic) {
+ final boolean ignoreScrimForce = mDirectReplying && mNavbarColorManagedByIme;
+ final boolean darkForScrim = mForceDarkForScrim && !ignoreScrimForce;
+ final boolean lightForScrim = mForceLightForScrim && !ignoreScrimForce;
+ final boolean darkForQs = mQsCustomizing || mQsExpanded || mGlobalActionsVisible;
+ mNavigationLight =
+ ((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForQs;
+ mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
+ + " appearance=" + appearance
+ + " nbModeChanged=" + nbModeChanged
+ + " navigationBarMode=" + navigationBarMode
+ + " navbarColorManagedByIme=" + navbarColorManagedByIme
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " ignoreScrimForce=" + ignoreScrimForce
+ + " darkForScrim=" + darkForScrim
+ + " lightForScrim=" + lightForScrim
+ + " darkForQs=" + darkForQs
+ + " mNavigationLight=" + mNavigationLight
+ + " last=" + last
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ } else {
+ mNavigationLight = mHasLightNavigationBar
+ && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
+ && !mQsCustomizing;
+ mLastNavigationBarAppearanceChangedLog = "onNavigationBarAppearanceChanged()"
+ + " appearance=" + appearance
+ + " nbModeChanged=" + nbModeChanged
+ + " navigationBarMode=" + navigationBarMode
+ + " navbarColorManagedByIme=" + navbarColorManagedByIme
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mNavigationLight=" + mNavigationLight
+ + " last=" + last
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastNavigationBarAppearanceChangedLog);
+ }
if (mNavigationLight != last) {
updateNavigation();
}
@@ -188,6 +246,20 @@
reevaluate();
}
+ /** Set if Quick Settings is fully expanded, which affects notification scrim visibility */
+ public void setQsExpanded(boolean expanded) {
+ if (mQsExpanded == expanded) return;
+ mQsExpanded = expanded;
+ reevaluate();
+ }
+
+ /** Set if Global Actions dialog is visible, which requires dark mode (light buttons) */
+ public void setGlobalActionsVisible(boolean visible) {
+ if (mGlobalActionsVisible == visible) return;
+ mGlobalActionsVisible = visible;
+ reevaluate();
+ }
+
/**
* Controls the light status bar temporarily for back navigation.
* @param appearance the custmoized appearance.
@@ -225,16 +297,52 @@
public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
GradientColors scrimInFrontColor) {
- boolean forceDarkForScrimLast = mForceDarkForScrim;
- // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
- // This enables IMEs to control the navigation bar color.
- // For other cases, scrim should be able to veto the light navigation bar.
- mForceDarkForScrim = scrimState != ScrimState.BOUNCER
- && scrimState != ScrimState.BOUNCER_SCRIMMED
- && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD
- && !scrimInFrontColor.supportsDarkText();
- if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
- reevaluate();
+ if (mUseNewLightBarLogic) {
+ boolean forceDarkForScrimLast = mForceDarkForScrim;
+ boolean forceLightForScrimLast = mForceLightForScrim;
+ final boolean forceForScrim =
+ scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+ final boolean scrimColorIsLight = scrimInFrontColor.supportsDarkText();
+
+ mForceDarkForScrim = forceForScrim && !scrimColorIsLight;
+ mForceLightForScrim = forceForScrim && scrimColorIsLight;
+ if (mHasLightNavigationBar) {
+ if (mForceDarkForScrim != forceDarkForScrimLast) reevaluate();
+ } else {
+ if (mForceLightForScrim != forceLightForScrimLast) reevaluate();
+ }
+ mLastSetScrimStateLog = "setScrimState()"
+ + " scrimState=" + scrimState
+ + " scrimBehindAlpha=" + scrimBehindAlpha
+ + " scrimInFrontColor=" + scrimInFrontColor
+ + " forceForScrim=" + forceForScrim
+ + " scrimColorIsLight=" + scrimColorIsLight
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mForceDarkForScrim=" + mForceDarkForScrim
+ + " mForceLightForScrim=" + mForceLightForScrim
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
+ } else {
+ boolean forceDarkForScrimLast = mForceDarkForScrim;
+ // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
+ // This enables IMEs to control the navigation bar color.
+ // For other cases, scrim should be able to veto the light navigation bar.
+ // NOTE: this was also wrong for S and has been removed in the new logic.
+ mForceDarkForScrim = scrimState != ScrimState.BOUNCER
+ && scrimState != ScrimState.BOUNCER_SCRIMMED
+ && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD
+ && !scrimInFrontColor.supportsDarkText();
+ if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
+ reevaluate();
+ }
+ mLastSetScrimStateLog = "setScrimState()"
+ + " scrimState=" + scrimState
+ + " scrimBehindAlpha=" + scrimBehindAlpha
+ + " scrimInFrontColor=" + scrimInFrontColor
+ + " mHasLightNavigationBar=" + mHasLightNavigationBar
+ + " mForceDarkForScrim=" + mForceDarkForScrim
+ + " timestamp=" + new Date();
+ if (DEBUG) Log.d(TAG, mLastSetScrimStateLog);
}
}
@@ -309,16 +417,24 @@
pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight);
}
- pw.print(" mNavigationLight="); pw.print(mNavigationLight);
+ pw.print(" mNavigationLight="); pw.println(mNavigationLight);
pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar);
-
+ pw.println();
pw.print(" mStatusBarMode="); pw.print(mStatusBarMode);
pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode);
-
- pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
- pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
+ pw.println();
+ pw.print(" mForceDarkForScrim="); pw.println(mForceDarkForScrim);
+ pw.print(" mForceLightForScrim="); pw.println(mForceLightForScrim);
+ pw.println();
+ pw.print(" mQsCustomizing="); pw.println(mQsCustomizing);
+ pw.print(" mQsExpanded="); pw.println(mQsExpanded);
+ pw.print(" mGlobalActionsVisible="); pw.println(mGlobalActionsVisible);
pw.print(" mDirectReplying="); pw.println(mDirectReplying);
pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme);
+ pw.println();
+ pw.println(" Recent Calculation Logs:");
+ pw.print(" "); pw.println(mLastSetScrimStateLog);
+ pw.print(" "); pw.println(mLastNavigationBarAppearanceChangedLog);
pw.println();
@@ -344,6 +460,7 @@
private final DarkIconDispatcher mDarkIconDispatcher;
private final BatteryController mBatteryController;
private final NavigationModeController mNavModeController;
+ private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
private final DisplayTracker mDisplayTracker;
@@ -352,12 +469,14 @@
DarkIconDispatcher darkIconDispatcher,
BatteryController batteryController,
NavigationModeController navModeController,
+ FeatureFlags featureFlags,
DumpManager dumpManager,
DisplayTracker displayTracker) {
mDarkIconDispatcher = darkIconDispatcher;
mBatteryController = batteryController;
mNavModeController = navModeController;
+ mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mDisplayTracker = displayTracker;
}
@@ -365,7 +484,7 @@
/** Create an {@link LightBarController} */
public LightBarController create(Context context) {
return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
- mNavModeController, mDumpManager, mDisplayTracker);
+ mNavModeController, mFeatureFlags, mDumpManager, mDisplayTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 006a029d..b9a12e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -306,7 +306,7 @@
public void applyIconStates() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- ViewState childState = mIconStates.get(child);
+ IconState childState = mIconStates.get(child);
if (childState != null) {
childState.applyToView(child);
}
@@ -339,6 +339,7 @@
}
}
if (child instanceof StatusBarIconView) {
+ ((StatusBarIconView) child).updateIconDimens();
((StatusBarIconView) child).setDozing(mDozing, false, 0);
}
}
@@ -447,9 +448,14 @@
@VisibleForTesting
boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
float iconSize) {
- // Layout end, as used here, does not include padding end.
- final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
- return translationX >= overflowX;
+ if (isLastChild) {
+ return translationX + iconSize > layoutEnd;
+ } else {
+ // If the child is not the last child, we need to ensure that we have room for the next
+ // icon and the dot. The dot could be as large as an icon, so verify that we have room
+ // for 2 icons.
+ return translationX + iconSize * 2f > layoutEnd;
+ }
}
/**
@@ -489,10 +495,7 @@
// First icon to overflow.
if (firstOverflowIndex == -1 && isOverflowing) {
firstOverflowIndex = i;
- mVisualOverflowStart = layoutEnd - mIconSize;
- if (forceOverflow || mIsStaticLayout) {
- mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
- }
+ mVisualOverflowStart = translationX;
}
final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
? ((StatusBarIconView) view).getIconScaleIncreased()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 51c56a0..fdb772b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -42,6 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -249,6 +250,7 @@
private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
+ private final boolean mUseNewLightBarLogic;
private Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
@@ -306,6 +308,7 @@
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
+ mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC);
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
@@ -1159,7 +1162,13 @@
if (mClipsQsScrim && mQsBottomVisible) {
alpha = mNotificationsAlpha;
}
- mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors());
+ if (mUseNewLightBarLogic) {
+ mScrimStateListener.accept(mState, alpha, mColors);
+ } else {
+ // NOTE: This wasn't wrong, but it implied that each scrim might have different colors,
+ // when in fact they all share the same GradientColors instance, which we own.
+ mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors());
+ }
}
private void dispatchScrimsVisible() {
@@ -1487,8 +1496,15 @@
int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor();
mColors.setMainColor(background);
mColors.setSecondaryColor(accent);
- mColors.setSupportsDarkText(
- ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5);
+ if (mUseNewLightBarLogic) {
+ final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background);
+ mColors.setSupportsDarkText(isBackgroundLight);
+ } else {
+ // NOTE: This was totally backward, but LightBarController was flipping it back.
+ // There may be other consumers of this which would struggle though
+ mColors.setSupportsDarkText(
+ ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5);
+ }
mNeedsDrawableColorUpdate = true;
}
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 a8a834f..678873c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -203,8 +203,7 @@
@Override
protected LayoutParams onCreateLayoutParams() {
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ LinearLayout.LayoutParams lp = super.onCreateLayoutParams();
lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
return lp;
}
@@ -370,7 +369,7 @@
private final MobileIconsViewModel mMobileIconsViewModel;
protected final Context mContext;
- protected final int mIconSize;
+ protected int mIconSize;
// Whether or not these icons show up in dumpsys
protected boolean mShouldLog = false;
private StatusBarIconController mController;
@@ -395,10 +394,10 @@
mStatusBarPipelineFlags = statusBarPipelineFlags;
mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
- mIconSize = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_icon_size);
mLocation = location;
+ reloadDimens();
+
if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
// This starts the flow for the new pipeline, and will notify us of changes if
// {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
@@ -609,13 +608,9 @@
mGroup.removeAllViews();
}
- protected void onDensityOrFontScaleChanged() {
- for (int i = 0; i < mGroup.getChildCount(); i++) {
- View child = mGroup.getChildAt(i);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
- child.setLayoutParams(lp);
- }
+ protected void reloadDimens() {
+ mIconSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_icon_size);
}
private void setHeightAndCenter(ImageView imageView, int height) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 3a18423..80d5651 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -109,6 +109,7 @@
}
group.setController(this);
+ group.reloadDimens();
mIconGroups.add(group);
List<Slot> allSlots = mStatusBarIconList.getSlots();
for (int i = 0; i < allSlots.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 89dddbf..f2fbd7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -136,7 +136,7 @@
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
- private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
+ KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -430,6 +430,7 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
+ mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
}
/** Register a callback, to be invoked by the Predictive Back system. */
@@ -1564,6 +1565,14 @@
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
+ private KeyguardStateController.Callback mKeyguardStateControllerCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
+ }
+ };
+
/**
* Delegate used to send show and hide events to an alternate authentication method instead of
* the regular pin/pattern/password bouncer.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 26c1767..ddbfd43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -22,6 +22,8 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -72,13 +74,16 @@
// Any ignored icon will never be added as a child
private ArrayList<String> mIgnoredSlots = new ArrayList<>();
+ private Configuration mConfiguration;
+
public StatusIconContainer(Context context) {
this(context, null);
}
public StatusIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
- initDimens();
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
+ reloadDimens();
setWillNotDraw(!DEBUG_OVERFLOW);
}
@@ -95,7 +100,7 @@
return mShouldRestrictIcons;
}
- private void initDimens() {
+ private void reloadDimens() {
// This is the same value that StatusBarIconView uses
mIconDotFrameWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
@@ -211,6 +216,16 @@
child.setTag(R.id.status_bar_view_state_tag, null);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) {
+ reloadDimens();
+ }
+ }
+
/**
* Add a name of an icon slot to be ignored. It will not show up nor be measured
* @param slotName name of the icon as it exists in
@@ -348,13 +363,17 @@
int totalVisible = mLayoutStates.size();
int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
- mUnderflowStart = 0;
+ // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon.
+ // It to prevent if the underflow happens at rightest(totalVisible - 1) child then break the
+ // for loop with mUnderflowStart staying 0(initial value), causing the dot be placed at the
+ // leftest side.
+ mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth);
int visible = 0;
int firstUnderflowIndex = -1;
for (int i = totalVisible - 1; i >= 0; i--) {
StatusIconState state = mLayoutStates.get(i);
// Allow room for underflow if we found we need it in onMeasure
- if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))
+ if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)))
|| (mShouldRestrictIcons && (visible >= maxVisible))) {
firstUnderflowIndex = i;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 2c57a26..67243b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -16,26 +16,15 @@
package com.android.systemui.statusbar.phone.dagger;
-import android.content.ContentResolver;
-import android.os.Handler;
import android.view.LayoutInflater;
-import android.view.ViewStub;
-
-import androidx.constraintlayout.motion.widget.MotionLayout;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.CombinedShadeHeadersConstraintManager;
-import com.android.systemui.shade.CombinedShadeHeadersConstraintManagerImpl;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
@@ -59,17 +48,13 @@
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
-import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -90,7 +75,6 @@
})
public abstract class StatusBarViewModule {
- public static final String SHADE_HEADER = "large_screen_shade_header";
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */
@@ -136,76 +120,6 @@
abstract ShadeViewController bindsShadeViewController(
NotificationPanelViewController notificationPanelViewController);
- @Provides
- @Named(SHADE_HEADER)
- @CentralSurfacesComponent.CentralSurfacesScope
- public static MotionLayout getLargeScreenShadeHeaderBarView(
- NotificationShadeWindowView notificationShadeWindowView,
- FeatureFlags featureFlags) {
- ViewStub stub = notificationShadeWindowView.findViewById(R.id.qs_header_stub);
- int layoutId = R.layout.combined_qs_header;
- stub.setLayoutResource(layoutId);
- MotionLayout v = (MotionLayout) stub.inflate();
- return v;
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static CombinedShadeHeadersConstraintManager
- provideCombinedShadeHeadersConstraintManager() {
- return CombinedShadeHeadersConstraintManagerImpl.INSTANCE;
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
- @Named(SHADE_HEADER) MotionLayout header) {
- return header.findViewById(R.id.privacy_chip);
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- static StatusIconContainer providesStatusIconContainer(
- @Named(SHADE_HEADER) MotionLayout header) {
- return header.findViewById(R.id.statusIcons);
- }
-
- /** */
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- @Named(SHADE_HEADER)
- static BatteryMeterView getBatteryMeterView(@Named(SHADE_HEADER) MotionLayout view) {
- return view.findViewById(R.id.batteryRemainingIcon);
- }
-
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- @Named(SHADE_HEADER)
- static BatteryMeterViewController getBatteryMeterViewController(
- @Named(SHADE_HEADER) BatteryMeterView batteryMeterView,
- UserTracker userTracker,
- ConfigurationController configurationController,
- TunerService tunerService,
- @Main Handler mainHandler,
- ContentResolver contentResolver,
- FeatureFlags featureFlags,
- BatteryController batteryController
- ) {
- return new BatteryMeterViewController(
- batteryMeterView,
- userTracker,
- configurationController,
- tunerService,
- mainHandler,
- contentResolver,
- featureFlags,
- batteryController);
-
- }
-
/** */
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index 98cde2a..abedd3220 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -28,6 +28,11 @@
void startCasting(CastDevice device);
void stopCasting(CastDevice device);
+ /**
+ * @return whether we have a connected device.
+ */
+ boolean hasConnectedCastDevice();
+
public interface Callback {
void onCastDevicesChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 7290a1a..f7b601b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -217,6 +217,12 @@
}
}
+ @Override
+ public boolean hasConnectedCastDevice() {
+ return getCastDevices().stream().anyMatch(
+ castDevice -> castDevice.state == CastDevice.STATE_CONNECTED);
+ }
+
private void setProjection(MediaProjectionInfo projection, boolean started) {
boolean changed = false;
final MediaProjectionInfo oldProjection = mProjection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
index aa502bc..f61f3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
@@ -34,6 +34,8 @@
private val halfFoldedStates =
context.resources.getIntArray(R.array.config_halfFoldedDeviceStates)
private val unfoldedStates = context.resources.getIntArray(R.array.config_openDeviceStates)
+ private val rearDisplayStates =
+ context.resources.getIntArray(R.array.config_rearDisplayDeviceStates)
fun logListeningChange(listening: Boolean) {
logBuffer.log(TAG, VERBOSE, { bool1 = listening }, { "setListening: $bool1" })
@@ -122,6 +124,7 @@
in foldedStates -> "Folded"
in unfoldedStates -> "Unfolded"
in halfFoldedStates -> "Half-Folded"
+ in rearDisplayStates -> "Rear display"
-1 -> "Uninitialized"
else -> "Unknown"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 3d811cf..673819b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -240,7 +240,7 @@
|| (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
- boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceEnrolled();
+ boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
|| trustManaged != mTrustManaged || mTrusted != trusted
|| mFaceAuthEnabled != faceAuthEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 412b315..27aaa68 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -22,7 +22,6 @@
import android.hardware.BatteryState
import android.hardware.input.InputManager
import android.hardware.input.InputSettings
-import android.os.Build
import android.os.Handler
import android.util.ArrayMap
import android.util.Log
@@ -35,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.shared.hardware.hasInputDevice
import com.android.systemui.shared.hardware.isInternalStylusSource
import java.util.concurrent.CopyOnWriteArrayList
@@ -81,7 +81,7 @@
fun startListener() {
handler.post {
if (hasStarted) return@post
- logDebug { "Listener has started." }
+ debugLog { "Listener has started." }
hasStarted = true
isInUsiSession =
@@ -109,7 +109,7 @@
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
- logDebug {
+ debugLog {
"Stylus InputDevice added: $deviceId ${device.name}, " +
"External: ${device.isExternal}"
}
@@ -134,7 +134,7 @@
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
- logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" }
+ debugLog { "Stylus InputDevice changed: $deviceId ${device.name}" }
val currAddress: String? = device.bluetoothAddress
val prevAddress: String? = inputDeviceAddressMap[deviceId]
@@ -155,7 +155,7 @@
if (!hasStarted) return
if (!inputDeviceAddressMap.contains(deviceId)) return
- logDebug { "Stylus InputDevice removed: $deviceId" }
+ debugLog { "Stylus InputDevice removed: $deviceId" }
unregisterBatteryListener(deviceId)
@@ -180,7 +180,7 @@
val isCharging = String(value) == "true"
- logDebug {
+ debugLog {
"Charging state metadata changed for device $inputDeviceId " +
"${device.address}: $isCharging"
}
@@ -199,7 +199,7 @@
handler.post {
if (!hasStarted) return@post
- logDebug {
+ debugLog {
"Battery state changed for $deviceId. " +
"batteryState present: ${batteryState.isPresent}, " +
"capacity: ${batteryState.capacity}"
@@ -247,7 +247,7 @@
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
if (InputSettings.isStylusEverUsed(context)) return
- logDebug { "Stylus used for the first time." }
+ debugLog { "Stylus used for the first time." }
InputSettings.setStylusEverUsed(context, true)
executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
}
@@ -264,7 +264,7 @@
val hasBtConnection = if (inputDeviceBtSessionIdMap.isEmpty()) 0 else 1
if (batteryStateValid && usiSessionId == null) {
- logDebug { "USI battery newly present, entering new USI session: $deviceId" }
+ debugLog { "USI battery newly present, entering new USI session: $deviceId" }
usiSessionId = instanceIdSequence.newInstanceId()
uiEventLogger.logWithInstanceIdAndPosition(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
@@ -274,7 +274,7 @@
hasBtConnection,
)
} else if (!batteryStateValid && usiSessionId != null) {
- logDebug { "USI battery newly absent, exiting USI session: $deviceId" }
+ debugLog { "USI battery newly absent, exiting USI session: $deviceId" }
uiEventLogger.logWithInstanceIdAndPosition(
StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
0,
@@ -291,7 +291,7 @@
btAddress: String,
btConnected: Boolean
) {
- logDebug {
+ debugLog {
"Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" +
" $deviceId $btAddress"
}
@@ -386,9 +386,3 @@
val TAG = StylusManager::class.simpleName.orEmpty()
}
}
-
-private inline fun logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) {
- Log.d(StylusManager.TAG, message())
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 21b0efa..6eddd9e 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -26,7 +26,6 @@
import android.content.IntentFilter
import android.hardware.BatteryState
import android.hardware.input.InputManager
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.UserHandle
@@ -40,6 +39,7 @@
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.shared.hardware.hasInputDevice
import com.android.systemui.shared.hardware.isAnyStylusSource
import com.android.systemui.util.NotificationChannels
@@ -110,7 +110,7 @@
inputDeviceId = deviceId
batteryCapacity = batteryState.capacity
- logDebug {
+ debugLog {
"Updating notification battery state to $batteryCapacity " +
"for InputDevice $deviceId."
}
@@ -130,14 +130,14 @@
handler.post updateSuppressed@{
if (suppressed == suppress) return@updateSuppressed
- logDebug { "Updating notification suppression to $suppress." }
+ debugLog { "Updating notification suppression to $suppress." }
suppressed = suppress
refresh()
}
}
private fun hideNotification() {
- logDebug { "Cancelling USI low battery notification." }
+ debugLog { "Cancelling USI low battery notification." }
instanceId = null
notificationManager.cancel(USI_NOTIFICATION_ID)
}
@@ -160,7 +160,7 @@
.setAutoCancel(true)
.build()
- logDebug { "Show or update USI low battery notification at $batteryCapacity." }
+ debugLog { "Show or update USI low battery notification at $batteryCapacity." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN)
notificationManager.notify(USI_NOTIFICATION_ID, notification)
}
@@ -188,12 +188,12 @@
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_DISMISSED_LOW_BATTERY -> {
- logDebug { "USI low battery notification dismissed." }
+ debugLog { "USI low battery notification dismissed." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED)
updateSuppression(true)
}
ACTION_CLICKED_LOW_BATTERY -> {
- logDebug { "USI low battery notification clicked." }
+ debugLog { "USI low battery notification clicked." }
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED)
updateSuppression(true)
if (inputDeviceId == null) return
@@ -263,9 +263,3 @@
@VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args"
}
}
-
-private inline fun logDebug(message: () -> String) {
- if (Build.IS_DEBUGGABLE) {
- Log.d(StylusUsiPowerUI.TAG, message())
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index 6026d2c..f0fa779 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.theme
import android.util.Pair
@@ -24,68 +25,73 @@
@JvmField
val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> =
arrayListOf(
- Pair.create("primary_container", MDC.primaryContainer),
- Pair.create("on_primary_container", MDC.onPrimaryContainer),
- Pair.create("primary", MDC.primary),
- Pair.create("on_primary", MDC.onPrimary),
- Pair.create("secondary_container", MDC.secondaryContainer),
- Pair.create("on_secondary_container", MDC.onSecondaryContainer),
- Pair.create("secondary", MDC.secondary),
- Pair.create("on_secondary", MDC.onSecondary),
- Pair.create("tertiary_container", MDC.tertiaryContainer),
- Pair.create("on_tertiary_container", MDC.onTertiaryContainer),
- Pair.create("tertiary", MDC.tertiary),
- Pair.create("on_tertiary", MDC.onTertiary),
- Pair.create("background", MDC.background),
- Pair.create("on_background", MDC.onBackground),
- Pair.create("surface", MDC.surface),
- Pair.create("on_surface", MDC.onSurface),
- Pair.create("surface_container_low", MDC.surfaceContainerLow),
- Pair.create("surface_container_lowest", MDC.surfaceContainerLowest),
- Pair.create("surface_container", MDC.surfaceContainer),
- Pair.create("surface_container_high", MDC.surfaceContainerHigh),
- Pair.create("surface_container_highest", MDC.surfaceContainerHighest),
- Pair.create("surface_bright", MDC.surfaceBright),
- Pair.create("surface_dim", MDC.surfaceDim),
- Pair.create("surface_variant", MDC.surfaceVariant),
- Pair.create("on_surface_variant", MDC.onSurfaceVariant),
- Pair.create("outline", MDC.outline),
- Pair.create("outline_variant", MDC.outlineVariant),
- Pair.create("error", MDC.error),
- Pair.create("on_error", MDC.onError),
- Pair.create("error_container", MDC.errorContainer),
- Pair.create("on_error_container", MDC.onErrorContainer),
- Pair.create("primary_fixed", MDC.primaryFixed),
- Pair.create("primary_fixed_dim", MDC.primaryFixedDim),
- Pair.create("on_primary_fixed", MDC.onPrimaryFixed),
- Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant),
- Pair.create("secondary_fixed", MDC.secondaryFixed),
- Pair.create("secondary_fixed_dim", MDC.secondaryFixedDim),
- Pair.create("on_secondary_fixed", MDC.onSecondaryFixed),
- Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant),
- Pair.create("tertiary_fixed", MDC.tertiaryFixed),
- Pair.create("tertiary_fixed_dim", MDC.tertiaryFixedDim),
- Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed),
- Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant),
- Pair.create("control_activated", MDC.controlActivated),
- Pair.create("control_normal", MDC.controlNormal),
- Pair.create("control_highlight", MDC.controlHighlight),
- Pair.create("text_primary_inverse", MDC.textPrimaryInverse),
+ Pair.create("primary_container", MDC.primaryContainer()),
+ Pair.create("on_primary_container", MDC.onPrimaryContainer()),
+ Pair.create("primary", MDC.primary()),
+ Pair.create("on_primary", MDC.onPrimary()),
+ Pair.create("secondary_container", MDC.secondaryContainer()),
+ Pair.create("on_secondary_container", MDC.onSecondaryContainer()),
+ Pair.create("secondary", MDC.secondary()),
+ Pair.create("on_secondary", MDC.onSecondary()),
+ Pair.create("tertiary_container", MDC.tertiaryContainer()),
+ Pair.create("on_tertiary_container", MDC.onTertiaryContainer()),
+ Pair.create("tertiary", MDC.tertiary()),
+ Pair.create("on_tertiary", MDC.onTertiary()),
+ Pair.create("background", MDC.background()),
+ Pair.create("on_background", MDC.onBackground()),
+ Pair.create("surface", MDC.surface()),
+ Pair.create("on_surface", MDC.onSurface()),
+ Pair.create("surface_container_low", MDC.surfaceContainerLow()),
+ Pair.create("surface_container_lowest", MDC.surfaceContainerLowest()),
+ Pair.create("surface_container", MDC.surfaceContainer()),
+ Pair.create("surface_container_high", MDC.surfaceContainerHigh()),
+ Pair.create("surface_container_highest", MDC.surfaceContainerHighest()),
+ Pair.create("surface_bright", MDC.surfaceBright()),
+ Pair.create("surface_dim", MDC.surfaceDim()),
+ Pair.create("surface_variant", MDC.surfaceVariant()),
+ Pair.create("on_surface_variant", MDC.onSurfaceVariant()),
+ Pair.create("outline", MDC.outline()),
+ Pair.create("error", MDC.error()),
+ Pair.create("on_error", MDC.onError()),
+ Pair.create("error_container", MDC.errorContainer()),
+ Pair.create("on_error_container", MDC.onErrorContainer()),
+ Pair.create("primary_fixed", MDC.primaryFixed()),
+ Pair.create("primary_fixed_dim", MDC.primaryFixedDim()),
+ Pair.create("on_primary_fixed", MDC.onPrimaryFixed()),
+ Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant()),
+ Pair.create("secondary_fixed", MDC.secondaryFixed()),
+ Pair.create("secondary_fixed_dim", MDC.secondaryFixedDim()),
+ Pair.create("on_secondary_fixed", MDC.onSecondaryFixed()),
+ Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant()),
+ Pair.create("tertiary_fixed", MDC.tertiaryFixed()),
+ Pair.create("tertiary_fixed_dim", MDC.tertiaryFixedDim()),
+ Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed()),
+ Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant()),
+ Pair.create("control_activated", MDC.controlActivated()),
+ Pair.create("control_normal", MDC.controlNormal()),
+ Pair.create("control_highlight", MDC.controlHighlight()),
+ Pair.create("text_primary_inverse", MDC.textPrimaryInverse()),
Pair.create(
"text_secondary_and_tertiary_inverse",
- MDC.textSecondaryAndTertiaryInverse
+ MDC.textSecondaryAndTertiaryInverse()
),
- Pair.create("text_primary_inverse_disable_only", MDC.textPrimaryInverseDisableOnly),
+ Pair.create(
+ "text_primary_inverse_disable_only",
+ MDC.textPrimaryInverseDisableOnly()
+ ),
Pair.create(
"text_secondary_and_tertiary_inverse_disabled",
- MDC.textSecondaryAndTertiaryInverseDisabled
+ MDC.textSecondaryAndTertiaryInverseDisabled()
),
- Pair.create("text_hint_inverse", MDC.textHintInverse),
- Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor),
- Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor),
- Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor),
- Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor),
- Pair.create("palette_key_color_neutral_variant", MDC.neutralVariantPaletteKeyColor)
+ Pair.create("text_hint_inverse", MDC.textHintInverse()),
+ Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor()),
+ Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor()),
+ Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor()),
+ Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor()),
+ Pair.create(
+ "palette_key_color_neutral_variant",
+ MDC.neutralVariantPaletteKeyColor()
+ )
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index faae8df..43d15bc 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.systemui.theme;
import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
@@ -659,9 +660,9 @@
&& res.getColor(android.R.color.system_neutral2_500, theme)
== mColorScheme.getNeutral2().getS500()
&& res.getColor(android.R.color.system_primary_container_dark, theme)
- == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeDark)
+ == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
&& res.getColor(android.R.color.system_primary_container_light, theme)
- == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeLight))) {
+ == MaterialDynamicColors.primaryContainer().getArgb(mDynamicSchemeLight))) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 41c6b5d..64234c2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -16,22 +16,20 @@
package com.android.systemui.util
-import android.os.Handler
+import android.os.Trace
import android.os.TraceNameSupplier
-import androidx.tracing.Trace
/**
- * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
- * after the passed block. If tracing is disabled, it will run the block directly to avoid using an
- * unnecessary try-finally block.
+ * Run a block within a [Trace] section.
+ * Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
*/
inline fun <T> traceSection(tag: String, block: () -> T): T =
- if (Trace.isEnabled()) {
- Trace.beginSection(tag)
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
try {
block()
} finally {
- Trace.endSection()
+ Trace.traceEnd(Trace.TRACE_TAG_APP)
}
} else {
block()
@@ -44,10 +42,8 @@
}
/**
- * Helper function for creating a [Runnable] that implements [TraceNameSupplier]. This is
- * useful when posting to a [Handler] so that the [Runnable] has a meaningful name in the
- * trace. Otherwise, the class name of the [Runnable] is used, which is often something like
- * `pkg.MyClass$$ExternalSyntheticLambda0`.
+ * Helper function for creating a Runnable object that implements TraceNameSupplier.
+ * This is useful for posting Runnables to Handlers with meaningful names.
*/
inline fun namedRunnable(tag: String, crossinline block: () -> Unit): Runnable {
return object : Runnable, TraceNameSupplier {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b3e7cb0..e60f9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -33,6 +33,7 @@
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.Display;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
@@ -347,12 +348,16 @@
void initDesktopMode(DesktopMode desktopMode) {
desktopMode.addVisibleTasksListener(
new DesktopModeTaskRepository.VisibleTasksListener() {
- @Override
- public void onVisibilityChanged(boolean hasFreeformTasks) {
- mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
- .commitUpdate(mDisplayTracker.getDefaultDisplayId());
- }
- }, mSysUiMainExecutor);
+ @Override
+ public void onVisibilityChanged(int displayId, boolean hasFreeformTasks) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
+ hasFreeformTasks)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ // TODO(b/278084491): update sysui state for changes on other displays
+ }
+ }, mSysUiMainExecutor);
}
@Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e2b568c..080be6d 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -196,6 +196,17 @@
android:exported="false"
android:permission="com.android.systemui.permission.SELF"
android:excludeFromRecents="true" />
+
+ <activity
+ android:name="com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity"
+ android:exported="false"
+ android:permission="com.android.systemui.permission.SELF"
+ android:excludeFromRecents="true" >
+ <intent-filter>
+ <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index f4df26d..8a05a37 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -682,8 +682,7 @@
}
@Test
- public void testReinflateViewFlipper_asyncBouncerFlagOn() {
- when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true);
+ public void testReinflateViewFlipper() {
KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback =
controller -> {
};
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 71246c9..ddd9a08 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -151,7 +151,9 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.internal.util.reflection.FieldSetter;
@@ -383,7 +385,6 @@
}
private void setupFingerprintAuth(boolean isClass3) throws RemoteException {
- when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
mFingerprintSensorProperties = List.of(
@@ -2693,42 +2694,33 @@
}
@Test
public void testFingerprintSensorProperties() throws RemoteException {
- // GIVEN no fingerprint sensor properties
- when(mAuthController.isFingerprintEnrolled(anyInt())).thenReturn(true);
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
- // THEN fingerprint is not possible
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())).isFalse();
- // WHEN there are fingerprint sensor properties
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
- // THEN unlock with fp is possible & fingerprint starts listening
+ verifyFingerprintAuthenticateCall();
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())).isTrue();
- verifyFingerprintAuthenticateCall();
}
@Test
public void testFaceSensorProperties() throws RemoteException {
- // GIVEN no face sensor properties
- when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
- // THEN face is not possible
- assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
+ assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
KeyguardUpdateMonitor.getCurrentUser())).isFalse();
- // WHEN there are face sensor properties
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
+ biometricsEnabledForCurrentUser();
- // THEN face is possible but face does NOT start listening immediately
- assertThat(mKeyguardUpdateMonitor.isUnlockWithFacePossible(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
verifyFaceAuthenticateNeverCalled();
verifyFaceDetectNeverCalled();
+ assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
+ KeyguardUpdateMonitor.getCurrentUser())).isTrue();
}
@Test
@@ -2747,6 +2739,36 @@
verifyFingerprintAuthenticateCall();
}
+ @Test
+ public void onTrustChangedCallbacksCalledBeforeOnTrustGrantedForCurrentUserCallback() {
+ // GIVEN device is interactive
+ deviceIsInteractive();
+
+ // GIVEN callback is registered
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN onTrustChanged enabled=true
+ mKeyguardUpdateMonitor.onTrustChanged(
+ true /* enabled */,
+ true /* newlyUnlocked */,
+ getCurrentUser() /* userId */,
+ TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
+ null /* trustGrantedMessages */);
+
+ // THEN onTrustChanged is called FIRST
+ final InOrder inOrder = Mockito.inOrder(callback);
+ inOrder.verify(callback).onTrustChanged(eq(getCurrentUser()));
+
+ // AND THEN onTrustGrantedForCurrentUser callback called
+ inOrder.verify(callback).onTrustGrantedForCurrentUser(
+ eq(true) /* dismissKeyguard */,
+ eq(true) /* newlyUnlocked */,
+ eq(new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD)),
+ eq(null) /* message */
+ );
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
@@ -2801,6 +2823,9 @@
}
private void mockCanBypassLockscreen(boolean canBypass) {
+ // force update the isFaceEnrolled cache:
+ mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(getCurrentUser());
+
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
index dea2082..6fe8892 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
@@ -18,6 +18,7 @@
import android.animation.Animator
import android.testing.AndroidTestingRunner
import android.transition.TransitionValues
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardStatusViewController.SplitShadeTransitionAdapter
import com.android.systemui.SysuiTestCase
@@ -44,14 +45,16 @@
@Test
fun createAnimator_nullStartValues_returnsNull() {
- val animator = adapter.createAnimator(startValues = null, endValues = TransitionValues())
+ val endValues = createEndValues()
+
+ val animator = adapter.createAnimator(startValues = null, endValues = endValues)
assertThat(animator).isNull()
}
@Test
fun createAnimator_nullEndValues_returnsNull() {
- val animator = adapter.createAnimator(startValues = TransitionValues(), endValues = null)
+ val animator = adapter.createAnimator(startValues = createStartValues(), endValues = null)
assertThat(animator).isNull()
}
@@ -59,10 +62,22 @@
@Test
fun createAnimator_nonNullStartAndEndValues_returnsAnimator() {
val animator =
- adapter.createAnimator(startValues = TransitionValues(), endValues = TransitionValues())
+ adapter.createAnimator(startValues = createStartValues(), endValues = createEndValues())
assertThat(animator).isNotNull()
}
+
+ private fun createStartValues() =
+ TransitionValues().also { values ->
+ values.view = View(context)
+ adapter.captureStartValues(values)
+ }
+
+ private fun createEndValues() =
+ TransitionValues().also { values ->
+ values.view = View(context)
+ adapter.captureEndValues(values)
+ }
}
private fun SplitShadeTransitionAdapter.createAnimator(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..a2001e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
new file mode 100644
index 0000000..025c88c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.input.InputManager;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import dagger.Lazy;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@TestableLooper.RunWithLooper
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SystemActionsTest extends SysuiTestCase {
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private NotificationShadeWindowController mNotificationShadeController;
+ @Mock
+ private ShadeController mShadeController;
+ @Mock
+ private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ @Mock
+ private Optional<Recents> mRecentsOptional;
+ @Mock
+ private TelecomManager mTelecomManager;
+ @Mock
+ private InputManager mInputManager;
+ private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
+ private SystemActions mSystemActions;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+ mContext.addMockSystemService(InputManager.class, mInputManager);
+ mSystemActions = new SystemActions(mContext, mUserTracker, mNotificationShadeController,
+ mShadeController, mCentralSurfacesOptionalLazy, mRecentsOptional, mDisplayTracker);
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateIdle_injectsKeyEvents() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_IDLE);
+ // Use a custom doAnswer captor that copies the KeyEvent before storing it, because the
+ // method under test modifies the event object after injecting it which prevents
+ // reliably asserting on the event properties.
+ final List<KeyEvent> keyEvents = new ArrayList<>();
+ doAnswer(invocation -> {
+ keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+ return null;
+ }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+ mSystemActions.handleHeadsetHook();
+
+ assertThat(keyEvents.size()).isEqualTo(2);
+ assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_HEADSETHOOK);
+ assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+ assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_HEADSETHOOK);
+ assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateRinging_answersCall() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_RINGING);
+
+ mSystemActions.handleHeadsetHook();
+
+ verify(mTelecomManager).acceptRingingCall();
+ }
+
+ @Test
+ public void handleHeadsetHook_callStateOffhook_endsCall() {
+ when(mTelecomManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ mSystemActions.handleHeadsetHook();
+
+ verify(mTelecomManager).endCall();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 6d4c467..6e37ee7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -40,15 +40,21 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import org.junit.After
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,6 +68,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+@Ignore("b/279650412")
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -85,13 +92,26 @@
@Mock
lateinit var interactionJankMonitor: InteractionJankMonitor
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val biometricPromptRepository = FakePromptRepository()
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
private val bpCredentialInteractor = BiometricPromptCredentialInteractor(
Dispatchers.Main.immediate,
biometricPromptRepository,
credentialInteractor
)
+ private val displayStateInteractor = DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+
+ private val authBiometricFingerprintViewModel = AuthBiometricFingerprintViewModel(
+ displayStateInteractor
+ )
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private var authContainer: TestAuthContainerView? = null
@@ -467,9 +487,10 @@
lockPatternUtils,
interactionJankMonitor,
{ bpCredentialInteractor },
+ { authBiometricFingerprintViewModel },
{ credentialViewModel },
Handler(TestableLooper.get(this).looper),
- FakeExecutor(FakeSystemClock())
+ fakeExecutor
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 4f24b3a..a326cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -93,6 +93,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
+import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
@@ -172,6 +173,8 @@
@Mock
private BiometricPromptCredentialInteractor mBiometricPromptCredentialInteractor;
@Mock
+ private AuthBiometricFingerprintViewModel mAuthBiometricFingerprintViewModel;
+ @Mock
private CredentialViewModel mCredentialViewModel;
@Mock
private UdfpsUtils mUdfpsUtils;
@@ -995,8 +998,9 @@
() -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger,
mLogContextInteractor, () -> mBiometricPromptCredentialInteractor,
- () -> mCredentialViewModel, mInteractionJankMonitor, mHandler,
- mBackgroundExecutor, mVibratorHelper, mUdfpsUtils);
+ () -> mAuthBiometricFingerprintViewModel, () -> mCredentialViewModel,
+ mInteractionJankMonitor, mHandler, mBackgroundExecutor, mVibratorHelper,
+ mUdfpsUtils);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index e6334cf..40d9009 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -55,6 +55,9 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -66,7 +69,9 @@
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -90,6 +95,8 @@
private const val DISPLAY_ID = 2
private const val SENSOR_ID = 1
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
@SmallTest
@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@@ -112,7 +119,12 @@
private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository
private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private lateinit var displayStateInteractor: DisplayStateInteractor
+
private val executor = FakeExecutor(FakeSystemClock())
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+ private val testScope = TestScope(StandardTestDispatcher())
+
private lateinit var overlayController: ISidefpsController
private lateinit var sideFpsController: SideFpsController
@@ -142,6 +154,13 @@
FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
)
+ displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ context,
+ executor,
+ rearDisplayStateRepository
+ )
context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, windowManager)
@@ -168,6 +187,7 @@
isReverseDefaultRotation: Boolean = false,
initInfo: DisplayInfo.() -> Unit = {},
windowInsets: WindowInsets = insetsForSmallNavbar(),
+ inRearDisplayMode: Boolean = false,
block: () -> Unit
) {
this.deviceConfig = deviceConfig
@@ -228,6 +248,13 @@
isReverseDefaultRotation
)
+ val rearDisplayDeviceStates =
+ if (inRearDisplayMode) intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE) else intArrayOf()
+ sideFpsControllerContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.array.config_rearDisplayDeviceStates,
+ rearDisplayDeviceStates
+ )
+
sideFpsController =
SideFpsController(
sideFpsControllerContext,
@@ -237,12 +264,14 @@
activityTaskManager,
overviewProxyService,
displayManager,
+ displayStateInteractor,
executor,
handler,
alternateBouncerInteractor,
TestCoroutineScope(),
- dumpManager,
+ dumpManager
)
+ rearDisplayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)
overlayController =
ArgumentCaptor.forClass(ISidefpsController::class.java)
@@ -584,10 +613,62 @@
verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
}
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_0 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_90() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_90 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_180() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_180 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
+ @Test
+ fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_270() =
+ testWithDisplay(
+ deviceConfig = DeviceConfig.Y_ALIGNED,
+ isReverseDefaultRotation = false,
+ { rotation = Surface.ROTATION_270 },
+ inRearDisplayMode = true,
+ ) {
+ verifySfpsIndicator_notAdded_InRearDisplayMode()
+ }
+
private fun verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible: Boolean) {
sideFpsController.overlayOffsets = sensorLocation
}
+ private fun verifySfpsIndicator_notAdded_InRearDisplayMode() {
+ sideFpsController.overlayOffsets = sensorLocation
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+
fun alternateBouncerVisibility_showAndHideSideFpsUI() = testWithDisplay {
// WHEN alternate bouncer is visible
keyguardBouncerRepository.setAlternateVisible(true)
@@ -624,7 +705,7 @@
* in other rotations have been omitted.
*/
@Test
- fun verifiesIndicatorPlacementForXAlignedSensor_0() {
+ fun verifiesIndicatorPlacementForXAlignedSensor_0() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
@@ -641,7 +722,6 @@
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
- }
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270
@@ -650,7 +730,7 @@
* correctly, tests for indicator placement in other rotations have been omitted.
*/
@Test
- fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() {
+ fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() =
testWithDisplay(
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = true,
@@ -667,7 +747,6 @@
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0)
}
- }
/**
* {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
new file mode 100644
index 0000000..f3a100b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.biometrics.data.repository
+
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.biometrics.SensorProperties
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorProperties
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class FingerprintRepositoryImplTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var fingerprintManager: FingerprintManager
+ private lateinit var repository: FingerprintPropertyRepositoryImpl
+
+ @Captor
+ private lateinit var fingerprintAuthenticatorsCaptor:
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback.Stub>
+
+ @Before
+ fun setup() {
+ val dispatcher = StandardTestDispatcher()
+ testScope = TestScope(dispatcher)
+ repository =
+ FingerprintPropertyRepositoryImpl(testScope.backgroundScope, fingerprintManager)
+ testScope.runCurrent()
+
+ verify(fingerprintManager)
+ .addAuthenticatorsRegisteredCallback(fingerprintAuthenticatorsCaptor.capture())
+ }
+
+ @Test
+ fun initializeProperties() =
+ testScope.runTest {
+ val isInitialized = collectLastValue(repository.isInitialized)
+
+ assertDefaultProperties()
+ assertThat(isInitialized()).isFalse()
+
+ val fingerprintProps =
+ listOf(
+ FingerprintSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ listOf<ComponentInfoInternal>(
+ ComponentInfoInternal(
+ "sensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */,
+ "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */,
+ "" /* softwareVersion */
+ )
+ ),
+ FingerprintSensorProperties.TYPE_REAR,
+ false /* halControlsIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ listOf<SensorLocationInternal>(
+ SensorLocationInternal(
+ "" /* displayId */,
+ 540 /* sensorLocationX */,
+ 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */
+ )
+ )
+ )
+ )
+
+ fingerprintAuthenticatorsCaptor.value.onAllAuthenticatorsRegistered(fingerprintProps)
+
+ assertThat(repository.sensorId.value).isEqualTo(1)
+ assertThat(repository.strength.value).isEqualTo(SensorStrength.STRONG)
+ assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.REAR)
+ with(repository.sensorLocation.value) {
+ assertThat(displayId).isEqualTo("")
+ assertThat(sensorLocationX).isEqualTo(540)
+ assertThat(sensorLocationY).isEqualTo(1636)
+ assertThat(sensorRadius).isEqualTo(130)
+ }
+ assertThat(isInitialized()).isTrue()
+ }
+
+ private fun assertDefaultProperties() {
+ assertThat(repository.sensorId.value).isEqualTo(-1)
+ assertThat(repository.strength.value).isEqualTo(SensorStrength.CONVENIENCE)
+ assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.UNKNOWN)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.kt
new file mode 100644
index 0000000..dfe8d36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/RearDisplayStateRepositoryTest.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.keyguard.data.repository
+
+import android.hardware.devicestate.DeviceStateManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepository
+import com.android.systemui.biometrics.data.repository.RearDisplayStateRepositoryImpl
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val NORMAL_DISPLAY_MODE_DEVICE_STATE = 2
+private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class RearDisplayStateRepositoryTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var deviceStateManager: DeviceStateManager
+ private lateinit var underTest: RearDisplayStateRepository
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ @Captor
+ private lateinit var callbackCaptor: ArgumentCaptor<DeviceStateManager.DeviceStateCallback>
+
+ @Before
+ fun setUp() {
+ val rearDisplayDeviceStates = intArrayOf(REAR_DISPLAY_MODE_DEVICE_STATE)
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.array.config_rearDisplayDeviceStates,
+ rearDisplayDeviceStates
+ )
+
+ underTest =
+ RearDisplayStateRepositoryImpl(
+ testScope.backgroundScope,
+ mContext,
+ deviceStateManager,
+ fakeExecutor
+ )
+ }
+
+ @Test
+ fun updatesIsInRearDisplayMode_whenRearDisplayStateChanges() =
+ testScope.runTest {
+ val isInRearDisplayMode = collectLastValue(underTest.isInRearDisplayMode)
+ runCurrent()
+
+ val callback = deviceStateManager.captureCallback()
+
+ callback.onStateChanged(NORMAL_DISPLAY_MODE_DEVICE_STATE)
+ assertThat(isInRearDisplayMode()).isFalse()
+
+ callback.onStateChanged(REAR_DISPLAY_MODE_DEVICE_STATE)
+ assertThat(isInRearDisplayMode()).isTrue()
+ }
+}
+
+private fun DeviceStateManager.captureCallback() =
+ withArgCaptor<DeviceStateManager.DeviceStateCallback> {
+ verify(this@captureCallback).registerCallback(any(), capture())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
new file mode 100644
index 0000000..2217c5c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -0,0 +1,84 @@
+package com.android.systemui.biometrics.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DisplayStateInteractorImplTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+
+ @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+ private lateinit var interactor: DisplayStateInteractorImpl
+
+ @Before
+ fun setup() {
+ interactor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+ interactor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+ }
+
+ @Test
+ fun isInRearDisplayModeChanges() =
+ testScope.runTest {
+ val isInRearDisplayMode = collectLastValue(interactor.isInRearDisplayMode)
+
+ rearDisplayStateRepository.setIsInRearDisplayMode(false)
+ assertThat(isInRearDisplayMode()).isFalse()
+
+ rearDisplayStateRepository.setIsInRearDisplayMode(true)
+ assertThat(isInRearDisplayMode()).isTrue()
+ }
+
+ @Test
+ fun isFoldedChanges() =
+ testScope.runTest {
+ val isFolded = collectLastValue(interactor.isFolded)
+ runCurrent()
+ val callback = screenSizeFoldProvider.captureCallback()
+
+ callback.onFoldUpdated(isFolded = true)
+ assertThat(isFolded()).isTrue()
+
+ callback.onFoldUpdated(isFolded = false)
+ assertThat(isFolded()).isFalse()
+ }
+}
+
+private fun FoldProvider.captureCallback() =
+ withArgCaptor<FoldProvider.FoldCallback> {
+ verify(this@captureCallback).registerCallback(capture(), any())
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
new file mode 100644
index 0000000..0c210e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
@@ -0,0 +1,69 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.content.res.Configuration
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class AuthBiometricFingerprintViewModelTest : SysuiTestCase() {
+
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ private lateinit var interactor: DisplayStateInteractor
+ private lateinit var viewModel: AuthBiometricFingerprintViewModel
+
+ @Before
+ fun setup() {
+ interactor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+ viewModel = AuthBiometricFingerprintViewModel(interactor)
+ }
+
+ @Test
+ fun iconUpdates_onConfigurationChanged() {
+ testScope.runTest {
+ runCurrent()
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ val currentIcon = collectLastValue(viewModel.iconAsset)
+
+ testConfig.smallestScreenWidthDp = folded
+ viewModel.onConfigurationChanged(testConfig)
+ val foldedIcon = currentIcon()
+
+ testConfig.smallestScreenWidthDp = unfolded
+ viewModel.onConfigurationChanged(testConfig)
+ val unfoldedIcon = currentIcon()
+
+ assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
+ }
+ }
+}
+
+internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
new file mode 100644
index 0000000..21516d49
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.clipboardoverlay
+
+import android.content.ContentResolver
+import android.content.Context
+import android.net.Uri
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.whenever
+import java.io.IOException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertNull
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class ClipboardImageLoaderTest : SysuiTestCase() {
+ @Mock private lateinit var mockContext: Context
+
+ @Mock private lateinit var mockContentResolver: ContentResolver
+
+ private lateinit var clipboardImageLoader: ClipboardImageLoader
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ @Throws(IOException::class)
+ fun test_imageLoadSuccess() = runTest {
+ val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ clipboardImageLoader =
+ ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ val testUri = Uri.parse("testUri")
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(context.resources)
+
+ clipboardImageLoader.load(testUri)
+
+ verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any())
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ @Throws(IOException::class)
+ fun test_imageLoadFailure() = runTest {
+ val testDispatcher = StandardTestDispatcher(this.testScheduler)
+ clipboardImageLoader =
+ ClipboardImageLoader(mockContext, testDispatcher, CoroutineScope(testDispatcher))
+ val testUri = Uri.parse("testUri")
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(context.resources)
+
+ val res = clipboardImageLoader.load(testUri)
+
+ verify(mockContentResolver).loadThumbnail(eq(testUri), any(), any())
+ assertNull(res)
+ }
+}
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 fe5fa1f..39fb7b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -25,6 +25,7 @@
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
+import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -90,6 +91,8 @@
@Mock
private ClipboardOverlayUtils mClipboardUtils;
@Mock
+ private ClipboardImageLoader mClipboardImageLoader;
+ @Mock
private UiEventLogger mUiEventLogger;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -120,6 +123,7 @@
mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
new ClipData.Item("Test Item"));
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests
mOverlayController = new ClipboardOverlayController(
mContext,
@@ -131,6 +135,7 @@
mFeatureFlags,
mClipboardUtils,
mExecutor,
+ mClipboardImageLoader,
mUiEventLogger);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -142,6 +147,69 @@
}
@Test
+ public void test_setClipData_invalidImageData_legacy() {
+ ClipData clipData = new ClipData("", new String[]{"image/png"},
+ new ClipData.Item(Uri.parse("")));
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
+
+ mOverlayController.setClipData(clipData, "");
+
+ verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
+ verify(mClipboardOverlayView, times(1)).showShareChip();
+ verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+ }
+
+ @Test
+ public void test_setClipData_nonImageUri_legacy() {
+ ClipData clipData = new ClipData("", new String[]{"resource/png"},
+ new ClipData.Item(Uri.parse("")));
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
+
+ mOverlayController.setClipData(clipData, "");
+
+ verify(mClipboardOverlayView, times(1)).showDefaultTextPreview();
+ verify(mClipboardOverlayView, times(1)).showShareChip();
+ verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+ }
+
+ @Test
+ public void test_setClipData_textData_legacy() {
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
+ mOverlayController.setClipData(mSampleClipData, "abc");
+
+ verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false);
+ verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "abc");
+ verify(mClipboardOverlayView, times(1)).showShareChip();
+ verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+ }
+
+ @Test
+ public void test_setClipData_sensitiveTextData_legacy() {
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
+ ClipDescription description = mSampleClipData.getDescription();
+ PersistableBundle b = new PersistableBundle();
+ b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
+ description.setExtras(b);
+ ClipData data = new ClipData(description, mSampleClipData.getItemAt(0));
+ mOverlayController.setClipData(data, "");
+
+ verify(mClipboardOverlayView, times(1)).showTextPreview("••••••", true);
+ verify(mClipboardOverlayView, times(1)).showShareChip();
+ verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+ }
+
+ @Test
+ public void test_setClipData_repeatedCalls_legacy() {
+ when(mAnimator.isRunning()).thenReturn(true);
+ mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
+
+ mOverlayController.setClipData(mSampleClipData, "");
+ mOverlayController.setClipData(mSampleClipData, "");
+
+ verify(mClipboardOverlayView, times(1)).getEnterAnimation();
+ }
+
+ @Test
public void test_setClipData_invalidImageData() {
ClipData clipData = new ClipData("", new String[]{"image/png"},
new ClipData.Item(Uri.parse("")));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index ebbe096..26cbd77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -41,11 +41,11 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -378,7 +378,13 @@
executor: DelayableExecutor,
lazyController: Lazy<ControlsController>,
userTracker: UserTracker
-) : ControlsBindingControllerImpl(context, executor, lazyController, userTracker) {
+) : ControlsBindingControllerImpl(
+ context,
+ executor,
+ lazyController,
+ mock(PackageUpdateMonitor.Factory::class.java),
+ userTracker
+) {
companion object {
val providers = mutableListOf<ControlsProviderLifecycleManager>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index da548f7..b5d3476 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -30,6 +30,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Assert.assertEquals
@@ -39,17 +43,17 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -62,16 +66,21 @@
private lateinit var subscriberService: IControlsSubscriber.Stub
@Mock
private lateinit var service: IControlsProvider.Stub
-
+ @Mock
+ private lateinit var packageUpdateMonitor: PackageUpdateMonitor
@Captor
private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper>
+ private lateinit var packageUpdateMonitorFactory: FakePackageUpdateMonitorFactory
+
private val componentName = ComponentName("test.pkg", "test.cls")
private lateinit var manager: ControlsProviderLifecycleManager
private lateinit var executor: FakeExecutor
+ private lateinit var fakeSystemClock: FakeSystemClock
companion object {
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ private val USER = UserHandle.of(0)
}
@Before
@@ -79,16 +88,20 @@
MockitoAnnotations.initMocks(this)
context.addMockService(componentName, service)
- executor = FakeExecutor(FakeSystemClock())
+ fakeSystemClock = FakeSystemClock()
+ executor = FakeExecutor(fakeSystemClock)
`when`(service.asBinder()).thenCallRealMethod()
- `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service)
+ `when`(service.queryLocalInterface(anyString())).thenReturn(service)
+
+ packageUpdateMonitorFactory = FakePackageUpdateMonitorFactory(packageUpdateMonitor)
manager = ControlsProviderLifecycleManager(
context,
executor,
actionCallbackService,
- UserHandle.of(0),
- componentName
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
)
}
@@ -122,7 +135,7 @@
@Test
fun testNullBinding() {
- val mockContext = mock(Context::class.java)
+ val mockContext = mock<Context>()
lateinit var serviceConnection: ServiceConnection
`when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer {
val component = (it.arguments[0] as Intent).component
@@ -139,8 +152,9 @@
mockContext,
executor,
actionCallbackService,
- UserHandle.of(0),
- componentName
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
)
nullManager.bindService()
@@ -229,14 +243,15 @@
@Test
fun testFalseBindCallsUnbind() {
- val falseContext = mock(Context::class.java)
+ val falseContext = mock<Context>()
`when`(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false)
val manager = ControlsProviderLifecycleManager(
falseContext,
executor,
actionCallbackService,
- UserHandle.of(0),
- componentName
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
)
manager.bindService()
executor.runAllReady()
@@ -247,4 +262,150 @@
verify(falseContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any())
verify(falseContext).unbindService(captor.value)
}
+
+ @Test
+ fun testPackageUpdateMonitor_createdWithCorrectValues() {
+ assertEquals(USER, packageUpdateMonitorFactory.lastUser)
+ assertEquals(componentName.packageName, packageUpdateMonitorFactory.lastPackage)
+ }
+
+ @Test
+ fun testBound_packageMonitorStartsMonitoring() {
+ manager.bindService()
+ executor.runAllReady()
+
+ // Service will connect and monitoring should start
+ verify(packageUpdateMonitor).startMonitoring()
+ }
+
+ @Test
+ fun testOnPackageUpdateWhileBound_unbound_thenBindAgain() {
+ val mockContext = mock<Context> {
+ `when`(bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true)
+ }
+
+ val manager = ControlsProviderLifecycleManager(
+ mockContext,
+ executor,
+ actionCallbackService,
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
+ )
+
+ manager.bindService()
+ executor.runAllReady()
+ clearInvocations(mockContext)
+
+ packageUpdateMonitorFactory.lastCallback?.run()
+ executor.runAllReady()
+
+ val inOrder = inOrder(mockContext)
+ inOrder.verify(mockContext).unbindService(any())
+ inOrder.verify(mockContext).bindServiceAsUser(any(), any(), anyInt(), any())
+ }
+
+ @Test
+ fun testOnPackageUpdateWhenNotBound_nothingHappens() {
+ val mockContext = mock<Context> {
+ `when`(bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true)
+ }
+
+ ControlsProviderLifecycleManager(
+ mockContext,
+ executor,
+ actionCallbackService,
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
+ )
+
+ packageUpdateMonitorFactory.lastCallback?.run()
+ verifyNoMoreInteractions(mockContext)
+ }
+
+ @Test
+ fun testUnbindService_stopsTracking() {
+ manager.bindService()
+ manager.unbindService()
+ executor.runAllReady()
+
+ verify(packageUpdateMonitor).stopMonitoring()
+ }
+
+ @Test
+ fun testRebindForPanelWithSameFlags() {
+ val mockContext = mock<Context> {
+ `when`(bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true)
+ }
+
+ val manager = ControlsProviderLifecycleManager(
+ mockContext,
+ executor,
+ actionCallbackService,
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
+ )
+
+ manager.bindServiceForPanel()
+ executor.runAllReady()
+
+ val flagsCaptor = argumentCaptor<Int>()
+ verify(mockContext).bindServiceAsUser(any(), any(), capture(flagsCaptor), any())
+ clearInvocations(mockContext)
+
+ packageUpdateMonitorFactory.lastCallback?.run()
+ executor.runAllReady()
+
+ verify(mockContext).bindServiceAsUser(any(), any(), eq(flagsCaptor.value), any())
+ }
+
+ @Test
+ fun testBindAfterSecurityExceptionWorks() {
+ val mockContext = mock<Context> {
+ `when`(bindServiceAsUser(any(), any(), anyInt(), any()))
+ .thenThrow(SecurityException("exception"))
+ }
+
+ val manager = ControlsProviderLifecycleManager(
+ mockContext,
+ executor,
+ actionCallbackService,
+ USER,
+ componentName,
+ packageUpdateMonitorFactory,
+ )
+
+ manager.bindServiceForPanel()
+ executor.runAllReady()
+
+ `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true)
+
+ manager.bindServiceForPanel()
+ executor.runAllReady()
+
+ verify(mockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any())
+ }
+
+ private class FakePackageUpdateMonitorFactory(
+ private val monitor: PackageUpdateMonitor
+ ) : PackageUpdateMonitor.Factory {
+
+ var lastUser: UserHandle? = null
+ var lastPackage: String? = null
+ var lastCallback: Runnable? = null
+
+ override fun create(
+ user: UserHandle,
+ packageName: String,
+ callback: Runnable
+ ): PackageUpdateMonitor {
+ lastUser = user
+ lastPackage = packageName
+ lastCallback = callback
+ return monitor
+ }
+ }
}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
new file mode 100644
index 0000000..6954710
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.controls.controller
+
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class PackageUpdateMonitorTest : SysuiTestCase() {
+
+ @Mock private lateinit var context: Context
+ @Mock private lateinit var bgHandler: Handler
+
+ private lateinit var underTest: PackageUpdateMonitor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun startMonitoring_registerOnlyOnce() {
+ underTest = PackageUpdateMonitor(USER, PACKAGE, {}, bgHandler, context)
+
+ underTest.startMonitoring()
+ // There are two receivers registered
+ verify(context, times(2))
+ .registerReceiverAsUser(any(), eq(USER), any(), eq(null), eq(bgHandler))
+
+ underTest.startMonitoring()
+ verifyNoMoreInteractions(context)
+ }
+
+ @Test
+ fun stopMonitoring_unregistersOnlyOnce() {
+ underTest = PackageUpdateMonitor(USER, PACKAGE, {}, bgHandler, context)
+
+ underTest.startMonitoring()
+ clearInvocations(context)
+
+ underTest.stopMonitoring()
+ verify(context).unregisterReceiver(any())
+
+ underTest.stopMonitoring()
+ verifyNoMoreInteractions(context)
+ }
+
+ @Test
+ fun onPackageUpdated_correctPackageAndUser_callbackRuns() {
+ val callback = mock<Runnable>()
+
+ underTest = PackageUpdateMonitor(USER, PACKAGE, callback, bgHandler, context)
+
+ underTest.onPackageUpdateFinished(PACKAGE, UserHandle.getUid(USER.identifier, 10000))
+ verify(callback).run()
+ }
+
+ @Test
+ fun onPackageUpdated_correctPackage_wrongUser_callbackDoesntRun() {
+ val callback = mock<Runnable>()
+
+ underTest = PackageUpdateMonitor(USER, PACKAGE, callback, bgHandler, context)
+
+ underTest.onPackageUpdateFinished(PACKAGE, UserHandle.getUid(USER.identifier + 1, 10000))
+ verify(callback, never()).run()
+ }
+
+ @Test
+ fun onPackageUpdated_wrongPackage_correctUser_callbackDoesntRun() {
+ val callback = mock<Runnable>()
+
+ underTest = PackageUpdateMonitor(USER, PACKAGE, callback, bgHandler, context)
+
+ underTest.onPackageUpdateFinished("bad", UserHandle.getUid(USER.identifier + 1, 10000))
+ verify(callback, never()).run()
+ }
+
+ companion object {
+ private val USER = UserHandle.of(0)
+ private val PACKAGE = "pkg"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
index c3506e8..5414259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
+import android.app.ActivityOptions
import android.app.PendingIntent
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -24,7 +25,10 @@
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.wm.shell.taskview.TaskView
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,6 +73,25 @@
verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
}
+ @Test
+ fun testActivityOptionsAllowBal() {
+ // GIVEN the dialog is created with a PendingIntent
+ val dialog = createDialog(pendingIntent)
+
+ // WHEN the TaskView is initialized
+ dialog.stateCallback.onInitialized()
+
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ // THEN the ActivityOptions have the correct flags
+ verify(taskView).startActivity(any(), any(), capture(optionsCaptor), any())
+
+ assertThat(optionsCaptor.value.pendingIntentBackgroundActivityStartMode)
+ .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ assertThat(optionsCaptor.value.isPendingIntentBackgroundActivityLaunchAllowedByPermission)
+ .isTrue()
+ }
+
private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
return DetailDialog(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 8795ac0..c9ee1e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -115,6 +116,7 @@
@Mock private MetricsLogger mMetricsLogger;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private IStatusBarService mStatusBarService;
+ @Mock private LightBarController mLightBarController;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@@ -166,6 +168,7 @@
mMetricsLogger,
mColorExtractor,
mStatusBarService,
+ mLightBarController,
mNotificationShadeWindowController,
mWindowManager,
mBackgroundExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
index 6f9dedf..fae6375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
@@ -18,9 +18,12 @@
package com.android.systemui.keyboard.data.repository
import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyboardBacklightListener
+import android.hardware.input.KeyboardBacklightState
import android.view.InputDevice
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -29,9 +32,11 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -50,6 +55,7 @@
@Captor
private lateinit var deviceListenerCaptor: ArgumentCaptor<InputManager.InputDeviceListener>
+ @Captor private lateinit var backlightListenerCaptor: ArgumentCaptor<KeyboardBacklightListener>
@Mock private lateinit var inputManager: InputManager
private lateinit var underTest: KeyboardRepository
@@ -174,7 +180,71 @@
@Test
fun passesKeyboardBacklightValues_fromBacklightListener() {
- // TODO(b/268645734): implement when implementing backlight listener
+ testScope.runTest {
+ // we want to capture backlight listener but this can only be done after Flow is
+ // subscribed to and listener is actually registered in inputManager
+ val backlight by collectLastValueImmediately(underTest.backlight)
+
+ verify(inputManager)
+ .registerKeyboardBacklightListener(any(), backlightListenerCaptor.capture())
+
+ backlightListenerCaptor.value.onBacklightChanged(current = 1, max = 5)
+
+ assertThat(backlight?.level).isEqualTo(1)
+ assertThat(backlight?.maxLevel).isEqualTo(5)
+ }
+ }
+
+ private fun <T> TestScope.collectLastValueImmediately(flow: Flow<T>): FlowValue<T?> {
+ val lastValue = collectLastValue(flow)
+ // runCurrent() makes us wait for collect that happens in collectLastValue and ensures
+ // Flow is initialized
+ runCurrent()
+ return lastValue
+ }
+
+ @Test
+ fun keyboardBacklightValuesNotPassed_fromBacklightListener_whenNotTriggeredByKeyPress() {
+ testScope.runTest() {
+ val backlight by collectLastValueImmediately(underTest.backlight)
+ verify(inputManager)
+ .registerKeyboardBacklightListener(any(), backlightListenerCaptor.capture())
+
+ backlightListenerCaptor.value.onBacklightChanged(
+ current = 1,
+ max = 5,
+ triggeredByKeyPress = false
+ )
+ assertThat(backlight).isNull()
+ }
+ }
+
+ @Test
+ fun passesKeyboardBacklightValues_fromBacklightListener_whenTriggeredByKeyPress() {
+ testScope.runTest() {
+ val backlight by collectLastValueImmediately(underTest.backlight)
+ verify(inputManager)
+ .registerKeyboardBacklightListener(any(), backlightListenerCaptor.capture())
+
+ backlightListenerCaptor.value.onBacklightChanged(
+ current = 1,
+ max = 5,
+ triggeredByKeyPress = true
+ )
+ assertThat(backlight).isNotNull()
+ }
+ }
+
+ private fun KeyboardBacklightListener.onBacklightChanged(
+ current: Int,
+ max: Int,
+ triggeredByKeyPress: Boolean = true
+ ) {
+ onKeyboardBacklightChanged(
+ /* deviceId= */ 0,
+ TestBacklightState(current, max),
+ triggeredByKeyPress
+ )
}
private companion object {
@@ -199,4 +269,12 @@
whenever(it.isFullKeyboard).thenReturn(fullKeyboard)
}
}
+
+ private class TestBacklightState(
+ private val brightnessLevel: Int,
+ private val maxBrightnessLevel: Int
+ ) : KeyboardBacklightState() {
+ override fun getBrightnessLevel() = brightnessLevel
+ override fun getMaxBrightnessLevel() = maxBrightnessLevel
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index b9cfc65..e981d62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -25,13 +25,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -59,13 +57,14 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WorkLockActivityControllerTest extends SysuiTestCase {
- private static final int USER_ID = 333;
+ private static final int TASK_USER_ID = 333;
+ private static final int PROFILE_USER_ID = 555;
private static final int TASK_ID = 444;
private static final ActivityManager.RunningTaskInfo TASK_INFO =
new ActivityManager.RunningTaskInfo();
static {
- TASK_INFO.userId = USER_ID;
+ TASK_INFO.userId = TASK_USER_ID;
TASK_INFO.taskId = TASK_ID;
}
@@ -101,10 +100,10 @@
setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_SUCCESS);
// And the controller receives a message saying the profile is locked,
- mTaskStackListener.onTaskProfileLocked(TASK_INFO);
+ mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID);
// The overlay should start and the task the activity started in should not be removed.
- verifyStartActivity(TASK_ID, true /*taskOverlay*/);
+ verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID);
verify(mIActivityTaskManager, never()).removeTask(anyInt() /*taskId*/);
}
@@ -114,11 +113,11 @@
setActivityStartCode(TASK_ID, true /*taskOverlay*/, ActivityManager.START_CLASS_NOT_FOUND);
// And the controller receives a message saying the profile is locked,
- mTaskStackListener.onTaskProfileLocked(TASK_INFO);
+ mTaskStackListener.onTaskProfileLocked(TASK_INFO, PROFILE_USER_ID);
// The task the activity started in should be removed to prevent the locked task from
// being shown.
- verifyStartActivity(TASK_ID, true /*taskOverlay*/);
+ verifyStartActivity(TASK_ID, true /*taskOverlay*/, PROFILE_USER_ID);
verify(mIActivityTaskManager).removeTask(TASK_ID);
}
@@ -141,12 +140,13 @@
eq(ActivityManager.getCurrentUser()));
}
- private void verifyStartActivity(int taskId, boolean taskOverlay) throws Exception {
+ private void verifyStartActivity(int taskId, boolean taskOverlay, int profileUserId)
+ throws Exception {
verify(mIActivityTaskManager).startActivityAsUser(
eq((IApplicationThread) null),
eq((String) null),
eq((String) null),
- any(Intent.class),
+ argThat(hasUserId(profileUserId)),
eq((String) null),
eq((IBinder) null),
eq((String) null),
@@ -157,24 +157,15 @@
eq(ActivityManager.getCurrentUser()));
}
- private static ArgumentMatcher<Intent> hasComponent(final Context context,
- final Class<? extends Activity> activityClass) {
- return new ArgumentMatcher<Intent>() {
- @Override
- public boolean matches(Intent intent) {
- return new ComponentName(context, activityClass).equals(intent.getComponent());
- }
- };
+ private static ArgumentMatcher<Intent> hasUserId(int userId) {
+ return intent -> intent.getIntExtra(Intent.EXTRA_USER_ID, -1) == userId;
}
private static ArgumentMatcher<Bundle> hasOptions(final int taskId, final boolean overlay) {
- return new ArgumentMatcher<Bundle>() {
- @Override
- public boolean matches(Bundle item) {
- final ActivityOptions options = ActivityOptions.fromBundle(item);
- return (options.getLaunchTaskId() == taskId)
- && (options.getTaskOverlay() == overlay);
- }
+ return item -> {
+ final ActivityOptions options = ActivityOptions.fromBundle(item);
+ return (options.getLaunchTaskId() == taskId)
+ && (options.getTaskOverlay() == overlay);
};
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b50cf73..1d0b58a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -67,6 +67,7 @@
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.captureMany
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
@@ -193,8 +194,24 @@
bypassControllerOverride: KeyguardBypassController? = bypassController
): DeviceEntryFaceAuthRepositoryImpl {
val systemClock = FakeSystemClock()
- val faceAuthBuffer = TableLogBuffer(10, "face auth", systemClock)
- val faceDetectBuffer = TableLogBuffer(10, "face detect", systemClock)
+ val faceAuthBuffer =
+ TableLogBuffer(
+ 10,
+ "face auth",
+ systemClock,
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope
+ )
+ val faceDetectBuffer =
+ TableLogBuffer(
+ 10,
+ "face detect",
+ systemClock,
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope
+ )
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
KeyguardTransitionInteractor(keyguardTransitionRepository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/FakeLogProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/FakeLogProxy.kt
new file mode 100644
index 0000000..471c461
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/FakeLogProxy.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.table
+
+/**
+ * Fake [LogProxy] that collects all lines sent to it. Mimics the ADB logcat format without the
+ * timestamp. [FakeLogProxy.d] will write a log like so:
+ * ```
+ * logger.d("TAG", "message here")
+ * // writes this to the [logs] field
+ * "D TAG: message here"
+ * ```
+ *
+ * Logs sent to this class are collected as a list of strings for simple test assertions.
+ */
+class FakeLogProxy : LogProxy {
+ val logs: MutableList<String> = mutableListOf()
+
+ override fun v(tag: String, message: String) {
+ logs.add("V $tag: $message")
+ }
+
+ override fun d(tag: String, message: String) {
+ logs.add("D $tag: $message")
+ }
+
+ override fun i(tag: String, message: String) {
+ logs.add("I $tag: $message")
+ }
+
+ override fun w(tag: String, message: String) {
+ logs.add("W $tag: $message")
+ }
+
+ override fun e(tag: String, message: String) {
+ logs.add("E $tag: $message")
+ }
+
+ override fun wtf(tag: String, message: String) {
+ logs.add("WTF $tag: $message")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index c49337a..1d182cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
@@ -39,7 +40,8 @@
@OptIn(ExperimentalCoroutinesApi::class)
class LogDiffsForTableTest : SysuiTestCase() {
- private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
private lateinit var systemClock: FakeSystemClock
private lateinit var tableLogBuffer: TableLogBuffer
@@ -47,7 +49,15 @@
@Before
fun setUp() {
systemClock = FakeSystemClock()
- tableLogBuffer = TableLogBuffer(MAX_SIZE, BUFFER_NAME, systemClock)
+ tableLogBuffer =
+ TableLogBuffer(
+ MAX_SIZE,
+ BUFFER_NAME,
+ systemClock,
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
}
// ---- Flow<Boolean> tests ----
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index af83a56..8ba7643 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -22,13 +22,20 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Test
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class TableLogBufferFactoryTest : SysuiTestCase() {
private val dumpManager: DumpManager = mock()
private val systemClock = FakeSystemClock()
- private val underTest = TableLogBufferFactory(dumpManager, systemClock)
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val underTest =
+ TableLogBufferFactory(dumpManager, systemClock, mock(), testDispatcher, testScope)
@Test
fun create_alwaysCreatesNewInstance() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index aed830a..a2b2322 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -19,31 +19,66 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.plugins.log.LogcatEchoTracker
+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.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Before
import org.junit.Test
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class TableLogBufferTest : SysuiTestCase() {
private lateinit var underTest: TableLogBuffer
private lateinit var systemClock: FakeSystemClock
private lateinit var outputWriter: StringWriter
+ private lateinit var logcatEchoTracker: LogcatEchoTracker
+ private lateinit var localLogcat: FakeLogProxy
+
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
@Before
fun setup() {
+ localLogcat = FakeLogProxy()
+ logcatEchoTracker = mock()
systemClock = FakeSystemClock()
outputWriter = StringWriter()
- underTest = TableLogBuffer(MAX_SIZE, NAME, systemClock)
+ underTest =
+ TableLogBuffer(
+ MAX_SIZE,
+ NAME,
+ systemClock,
+ logcatEchoTracker,
+ testDispatcher,
+ testScope.backgroundScope,
+ localLogcat = localLogcat,
+ )
+ underTest.init()
}
@Test(expected = IllegalArgumentException::class)
fun maxSizeZero_throwsException() {
- TableLogBuffer(maxSize = 0, "name", systemClock)
+ TableLogBuffer(
+ maxSize = 0,
+ "name",
+ systemClock,
+ logcatEchoTracker,
+ testDispatcher,
+ testScope.backgroundScope,
+ localLogcat = localLogcat,
+ )
}
@Test
@@ -791,6 +826,112 @@
assertThat(dumpedString).contains(evictedColumnLog3)
}
+ @Test
+ fun logcat_bufferNotLoggable_tagNotLoggable_noEcho() {
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenReturn(false)
+ whenever(logcatEchoTracker.isTagLoggable(eq("columnName"), any())).thenReturn(false)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ assertThat(localLogcat.logs).isEmpty()
+ }
+
+ @Test
+ fun logcat_bufferIsLoggable_tagNotLoggable_echoes() {
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenReturn(true)
+ whenever(logcatEchoTracker.isTagLoggable(eq("columnName"), any())).thenReturn(false)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ assertThat(localLogcat.logs).hasSize(1)
+ }
+
+ @Test
+ fun logcat_bufferNotLoggable_tagIsLoggable_echoes() {
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenReturn(false)
+ whenever(logcatEchoTracker.isTagLoggable(eq("columnName"), any())).thenReturn(true)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ assertThat(localLogcat.logs).hasSize(1)
+ }
+
+ @Test
+ fun logcat_echoesDebugLogs_debugDisabled_noEcho() {
+ // Allow any log other than debug
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenAnswer { invocation ->
+ (invocation.getArgument(1) as LogLevel) != LogLevel.DEBUG
+ }
+
+ underTest.logChange("prefix", "columnName", true)
+
+ assertThat(localLogcat.logs).isEmpty()
+ }
+
+ @Test
+ fun logcat_echoesDebugLogs_debugEnabled_echoes() {
+ // Only allow debug logs
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), eq(LogLevel.DEBUG))).thenReturn(true)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ assertThat(localLogcat.logs).hasSize(1)
+ }
+
+ @Test
+ fun logcat_bufferNotLoggable_tagIsLoggable_usesColNameForTagCheck() {
+ systemClock.setCurrentTimeMillis(1000L)
+
+ val nonLoggingTag = "nonLoggingColName"
+ val loggingTag = "loggingColName"
+
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenReturn(false)
+ whenever(logcatEchoTracker.isTagLoggable(eq(loggingTag), eq(LogLevel.DEBUG)))
+ .thenReturn(true)
+ whenever(logcatEchoTracker.isTagLoggable(eq(nonLoggingTag), eq(LogLevel.DEBUG)))
+ .thenReturn(false)
+
+ underTest.logChange("", nonLoggingTag, true)
+ underTest.logChange("", loggingTag, true)
+
+ assertThat(localLogcat.logs).hasSize(1)
+
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(1000L)
+ val expectedMessage = "${timestamp}${SEPARATOR}${loggingTag}${SEPARATOR}true"
+ val expectedLine = "D $NAME: $expectedMessage"
+
+ assertThat(localLogcat.logs[0]).isEqualTo(expectedLine)
+ }
+
+ @Test
+ fun logcat_bufferLoggable_multipleMessagesAreEchoed() {
+ systemClock.setCurrentTimeMillis(1000L)
+ whenever(logcatEchoTracker.isBufferLoggable(eq(NAME), any())).thenReturn(true)
+
+ val col1 = "column1"
+ val col2 = "column2"
+
+ // Log a couple of columns that flip bits
+ underTest.logChange("", col1, true)
+ underTest.logChange("", col2, false)
+ underTest.logChange("", col1, false)
+ underTest.logChange("", col2, true)
+
+ assertThat(localLogcat.logs).hasSize(4)
+
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(1000L)
+ val msg1 = "${timestamp}${SEPARATOR}${col1}${SEPARATOR}true"
+ val msg2 = "${timestamp}${SEPARATOR}${col2}${SEPARATOR}false"
+ val msg3 = "${timestamp}${SEPARATOR}${col1}${SEPARATOR}false"
+ val msg4 = "${timestamp}${SEPARATOR}${col2}${SEPARATOR}true"
+ val expected = listOf(msg1, msg2, msg3, msg4).map { "D $NAME: $it" }
+
+ // Logs use the same bg dispatcher for writing to logcat, they should be in order
+ for ((msg, logLine) in expected zip localLogcat.logs) {
+ assertThat(logLine).isEqualTo(msg)
+ }
+ }
+
private fun dumpChanges(): String {
underTest.dump(PrintWriter(outputWriter), arrayOf())
return outputWriter.toString()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 0a1db60..d428db7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -40,7 +40,6 @@
import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.internal.statusbar.IStatusBarService
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.R
@@ -131,7 +130,6 @@
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var smartspaceManager: SmartspaceManager
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock lateinit var statusBarService: IStatusBarService
lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@@ -194,8 +192,7 @@
mediaFlags = mediaFlags,
logger = logger,
smartspaceManager = smartspaceManager,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- statusBarService = statusBarService,
+ keyguardUpdateMonitor = keyguardUpdateMonitor
)
verify(tunerService)
.addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -520,136 +517,19 @@
}
@Test
- fun testOnNotificationAdded_emptyTitle_notLoaded() {
- // GIVEN that the manager has a notification with an empty title.
+ fun testOnNotificationRemoved_emptyTitle_notConverted() {
+ // GIVEN that the manager has a notification with a resume action and empty title.
whenever(controller.metadata)
.thenReturn(
metadataBuilder
.putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
.build()
)
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationAdded_blankTitle_notLoaded() {
- // GIVEN that the manager has a notification with a blank title.
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
- verify(logger, never()).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), any())
- }
-
- @Test
- fun testOnNotificationUpdated_invalidTitle_logMediaRemoved() {
- addNotificationAndLoad()
- val data = mediaDataCaptor.value
-
- verify(listener)
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
-
- reset(listener)
- whenever(controller.metadata)
- .thenReturn(
- metadataBuilder
- .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
- .build()
- )
- mediaDataManager.onNotificationAdded(KEY, mediaNotification)
- assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
- assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(statusBarService)
- .onNotificationError(
- eq(PACKAGE_NAME),
- eq(mediaNotification.tag),
- eq(mediaNotification.id),
- eq(mediaNotification.uid),
- eq(mediaNotification.initialPid),
- eq(MEDIA_TITLE_ERROR_MESSAGE),
- eq(mediaNotification.user.identifier)
- )
- verify(listener, never())
- .onMediaDataLoaded(
- eq(KEY),
- eq(null),
- capture(mediaDataCaptor),
- eq(true),
- eq(0),
- eq(false)
- )
- verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
- }
-
- @Test
- fun testOnNotificationRemoved_emptyTitle_notConverted() {
- // GIVEN that the manager has a notification with a resume action and empty title.
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_EMPTY_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
@@ -674,15 +554,17 @@
@Test
fun testOnNotificationRemoved_blankTitle_notConverted() {
// GIVEN that the manager has a notification with a resume action and blank title.
+ whenever(controller.metadata)
+ .thenReturn(
+ metadataBuilder
+ .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_BLANK_TITLE)
+ .build()
+ )
addNotificationAndLoad()
val data = mediaDataCaptor.value
val instanceId = data.instanceId
assertThat(data.resumption).isFalse()
- mediaDataManager.onMediaDataLoaded(
- KEY,
- null,
- data.copy(song = SESSION_BLANK_TITLE, resumeAction = Runnable {})
- )
+ mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
reset(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index a45e9d9..0c57e7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -468,7 +468,7 @@
}
@Test
- fun audioInfoChanged() {
+ fun audioInfoPlaybackTypeChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
// GIVEN a controller with local playback type
@@ -486,6 +486,25 @@
}
@Test
+ fun audioInfoVolumeControlIdChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+ whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with local playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with a volume control id change
+ whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is checked
+ verify(mr2).getRoutingSessionForMediaController(eq(controller))
+ }
+
+ @Test
fun audioInfoHasntChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0bdcaf4..299303d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -33,7 +34,12 @@
import android.app.KeyguardManager;
import android.app.Notification;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
@@ -44,6 +50,7 @@
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.PowerExemptionManager;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
@@ -74,6 +81,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@@ -93,29 +103,54 @@
private static final String TEST_SONG = "test_song";
private static final String TEST_SESSION_ID = "test_session_id";
private static final String TEST_SESSION_NAME = "test_session_name";
- private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private final ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController = mock(
- ActivityLaunchAnimator.Controller.class);
- private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
- NearbyMediaDevicesManager.class);
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock
+ private ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController;
+ @Mock
+ private NearbyMediaDevicesManager mNearbyMediaDevicesManager;
// Mock
- private MediaController mMediaController = mock(MediaController.class);
- private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
- private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager =
- mock(CachedBluetoothDeviceManager.class);
- private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
- private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class);
- private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
- private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
- private NearbyDevice mNearbyDevice1 = mock(NearbyDevice.class);
- private NearbyDevice mNearbyDevice2 = mock(NearbyDevice.class);
- private MediaMetadata mMediaMetadata = mock(MediaMetadata.class);
- private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
- private ActivityStarter mStarter = mock(ActivityStarter.class);
- private AudioManager mAudioManager = mock(AudioManager.class);
- private KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
- private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
- private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
+ @Mock
+ private MediaController mMediaController;
+ @Mock
+ private MediaSessionManager mMediaSessionManager;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private MediaOutputController.Callback mCb;
+ @Mock
+ private MediaDevice mMediaDevice1;
+ @Mock
+ private MediaDevice mMediaDevice2;
+ @Mock
+ private NearbyDevice mNearbyDevice1;
+ @Mock
+ private NearbyDevice mNearbyDevice2;
+ @Mock
+ private MediaMetadata mMediaMetadata;
+ @Mock
+ private RoutingSessionInfo mRemoteSessionInfo;
+ @Mock
+ private ActivityStarter mStarter;
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
+ private ActivityLaunchAnimator.Controller mController;
+ @Mock
+ private PowerExemptionManager mPowerExemptionManager;
+ @Mock
+ private CommonNotifCollection mNotifCollection;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private Drawable mDrawable;
+ @Mock
+ private PlaybackState mPlaybackState;
+
private FeatureFlags mFlags = mock(FeatureFlags.class);
private View mDialogLaunchView = mock(View.class);
private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
@@ -131,8 +166,11 @@
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext.setMockPackageManager(mPackageManager);
mSpyContext = spy(mContext);
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
doReturn(mMediaSessionManager).when(mSpyContext).getSystemService(
@@ -258,6 +296,34 @@
}
@Test
+ public void tryToLaunchMediaApplication_intentNotNull_startActivity() {
+ when(mDialogLaunchAnimator.createActivityLaunchController(any(View.class))).thenReturn(
+ mController);
+ Intent intent = new Intent(TEST_PACKAGE_NAME);
+ doReturn(intent).when(mPackageManager).getLaunchIntentForPackage(TEST_PACKAGE_NAME);
+ mMediaOutputController.start(mCallback);
+
+ mMediaOutputController.tryToLaunchMediaApplication(mDialogLaunchView);
+
+ verify(mStarter).startActivity(any(Intent.class), anyBoolean(),
+ Mockito.eq(mController));
+ }
+
+ @Test
+ public void tryToLaunchInAppRoutingIntent_componentNameNotNull_startActivity() {
+ when(mDialogLaunchAnimator.createActivityLaunchController(any(View.class))).thenReturn(
+ mController);
+ mMediaOutputController.start(mCallback);
+ when(mLocalMediaManager.getLinkedItemComponentName()).thenReturn(
+ new ComponentName(TEST_PACKAGE_NAME, ""));
+
+ mMediaOutputController.tryToLaunchInAppRoutingIntent(TEST_DEVICE_1_ID, mDialogLaunchView);
+
+ verify(mStarter).startActivity(any(Intent.class), anyBoolean(),
+ Mockito.eq(mController));
+ }
+
+ @Test
public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException {
mMediaOutputController.start(mCb);
@@ -342,6 +408,30 @@
}
@Test
+ public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize() {
+ when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
+ when(mMediaDevice1.getFeatures()).thenReturn(
+ ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK));
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1);
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+ final List<MediaDevice> devices = new ArrayList<>();
+ for (MediaItem item : mMediaOutputController.getMediaItemList()) {
+ if (item.getMediaDevice().isPresent()) {
+ devices.add(item.getMediaDevice().get());
+ }
+ }
+
+ assertThat(devices.containsAll(mMediaDevices)).isTrue();
+ assertThat(devices.size()).isEqualTo(mMediaDevices.size());
+ assertThat(mMediaOutputController.getMediaItemList().size()).isEqualTo(
+ mMediaDevices.size() + 1);
+ verify(mCb).onDeviceListChanged();
+ }
+
+ @Test
public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() {
when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
when(mMediaDevice1.isSuggestedDevice()).thenReturn(true);
@@ -582,6 +672,17 @@
}
@Test
+ public void isAnyDeviceTransferring_advancedLayoutSupport() {
+ when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(true);
+ when(mMediaDevice1.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mMediaOutputController.start(mCb);
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.isAnyDeviceTransferring()).isTrue();
+ }
+
+ @Test
public void isPlaying_stateIsNull() {
when(mMediaController.getPlaybackState()).thenReturn(null);
@@ -661,22 +762,6 @@
}
@Test
- public void connectDevice_verifyConnect() {
- when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2);
-
- mMediaOutputController.connectDevice(mMediaDevice1);
-
- // Wait for background thread execution
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- verify(mLocalMediaManager).connectDevice(mMediaDevice1);
- }
-
- @Test
public void getActiveRemoteMediaDevice_isSystemSession_returnSession() {
when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
@@ -883,6 +968,56 @@
}
@Test
+ public void getDeviceIconCompat_deviceIconIsNotNull_returnsIcon() {
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2);
+ when(mMediaDevice1.getIcon()).thenReturn(mDrawable);
+
+ assertThat(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).isInstanceOf(
+ IconCompat.class);
+ }
+
+ @Test
+ public void getDeviceIconCompat_deviceIconIsNull_returnsIcon() {
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2);
+ when(mMediaDevice1.getIcon()).thenReturn(null);
+
+ assertThat(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).isInstanceOf(
+ IconCompat.class);
+ }
+
+ @Test
+ public void setColorFilter_setColorFilterToDrawable() {
+ mMediaOutputController.setColorFilter(mDrawable, true);
+
+ verify(mDrawable).setColorFilter(any(PorterDuffColorFilter.class));
+ }
+
+ @Test
+ public void resetGroupMediaDevices_clearGroupDevices() {
+ final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+ final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+ when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+ assertThat(mMediaOutputController.getGroupMediaDevices().isEmpty()).isFalse();
+
+ mMediaOutputController.resetGroupMediaDevices();
+
+ assertThat(mMediaOutputController.mGroupMediaDevices.isEmpty()).isTrue();
+ }
+
+ @Test
public void isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse() {
when(mMediaDevice1.getDeviceType()).thenReturn(
MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
@@ -919,6 +1054,42 @@
}
@Test
+ public void setTemporaryAllowListExceptionIfNeeded_packageNameIsNull_NoAction() {
+ MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
+ null,
+ mMediaSessionManager, mLocalBluetoothManager, mStarter,
+ mNotifCollection, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mKeyguardManager, mFlags);
+
+ testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
+
+ verify(mPowerExemptionManager, never()).addToTemporaryAllowList(anyString(), anyInt(),
+ anyString(),
+ anyLong());
+ }
+
+ @Test
+ public void onMetadataChanged_triggersOnMetadataChanged() {
+ mMediaOutputController.mCallback = this.mCallback;
+
+ mMediaOutputController.mCb.onMetadataChanged(mMediaMetadata);
+
+ verify(mMediaOutputController.mCallback).onMediaChanged();
+ }
+
+ @Test
+ public void onPlaybackStateChanged_updateWithNullState_onMediaStoppedOrPaused() {
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ mMediaOutputController.mCallback = this.mCallback;
+ mMediaOutputController.start(mCb);
+
+ mMediaOutputController.mCb.onPlaybackStateChanged(null);
+
+ verify(mMediaOutputController.mCallback).onMediaStoppedOrPaused();
+ }
+
+ @Test
public void launchBluetoothPairing_isKeyguardLocked_dismissDialog() {
when(mDialogLaunchAnimator.createActivityLaunchController(mDialogLaunchView)).thenReturn(
mActivityLaunchAnimatorController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java
index fb5197e..4ce32d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/DynamicColorTest.java
@@ -17,18 +17,16 @@
package com.android.systemui.monet;
import static com.android.systemui.monet.utils.ArgbSubject.assertThat;
-
import static org.junit.Assert.assertTrue;
-
import androidx.test.filters.SmallTest;
-import com.android.systemui.SysuiTestCase;
import com.android.systemui.monet.contrast.Contrast;
import com.android.systemui.monet.dynamiccolor.DynamicColor;
import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors;
import com.android.systemui.monet.dynamiccolor.ToneDeltaConstraint;
import com.android.systemui.monet.dynamiccolor.TonePolarity;
+import com.android.systemui.SysuiTestCase;
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.DynamicScheme;
import com.android.systemui.monet.scheme.SchemeContent;
@@ -36,182 +34,174 @@
import com.android.systemui.monet.scheme.SchemeMonochrome;
import com.android.systemui.monet.scheme.SchemeTonalSpot;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
-public final class DynamicColorTest extends SysuiTestCase {
+public final class DynamicColorTest extends SysuiTestCase{
- @Test
- public void fromArgbNoBackground_doesntChangeForContrast() {
- final int blueArgb = 0xff0000ff;
- final DynamicColor dynamicColor = DynamicColor.fromArgb(blueArgb);
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
- final SchemeTonalSpot standardContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false,
- 0.0);
- assertThat(dynamicColor.getArgb(standardContrast)).isSameColorAs(blueArgb);
+ @Test
+ public void fromArgbNoBackground_doesntChangeForContrast() {
+ final int blueArgb = 0xff0000ff;
+ final DynamicColor dynamicColor = DynamicColor.fromArgb(blueArgb);
- final SchemeTonalSpot minContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, -1.0);
- assertThat(dynamicColor.getArgb(minContrast)).isSameColorAs(blueArgb);
+ final SchemeTonalSpot standardContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0);
+ assertThat(dynamicColor.getArgb(standardContrast)).isSameColorAs(blueArgb);
- final SchemeTonalSpot maxContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 1.0);
- assertThat(dynamicColor.getArgb(maxContrast)).isSameColorAs(blueArgb);
- }
+ final SchemeTonalSpot minContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, -1.0);
+ assertThat(dynamicColor.getArgb(minContrast)).isSameColorAs(blueArgb);
- @Test
- public void toneDeltaConstraintNoPreference_evaluatesCorrectly() {
- final int blueArgb = 0xff0000ff;
- final int redArgb = 0xffff0000;
- final DynamicColor otherDynamicColor = DynamicColor.fromArgb(redArgb);
- final DynamicColor dynamicColor =
- DynamicColor.fromArgb(
- blueArgb,
- (s) -> 30.0,
- null,
- (s) -> new ToneDeltaConstraint(30, otherDynamicColor,
- TonePolarity.NO_PREFERENCE));
- final SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0);
- assertThat(dynamicColor.getArgb(scheme)).isSameColorAs(0xff0000ef);
- }
+ final SchemeTonalSpot maxContrast = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 1.0);
+ assertThat(dynamicColor.getArgb(maxContrast)).isSameColorAs(blueArgb);
+ }
- @Test
- public void dynamicColor_withOpacity() {
- final DynamicColor dynamicColor =
- new DynamicColor(
- s -> 0.0,
- s -> 0.0,
- s -> s.isDark ? 100.0 : 0.0,
- s -> s.isDark ? 0.20 : 0.12,
- null,
- scheme ->
- DynamicColor.toneMinContrastDefault(
- (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
- scheme ->
- DynamicColor.toneMaxContrastDefault(
- (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
- null);
- final SchemeTonalSpot lightScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), false,
- 0.0);
- assertThat(dynamicColor.getArgb(lightScheme)).isSameColorAs(0x1f000000);
+ @Test
+ public void toneDeltaConstraintNoPreference_evaluatesCorrectly() {
+ final int blueArgb = 0xff0000ff;
+ final int redArgb = 0xffff0000;
+ final DynamicColor otherDynamicColor = DynamicColor.fromArgb(redArgb);
+ final DynamicColor dynamicColor =
+ DynamicColor.fromArgb(
+ blueArgb,
+ (s) -> 30.0,
+ null,
+ (s) -> new ToneDeltaConstraint(30, otherDynamicColor, TonePolarity.NO_PREFERENCE));
+ final SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(blueArgb), false, 0.0);
+ assertThat(dynamicColor.getArgb(scheme)).isSameColorAs(0xff0000ef);
+ }
- final SchemeTonalSpot darkScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), true, 0.0);
- assertThat(dynamicColor.getArgb(darkScheme)).isSameColorAs(0x33ffffff);
- }
+ @Test
+ public void dynamicColor_withOpacity() {
+ final DynamicColor dynamicColor =
+ new DynamicColor(
+ s -> 0.0,
+ s -> 0.0,
+ s -> s.isDark ? 100.0 : 0.0,
+ s -> s.isDark ? 0.20 : 0.12,
+ null,
+ scheme ->
+ DynamicColor.toneMinContrastDefault(
+ (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
+ scheme ->
+ DynamicColor.toneMaxContrastDefault(
+ (s) -> s.isDark ? 100.0 : 0.0, null, scheme, null),
+ null);
+ final SchemeTonalSpot lightScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), false, 0.0);
+ assertThat(dynamicColor.getArgb(lightScheme)).isSameColorAs(0x1f000000);
- @Test
- public void respectsContrast() {
- final Hct[] seedColors =
- new Hct[]{
- Hct.fromInt(0xFFFF0000),
- Hct.fromInt(0xFFFFFF00),
- Hct.fromInt(0xFF00FF00),
- Hct.fromInt(0xFF0000FF)
- };
+ final SchemeTonalSpot darkScheme = new SchemeTonalSpot(Hct.fromInt(0xff4285f4), true, 0.0);
+ assertThat(dynamicColor.getArgb(darkScheme)).isSameColorAs(0x33ffffff);
+ }
- final double[] contrastLevels = {-1.0, -0.5, 0.0, 0.5, 1.0};
+ @Test
+ public void respectsContrast() {
+ final Hct[] seedColors =
+ new Hct[] {
+ Hct.fromInt(0xFFFF0000),
+ Hct.fromInt(0xFFFFFF00),
+ Hct.fromInt(0xFF00FF00),
+ Hct.fromInt(0xFF0000FF)
+ };
- for (Hct seedColor : seedColors) {
- for (double contrastLevel : contrastLevels) {
- for (boolean isDark : new boolean[]{false, true}) {
- final DynamicScheme[] schemes =
- new DynamicScheme[]{
- new SchemeContent(seedColor, isDark, contrastLevel),
- new SchemeMonochrome(seedColor, isDark, contrastLevel),
- new SchemeTonalSpot(seedColor, isDark, contrastLevel),
- new SchemeFidelity(seedColor, isDark, contrastLevel)
- };
- for (final DynamicScheme scheme : schemes) {
- assertTrue(
- pairSatisfiesContrast(
- scheme, MaterialDynamicColors.onPrimary,
- MaterialDynamicColors.primary));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onPrimaryContainer,
- MaterialDynamicColors.primaryContainer));
- assertTrue(
- pairSatisfiesContrast(
- scheme, MaterialDynamicColors.onSecondary,
- MaterialDynamicColors.secondary));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onSecondaryContainer,
- MaterialDynamicColors.secondaryContainer));
- assertTrue(
- pairSatisfiesContrast(
- scheme, MaterialDynamicColors.onTertiary,
- MaterialDynamicColors.tertiary));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onTertiaryContainer,
- MaterialDynamicColors.tertiaryContainer));
- assertTrue(
- pairSatisfiesContrast(
- scheme, MaterialDynamicColors.onError,
- MaterialDynamicColors.error));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onErrorContainer,
- MaterialDynamicColors.errorContainer));
- assertTrue(
- pairSatisfiesContrast(
- scheme, MaterialDynamicColors.onBackground,
- MaterialDynamicColors.background));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onSurfaceVariant,
- MaterialDynamicColors.surfaceVariant));
- assertTrue(
- pairSatisfiesContrast(
- scheme,
- MaterialDynamicColors.onSurfaceInverse,
- MaterialDynamicColors.surfaceInverse));
- }
- }
- }
+ final double[] contrastLevels = {-1.0, -0.5, 0.0, 0.5, 1.0};
+
+ for (Hct seedColor : seedColors) {
+ for (double contrastLevel : contrastLevels) {
+ for (boolean isDark : new boolean[] {false, true}) {
+ final DynamicScheme[] schemes =
+ new DynamicScheme[] {
+ new SchemeContent(seedColor, isDark, contrastLevel),
+ new SchemeMonochrome(seedColor, isDark, contrastLevel),
+ new SchemeTonalSpot(seedColor, isDark, contrastLevel),
+ new SchemeFidelity(seedColor, isDark, contrastLevel)
+ };
+ for (final DynamicScheme scheme : schemes) {
+ assertTrue(
+ pairSatisfiesContrast(scheme, dynamicColors.onPrimary(), dynamicColors.primary()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onPrimaryContainer(), dynamicColors.primaryContainer()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onSecondary(), dynamicColors.secondary()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme,
+ dynamicColors.onSecondaryContainer(),
+ dynamicColors.secondaryContainer()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onTertiary(), dynamicColors.tertiary()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme,
+ dynamicColors.onTertiaryContainer(),
+ dynamicColors.tertiaryContainer()));
+ assertTrue(
+ pairSatisfiesContrast(scheme, dynamicColors.onError(), dynamicColors.error()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onErrorContainer(), dynamicColors.errorContainer()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onBackground(), dynamicColors.background()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.onSurfaceVariant(), dynamicColors.surfaceVariant()));
+ assertTrue(
+ pairSatisfiesContrast(
+ scheme, dynamicColors.inverseOnSurface(), dynamicColors.inverseSurface()));
+ }
}
+ }
}
+ }
- @Test
- public void valuesAreCorrect() {
- // Checks that the values of certain dynamic colors match Dart results.
- assertThat(
- MaterialDynamicColors.onPrimaryContainer.getArgb(
- new SchemeFidelity(Hct.fromInt(0xFFFF0000), false, 0.5)))
- .isSameColorAs(0xFFFFE5E1);
- assertThat(
- MaterialDynamicColors.onSecondaryContainer.getArgb(
- new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.5)))
- .isSameColorAs(0xFFFFFCFF);
- assertThat(
- MaterialDynamicColors.onTertiaryContainer.getArgb(
- new SchemeContent(Hct.fromInt(0xFFFFFF00), true, -0.5)))
- .isSameColorAs(0xFF616600);
- assertThat(
- MaterialDynamicColors.surfaceInverse.getArgb(
- new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.0)))
- .isSameColorAs(0xFF464652);
- assertThat(
- MaterialDynamicColors.primaryInverse.getArgb(
- new SchemeContent(Hct.fromInt(0xFFFF0000), false, -0.5)))
- .isSameColorAs(0xFFFF8C7A);
- assertThat(
- MaterialDynamicColors.outlineVariant.getArgb(
- new SchemeContent(Hct.fromInt(0xFFFFFF00), true, 0.0)))
- .isSameColorAs(0xFF484831);
- }
+ @Test
+ public void valuesAreCorrect() {
+ // Checks that the values of certain dynamic colors match Dart results.
+ assertThat(
+ dynamicColors
+ .onPrimaryContainer()
+ .getArgb(new SchemeFidelity(Hct.fromInt(0xFFFF0000), false, 0.5)))
+ .isSameColorAs(0xFFFFE5E1);
+ assertThat(
+ dynamicColors
+ .onSecondaryContainer()
+ .getArgb(new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.5)))
+ .isSameColorAs(0xFFFFFCFF);
+ assertThat(
+ dynamicColors
+ .onTertiaryContainer()
+ .getArgb(new SchemeContent(Hct.fromInt(0xFFFFFF00), true, -0.5)))
+ .isSameColorAs(0xFF616600);
+ assertThat(
+ dynamicColors
+ .inverseSurface()
+ .getArgb(new SchemeContent(Hct.fromInt(0xFF0000FF), false, 0.0)))
+ .isSameColorAs(0xFF2F2F3B);
+ assertThat(
+ dynamicColors
+ .inversePrimary()
+ .getArgb(new SchemeContent(Hct.fromInt(0xFFFF0000), false, -0.5)))
+ .isSameColorAs(0xFFFF907F);
+ assertThat(
+ dynamicColors
+ .outlineVariant()
+ .getArgb(new SchemeContent(Hct.fromInt(0xFFFFFF00), true, 0.0)))
+ .isSameColorAs(0xFF484831);
+ }
- private boolean pairSatisfiesContrast(DynamicScheme scheme, DynamicColor fg, DynamicColor bg) {
- double fgTone = fg.getHct(scheme).getTone();
- double bgTone = bg.getHct(scheme).getTone();
- double minimumRequirement = scheme.contrastLevel >= 0.0 ? 4.5 : 3.0;
- return Contrast.ratioOfTones(fgTone, bgTone) >= minimumRequirement;
- }
+ private boolean pairSatisfiesContrast(DynamicScheme scheme, DynamicColor fg, DynamicColor bg) {
+ double fgTone = fg.getHct(scheme).getTone();
+ double bgTone = bg.getHct(scheme).getTone();
+ double minimumRequirement = scheme.contrastLevel >= 0.0 ? 4.5 : 3.0;
+ return Contrast.ratioOfTones(fgTone, bgTone) >= minimumRequirement;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java
index 1ddfc4d..704aed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeContentTest.java
@@ -25,246 +25,229 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeContent;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeContentTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff080CFF);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff656DD3);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff81009F);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff767684);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff757589);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF1218FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF1218FF);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF0001C3);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF0001C3);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF000181);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF000181);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF5660FF);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF5660FF);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF2D36FF);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF2D36FF);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF0000E3);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E3);
}
@Test
public void lightTheme_minContrast_tertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFB042CC);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFB042CC);
}
@Test
public void lightTheme_standardContrast_tertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF9221AF);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF9221AF);
}
@Test
public void lightTheme_maxContrast_tertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF73008E);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF73008E);
}
@Test
public void lightTheme_minContrast_objectionableTertiaryContainerLightens() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFD03A71);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFD03A71);
}
@Test
public void lightTheme_standardContrast_objectionableTertiaryContainerLightens() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFAC1B57);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFAC1B57);
}
@Test
public void lightTheme_maxContrast_objectionableTertiaryContainerDarkens() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF850096), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF870040);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF870040);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFCBCDFF);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFCBCDFF);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFCECFFF);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFCECFFF);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFD6D6FF);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFD6D6FF);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, -1);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFFFBF8FF);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF5660FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF5660FF);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFBEC2FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFBEC2FF);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFF6F4FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFF6F4FF);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF0000E6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E6);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF0000E6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFF0000E6);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFC4C6FF);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xFFC4C6FF);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF7A83FF);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFF7A83FF);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFA4AAFF);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFFA4AAFF);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF0001C6);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xFF0001C6);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFCF60EA);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFCF60EA);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFFEB8CFF);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFFEB8CFF);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xFF63007B);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xFF63007B);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeContent scheme = new SchemeContent(Hct.fromInt(0xFF0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xFF12121D);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xFF12121D);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java
index 31e8711..32a589d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeExpressiveTest.java
@@ -25,183 +25,175 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeExpressive;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeExpressiveTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff35855F);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff8C6D8C);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff806EA1);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff79757F);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff7A7585);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff32835D);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffad603c);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff146C48);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff924b28);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff002818);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff401400);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffA2F4C6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffA2F4C6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff004D31);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff6f3010);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff1e724e);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff99512e);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff002112);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff351000);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff9aebbe);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffd0bc);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffdf7ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff32835d);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffad603c);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff87d7ab);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffb595);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffd5ffe4);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfffff3ee);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff005234);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff743413);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff005234);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff743413);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff8bdbaf);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffffbb9e);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff76c59b);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xfff99f75);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffa2f4c6);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffdbcc);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff004229);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff622706);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeExpressive scheme = new SchemeExpressive(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff14121a);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java
index 511f83b..6844a92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFidelityTest.java
@@ -25,245 +25,230 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeFidelity;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeFidelityTest extends SysuiTestCase {
+
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff080CFF);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff656DD3);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff9D0002);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff767684);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff757589);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff1218ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff1218ff);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff0001c3);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff0001c3);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff000181);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000181);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff5660ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff5660ff);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff2d36ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff2d36ff);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000e3);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e3);
}
@Test
public void lightTheme_minContrast_tertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd93628);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd93628);
}
@Test
public void lightTheme_standardContrast_tertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffb31910);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffb31910);
}
@Test
public void lightTheme_maxContrast_tertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff8c0002);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xff8c0002);
}
@Test
public void lightTheme_minContrast_objectionableTertiaryContainerLightens() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffbcac5a);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffbcac5a);
}
@Test
public void lightTheme_standardContrast_objectionableTertiaryContainerLightens() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffbcac5a);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xffbcac5a);
}
@Test
public void lightTheme_maxContrast_objectionableTertiaryContainerDarkens() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff850096), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff4d4300);
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(0xff4d4300);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffcbcdff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcbcdff);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffcecfff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcecfff);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd6d6ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000e6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e6);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000e6);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e6);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffc4c6ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff7a83ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff7a83ff);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffa4aaff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffa4aaff);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0001c6);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0001c6);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xfffe513e);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xfffe513e);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffFF9181);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffFF9181);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff790001);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff790001);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeFidelity scheme = new SchemeFidelity(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12121d);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12121d);
}
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java
index 4cf14d5..4bca2c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeFruitSaladTest.java
@@ -25,226 +25,230 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeFruitSalad;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeFruitSaladTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff0091C0);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff3A7E9E);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff6E72AC);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff777682);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff75758B);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff007ea7);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff007ea7);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff006688);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff006688);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff002635);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff002635);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xffC2E8FF);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xffC2E8FF);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xff004862);
}
@Test
public void lightTheme_minContrast_tertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffE0E0FF);
}
@Test
public void lightTheme_standardContrast_tertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffE0E0FF);
}
@Test
public void lightTheme_maxContrast_tertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xFF3A3E74);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff006C90);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff001E2B);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xffACE1FF);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff007EA7);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff007EA7);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFF76D1FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFF76D1FF);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xFFECF7FF);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xFFECF7FF);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xFF004D67);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xFF004D67);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xFF83D5FF);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff55C0F2);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xffC2E8FF);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff003E54);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffADB0EF);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffe0e0ff);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xff30346A);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeFruitSalad scheme = new SchemeFruitSalad(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131c);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131c);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java
index 3eca4dc..d511422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeMonochromeTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.monet;
+import static com.google.common.truth.Truth.assertThat;
import static com.android.systemui.monet.utils.ArgbSubject.assertThat;
import androidx.test.filters.SmallTest;
@@ -25,204 +26,227 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeMonochrome;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeMonochromeTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
}
@Test
public void lightTheme_minContrast_primary() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff747474);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff3c3c3c);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5e5e5e);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000000);
}
@Test
public void lightTheme_maxContrast_primary() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff222222);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000000);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e2e2);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff5f5f5f);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e2e2);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3b3b3b);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff434343);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3a3a3a);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff646464);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd9d9d9);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff1b1b1b);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffdadada);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffcdcdcd);
}
@Test
public void lightTheme_minContrast_surface() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, -1);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void lightTheme_maxContrast_surface() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 1);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void darkTheme_minContrast_primary() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff747474);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffcccccc);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffc6c6c6);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void darkTheme_maxContrast_primary() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff5f5f5);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff474747);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffa3a3a3);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff474747);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffd4d4d4);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffcbcbcb);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffd5d5d5);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffb5b5b5);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff393939);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e2e2);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff000000);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff393939);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff404040);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffb5b5b5);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd1d1d1);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e2e2);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff000000);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff393939);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff393939);
}
@Test
public void darkTheme_minContrast_surface() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, -1);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
}
@Test
public void darkTheme_maxContrast_surface() {
- SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 1);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
+ }
+
+ @Test
+ public void darkTheme_monochromeSpec() {
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), true, 0.0);
+ assertThat(dynamicColors.primary().getHct(scheme).getTone()).isWithin(1).of(100);
+ assertThat(dynamicColors.onPrimary().getHct(scheme).getTone()).isWithin(1).of(10);
+ assertThat(dynamicColors.primaryContainer().getHct(scheme).getTone()).isWithin(1).of(85);
+ assertThat(dynamicColors.onPrimaryContainer().getHct(scheme).getTone()).isWithin(1).of(0);
+ assertThat(dynamicColors.secondary().getHct(scheme).getTone()).isWithin(1).of(80);
+ assertThat(dynamicColors.onSecondary().getHct(scheme).getTone()).isWithin(1).of(10);
+ assertThat(dynamicColors.secondaryContainer().getHct(scheme).getTone()).isWithin(1).of(30);
+ assertThat(dynamicColors.onSecondaryContainer().getHct(scheme).getTone()).isWithin(1).of(90);
+ assertThat(dynamicColors.tertiary().getHct(scheme).getTone()).isWithin(1).of(90);
+ assertThat(dynamicColors.onTertiary().getHct(scheme).getTone()).isWithin(1).of(10);
+ assertThat(dynamicColors.tertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(60);
+ assertThat(dynamicColors.onTertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(0);
+ }
+
+ @Test
+ public void lightTheme_monochromeSpec() {
+ SchemeMonochrome scheme = new SchemeMonochrome(Hct.fromInt(0xff0000ff), false, 0.0);
+ assertThat(dynamicColors.primary().getHct(scheme).getTone()).isWithin(1).of(0);
+ assertThat(dynamicColors.onPrimary().getHct(scheme).getTone()).isWithin(1).of(90);
+ assertThat(dynamicColors.primaryContainer().getHct(scheme).getTone()).isWithin(1).of(25);
+ assertThat(dynamicColors.onPrimaryContainer().getHct(scheme).getTone()).isWithin(1).of(100);
+ assertThat(dynamicColors.secondary().getHct(scheme).getTone()).isWithin(1).of(40);
+ assertThat(dynamicColors.onSecondary().getHct(scheme).getTone()).isWithin(1).of(100);
+ assertThat(dynamicColors.secondaryContainer().getHct(scheme).getTone()).isWithin(1).of(85);
+ assertThat(dynamicColors.onSecondaryContainer().getHct(scheme).getTone()).isWithin(1).of(10);
+ assertThat(dynamicColors.tertiary().getHct(scheme).getTone()).isWithin(1).of(25);
+ assertThat(dynamicColors.onTertiary().getHct(scheme).getTone()).isWithin(1).of(90);
+ assertThat(dynamicColors.tertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(49);
+ assertThat(dynamicColors.onTertiaryContainer().getHct(scheme).getTone()).isWithin(1).of(100);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java
index 71e9f4d..4f3fc7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeNeutralTest.java
@@ -25,204 +25,193 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeNeutral;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeNeutralTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff767685);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff777680);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff75758B);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff787678);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff787678);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff737383);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff737383);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5d5d6c);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5d5d6c);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff21212e);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff21212e);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e1f3);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e1f3);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff414250);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff414250);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff636372);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff636372);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff1a1b27);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff1a1b27);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd9d8ea);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd9d8ea);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffcf8fa);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffcf8fa);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff737383);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff737383);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffc6c5d6);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffc6c5d6);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff454654);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff454654);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff454654);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff454654);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffcac9da);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffcac9da);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffb5b3c4);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffb5b3c4);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe2e1f3);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe2e1f3);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff373846);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff373846);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffb3b3cb);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffb3b3cb);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe1e0f9);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffe1e0f9);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff37374b);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff37374b);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeNeutral scheme = new SchemeNeutral(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131315);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131315);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java
index cc6f044..ece3f9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeRainbowTest.java
@@ -25,225 +25,229 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeRainbow;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeRainbowTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff696FC4);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff75758B);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff936B84);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff070707);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff676DC1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff676DC1);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5056A9);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5056A9);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff0F136A);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff0F136A);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xffE0E0FF);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xffE0E0FF);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xff34398B);
}
@Test
public void lightTheme_minContrast_tertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffffd8ee);
}
@Test
public void lightTheme_standardContrast_tertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffffd8ee);
}
@Test
public void lightTheme_maxContrast_tertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.tertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.tertiaryContainer().getArgb(scheme)).isSameColorAs(
0xff5A384E);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff565CB0);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff050865);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xffd6d6ff);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfff9f9f9);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfff9f9f9);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff676DC1);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff676DC1);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xff383E8F);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xff383E8F);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(
0xffc4c6ff);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xffa9afff);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xffe0e0ff);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(
0xff292f81);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffd5a8c3);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xffffd8ee);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(
0xff4f2e44);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeRainbow scheme = new SchemeRainbow(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131313);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131313);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java
index e4880d9..01d199b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeTonalSpotTest.java
@@ -25,348 +25,338 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeTonalSpot;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeTonalSpotTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff6E72AC);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff75758B);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff936B84);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff78767A);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff777680);
}
+
@Test
public void lightTheme_minContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff6c70aa);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff6a6fb1);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff555992);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff545999);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff181c51);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff161a59);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff3a3e74);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff383c7c);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff5C5F98);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff5a5fa0);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff11144B);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0e1253);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd6d6ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xffFCF8FD);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_minContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff605E62);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff5f5e65);
}
@Test
public void lightTheme_standardContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff1B1B1F);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff1b1b21);
}
@Test
public void lightTheme_maxContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xff1B1A1E);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xff1a1a20);
}
@Test
public void lightTheme_minContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffcfcfe7);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffcfcfe7);
}
@Test
public void lightTheme_standardContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffffffff);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void lightTheme_maxContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xffababc3);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xffababc3);
}
@Test
public void lightTheme_minContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xfff3c3df);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xfff3c3df);
}
@Test
public void lightTheme_standardContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xffffffff);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void lightTheme_maxContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xffcda0bb);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xffcda0bb);
}
@Test
public void lightTheme_minContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffffc2bb);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffffc2bb);
}
@Test
public void lightTheme_standardContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffffffff);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffffffff);
}
@Test
public void lightTheme_maxContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffff8d80);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffff8d80);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff6C70AA);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff6a6fb1);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff3E4278);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3c4180);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff3E4278);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff3c4180);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffc4c6ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffadb0ef);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffabaff7);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff30346A);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff2e3271);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd5a8c3);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffd5a8c3);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffffd8ee);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffffd8ee);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff4f2e44);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff4f2e44);
}
@Test
public void darkTheme_minContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xfffffbff);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xfffffbff);
}
@Test
public void darkTheme_standardContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xff2e2f42);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xff2e2f42);
}
@Test
public void darkTheme_maxContrast_onSecondary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onSecondary.getArgb(scheme)).isSameColorAs(0xff505165);
+ assertThat(dynamicColors.onSecondary().getArgb(scheme)).isSameColorAs(0xff505165);
}
@Test
public void darkTheme_minContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xfffffbff);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xfffffbff);
}
@Test
public void darkTheme_standardContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xff46263b);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xff46263b);
}
@Test
public void darkTheme_maxContrast_onTertiary() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiary.getArgb(scheme)).isSameColorAs(0xff6b485f);
+ assertThat(dynamicColors.onTertiary().getArgb(scheme)).isSameColorAs(0xff6b485f);
}
@Test
public void darkTheme_minContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xfffffbff);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xfffffbff);
}
@Test
public void darkTheme_standardContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xff690005);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xff690005);
}
@Test
public void darkTheme_maxContrast_onError() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onError.getArgb(scheme)).isSameColorAs(0xffa80710);
+ assertThat(dynamicColors.onError().getArgb(scheme)).isSameColorAs(0xffa80710);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff131316);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff131318);
}
@Test
public void darkTheme_minContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffa4a2a6);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffa4a2a9);
}
@Test
public void darkTheme_standardContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffe5e1e6);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffe4e1e9);
}
@Test
public void darkTheme_maxContrast_onSurface() {
SchemeTonalSpot scheme = new SchemeTonalSpot(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onSurface.getArgb(scheme)).isSameColorAs(0xffe6e2e7);
+ assertThat(dynamicColors.onSurface().getArgb(scheme)).isSameColorAs(0xffe5e2ea);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java
index e5963a2..0fb53eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/SchemeVibrantTest.java
@@ -25,204 +25,193 @@
import com.android.systemui.monet.hct.Hct;
import com.android.systemui.monet.scheme.SchemeVibrant;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Ignore("b/279581953")
@SmallTest
@RunWith(JUnit4.class)
public final class SchemeVibrantTest extends SysuiTestCase {
+ private final MaterialDynamicColors dynamicColors = new MaterialDynamicColors();
+
@Test
public void testKeyColors() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.primaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff080CFF);
- assertThat(MaterialDynamicColors.secondaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.secondaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff7B7296);
- assertThat(MaterialDynamicColors.tertiaryPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.tertiaryPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff886C9D);
- assertThat(MaterialDynamicColors.neutralPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff777682);
- assertThat(MaterialDynamicColors.neutralVariantPaletteKeyColor.getArgb(scheme))
+ assertThat(dynamicColors.neutralVariantPaletteKeyColor().getArgb(scheme))
.isSameColorAs(0xff767685);
}
@Test
public void lightTheme_minContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff);
}
@Test
public void lightTheme_standardContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff343dff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff343dff);
}
@Test
public void lightTheme_maxContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff000181);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff000181);
}
@Test
public void lightTheme_minContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void lightTheme_standardContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void lightTheme_maxContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000e3);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000e3);
}
@Test
public void lightTheme_minContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff3e47ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff3e47ff);
}
@Test
public void lightTheme_standardContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff00006e);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff00006e);
}
@Test
public void lightTheme_maxContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffd6d6ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffd6d6ff);
}
@Test
public void lightTheme_minContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_standardContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void lightTheme_maxContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), false, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xfffbf8ff);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xfffbf8ff);
}
@Test
public void darkTheme_minContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xff5660ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xff5660ff);
}
@Test
public void darkTheme_standardContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xffbec2ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xffbec2ff);
}
@Test
public void darkTheme_maxContrast_primary() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primary.getArgb(scheme)).isSameColorAs(0xfff6f4ff);
+ assertThat(dynamicColors.primary().getArgb(scheme)).isSameColorAs(0xfff6f4ff);
}
@Test
public void darkTheme_minContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000ef);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000ef);
}
@Test
public void darkTheme_standardContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0000ef);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xff0000ef);
}
@Test
public void darkTheme_maxContrast_primaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.primaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffc4c6ff);
+ assertThat(dynamicColors.primaryContainer().getArgb(scheme)).isSameColorAs(0xffc4c6ff);
}
@Test
public void darkTheme_minContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffa9afff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffa9afff);
}
@Test
public void darkTheme_standardContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffe0e0ff);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xffe0e0ff);
}
@Test
public void darkTheme_maxContrast_onPrimaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onPrimaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff0001c6);
+ assertThat(dynamicColors.onPrimaryContainer().getArgb(scheme)).isSameColorAs(0xff0001c6);
}
@Test
public void darkTheme_minContrast_onTertiaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xffc9a9df);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xffc9a9df);
}
@Test
public void darkTheme_standardContrast_onTertiaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xfff2daff);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xfff2daff);
}
@Test
public void darkTheme_maxContrast_onTertiaryContainer() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.onTertiaryContainer.getArgb(scheme)).isSameColorAs(
- 0xff472e5b);
+ assertThat(dynamicColors.onTertiaryContainer().getArgb(scheme)).isSameColorAs(0xff472e5b);
}
@Test
public void darkTheme_minContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, -1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
@Test
public void darkTheme_standardContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 0.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
@Test
public void darkTheme_maxContrast_surface() {
SchemeVibrant scheme = new SchemeVibrant(Hct.fromInt(0xff0000ff), true, 1.0);
- assertThat(MaterialDynamicColors.surface.getArgb(scheme)).isSameColorAs(0xff12131C);
+ assertThat(dynamicColors.surface().getArgb(scheme)).isSameColorAs(0xff12131a);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index d9428f8..611c5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -104,6 +104,9 @@
continueTouch(START_X + touchSlop.toFloat() + 1)
// Move again to cross the back trigger threshold
continueTouch(START_X + touchSlop + triggerThreshold + 1)
+ // Wait threshold duration and hold touch past trigger threshold
+ Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
+ continueTouch(START_X + touchSlop + triggerThreshold + 1)
assertThat(mBackPanelController.currentState)
.isEqualTo(BackPanelController.GestureState.ACTIVE)
@@ -114,14 +117,22 @@
finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
assertThat(mBackPanelController.currentState)
- .isEqualTo(BackPanelController.GestureState.FLUNG)
+ .isEqualTo(BackPanelController.GestureState.COMMITTED)
verify(backCallback).triggerBack()
}
@Test
fun handlesBackCancelled() {
startTouch()
+ // Move once to cross the touch slop
continueTouch(START_X + touchSlop.toFloat() + 1)
+ // Move again to cross the back trigger threshold
+ continueTouch(
+ START_X + touchSlop + triggerThreshold -
+ mBackPanelController.params.deactivationTriggerThreshold
+ )
+ // Wait threshold duration and hold touch before trigger threshold
+ Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
continueTouch(
START_X + touchSlop + triggerThreshold -
mBackPanelController.params.deactivationTriggerThreshold
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
new file mode 100644
index 0000000..36b913f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.notetask
+
+import android.content.Context
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class LaunchNotesRoleSettingsTrampolineActivityTest : SysuiTestCase() {
+
+ @Mock lateinit var noteTaskController: NoteTaskController
+
+ @Rule
+ @JvmField
+ val activityRule =
+ ActivityTestRule<LaunchNotesRoleSettingsTrampolineActivity>(
+ /* activityFactory= */ object :
+ SingleActivityFactory<LaunchNotesRoleSettingsTrampolineActivity>(
+ LaunchNotesRoleSettingsTrampolineActivity::class.java
+ ) {
+ override fun create(intent: Intent?) =
+ LaunchNotesRoleSettingsTrampolineActivity(noteTaskController)
+ },
+ /* initialTouchMode= */ false,
+ /* launchActivity= */ false,
+ )
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @After
+ fun tearDown() {
+ activityRule.finishActivity()
+ }
+
+ @Test
+ fun startActivity_noAction_shouldLaunchNotesRoleSettingTaskWithNullEntryPoint() {
+ activityRule.launchActivity(/* startIntent= */ null)
+
+ verify(noteTaskController).startNotesRoleSetting(any(Context::class.java), eq(null))
+ }
+
+ @Test
+ fun startActivity_quickAffordanceAction_shouldLaunchNotesRoleSettingTaskWithQuickAffordanceEntryPoint() { // ktlint-disable max-line-length
+ activityRule.launchActivity(Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE))
+
+ verify(noteTaskController)
+ .startNotesRoleSetting(any(Context::class.java), eq(QUICK_AFFORDANCE))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 55f221d..5f89705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -45,8 +45,11 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
+import com.android.systemui.notetask.NoteTaskController.Companion.SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
-import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
+import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity
import com.android.systemui.settings.FakeUserTracker
@@ -423,8 +426,8 @@
eq(COMPONENT_ENABLED_STATE_ENABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
}
@Test
@@ -438,8 +441,7 @@
eq(COMPONENT_ENABLED_STATE_DISABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
}
@Test
@@ -458,8 +460,7 @@
eq(COMPONENT_ENABLED_STATE_ENABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
}
@Test
@@ -479,8 +480,7 @@
eq(COMPONENT_ENABLED_STATE_DISABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
}
// endregion
@@ -496,7 +496,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -512,7 +512,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -528,7 +528,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
}
@@ -544,7 +544,7 @@
)
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
}
@@ -556,7 +556,7 @@
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(listOf(mainUserInfo), mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(mainUserInfo.userHandle)
}
@@ -566,7 +566,7 @@
whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
- createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
+ createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
verifyNoteTaskOpenInBubbleInUser(workUserInfo.userHandle)
}
@@ -664,8 +664,7 @@
eq(COMPONENT_ENABLED_STATE_ENABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(actualComponent.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(actualComponent.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
verify(shortcutManager, never()).disableShortcuts(any())
verify(shortcutManager).enableShortcuts(listOf(SHORTCUT_ID))
val actualShortcuts = argumentCaptor<List<ShortcutInfo>>()
@@ -696,8 +695,7 @@
eq(COMPONENT_ENABLED_STATE_DISABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
verify(shortcutManager).disableShortcuts(listOf(SHORTCUT_ID))
verify(shortcutManager, never()).enableShortcuts(any())
verify(shortcutManager, never()).updateShortcuts(any())
@@ -714,8 +712,7 @@
eq(COMPONENT_ENABLED_STATE_DISABLED),
eq(PackageManager.DONT_KILL_APP),
)
- assertThat(argument.value.className)
- .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ assertThat(argument.value).isEqualTo(SETTINGS_CREATE_NOTE_TASK_SHORTCUT_COMPONENT)
verify(shortcutManager).disableShortcuts(listOf(SHORTCUT_ID))
verify(shortcutManager, never()).enableShortcuts(any())
verify(shortcutManager, never()).updateShortcuts(any())
@@ -740,6 +737,129 @@
}
// endregion
+ // region getUserForHandlingNotesTaking
+ @Test
+ fun getUserForHandlingNotesTaking_cope_quickAffordance_shouldReturnWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+
+ assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_cope_tailButton_shouldReturnWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON)
+
+ assertThat(user).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_cope_appClip_shouldReturnCurrentUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_quickAffordance_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(QUICK_AFFORDANCE)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_tailButton_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(TAIL_BUTTON)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun getUserForHandlingNotesTaking_noManagement_appClip_shouldReturnCurrentUser() {
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ val user = createNoteTaskController().getUserForHandlingNotesTaking(APP_CLIPS)
+
+ assertThat(user).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+ // endregion
+
+ // startregion startNotesRoleSetting
+ @Test
+ fun startNotesRoleSetting_cope_quickAffordance_shouldStartNoteRoleIntentWithWorkProfileUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(workUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_cope_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() {
+ whenever(devicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile).thenReturn(true)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, entryPoint = null)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_noManagement_quickAffordance_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, QUICK_AFFORDANCE)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+
+ @Test
+ fun startNotesRoleSetting_noManagement_nullEntryPoint_shouldStartNoteRoleIntentWithCurrentUser() { // ktlint-disable max-line-length
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().startNotesRoleSetting(context, entryPoint = null)
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent).hasAction(Intent.ACTION_MANAGE_DEFAULT_APP)
+ }
+ assertThat(userCaptor.value).isEqualTo(UserHandle.of(mainUserInfo.id))
+ }
+ // endregion
+
private companion object {
const val NOTE_TASK_SHORT_LABEL = "Notetaking"
const val NOTE_TASK_ACTIVITY_NAME = "NoteTaskActivity"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index 42ef2b5..4526580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -18,7 +18,12 @@
package com.android.systemui.notetask.quickaffordance
+import android.app.role.RoleManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
import android.hardware.input.InputSettings
+import android.os.UserHandle
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
@@ -31,11 +36,18 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
+import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
+import com.android.systemui.notetask.NoteTaskInfoResolver
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR
import com.android.systemui.stylus.StylusManager
+import com.android.systemui.util.concurrency.FakeExecutor
+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.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -45,6 +57,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.verify
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
@@ -58,6 +71,8 @@
@Mock lateinit var stylusManager: StylusManager
@Mock lateinit var repository: KeyguardQuickAffordanceRepository
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var roleManager: RoleManager
+ @Mock lateinit var packageManager: PackageManager
private lateinit var mockitoSession: MockitoSession
@@ -69,6 +84,23 @@
.mockStatic(InputSettings::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
+
+ whenever(
+ packageManager.getApplicationInfoAsUser(
+ anyString(),
+ any(ApplicationInfoFlags::class.java),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(ApplicationInfo())
+ whenever(controller.getUserForHandlingNotesTaking(any())).thenReturn(UserHandle.SYSTEM)
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(listOf("com.google.test.notes"))
}
@After
@@ -85,6 +117,9 @@
keyguardMonitor = mock(),
lazyRepository = { repository },
isEnabled = isEnabled,
+ backgroundExecutor = FakeExecutor(FakeSystemClock()),
+ roleManager = roleManager,
+ noteTaskInfoResolver = NoteTaskInfoResolver(roleManager, packageManager)
)
private fun createLockScreenStateVisible(): LockScreenState =
@@ -112,6 +147,27 @@
}
@Test
+ fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() =
+ runTest {
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(emptyList())
+
+ val underTest = createUnderTest()
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Test
fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest {
TestConfig()
.setStylusEverUsed(false)
@@ -217,6 +273,39 @@
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
}
+ // region getPickerScreenState
+ @Test
+ fun getPickerScreenState_defaultNoteAppSet_shouldReturnDefault() = runTest {
+ val underTest = createUnderTest(isEnabled = true)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
+ }
+
+ @Test
+ fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest {
+ val underTest = createUnderTest(isEnabled = true)
+ whenever(
+ roleManager.getRoleHoldersAsUser(
+ eq(RoleManager.ROLE_NOTES),
+ any(UserHandle::class.java)
+ )
+ )
+ .thenReturn(emptyList())
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(
+ KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
+ listOf("Select a default notes app to use the notetaking shortcut"),
+ actionText = "Select app",
+ actionComponentName =
+ "${context.packageName}$COMPONENT_NAME_SEPARATOR" +
+ "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE"
+ )
+ )
+ }
+ // endregion
+
private inner class TestConfig {
fun setStylusEverUsed(value: Boolean) = also {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 88d7e9c..2dc78a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -587,14 +587,14 @@
assertEquals(addLink(mContext.getString(R.string.monitoring_description_two_named_vpns,
VPN_PACKAGE, VPN_PACKAGE_2)),
mFooterUtils.getVpnMessage(false, true, VPN_PACKAGE, VPN_PACKAGE_2));
- assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
- VPN_PACKAGE)),
+ assertEquals(addLink(mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE)),
mFooterUtils.getVpnMessage(true, false, VPN_PACKAGE, null));
assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
VPN_PACKAGE)),
mFooterUtils.getVpnMessage(false, false, VPN_PACKAGE, null));
- assertEquals(addLink(mContext.getString(R.string.monitoring_description_named_vpn,
- VPN_PACKAGE_2)),
+ assertEquals(addLink(mContext.getString(
+ R.string.monitoring_description_managed_device_named_vpn, VPN_PACKAGE_2)),
mFooterUtils.getVpnMessage(true, true, null, VPN_PACKAGE_2));
assertEquals(addLink(mContext.getString(
R.string.monitoring_description_managed_profile_named_vpn,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index a1d78cb..7c30843b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -94,6 +93,7 @@
doReturn(mContext.getUserId()).when(mRecordingService).getUserId();
doReturn(mContext.getPackageName()).when(mRecordingService).getPackageName();
doReturn(mContext.getContentResolver()).when(mRecordingService).getContentResolver();
+ doReturn(mContext.getResources()).when(mRecordingService).getResources();
// Mock notifications
doNothing().when(mRecordingService).createRecordingNotification();
@@ -101,7 +101,7 @@
doReturn(mNotification).when(mRecordingService).createSaveNotification(any());
doNothing().when(mRecordingService).createErrorNotification();
doNothing().when(mRecordingService).showErrorToast(anyInt());
- doNothing().when(mRecordingService).stopForeground(anyBoolean());
+ doNothing().when(mRecordingService).stopForeground(anyInt());
doNothing().when(mRecordingService).startForeground(anyInt(), any());
doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index f870631..9a8ec88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -115,6 +115,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -150,6 +151,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -157,6 +159,7 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -238,6 +241,7 @@
@Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
+ @Mock protected LightBarController mLightBarController;
@Mock protected NotificationStackScrollLayoutController
mNotificationStackScrollLayoutController;
@Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
@@ -304,6 +308,8 @@
mEmptySpaceClickListenerCaptor;
@Mock protected ActivityStarter mActivityStarter;
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ @Mock protected ShadeRepository mShadeRepository;
+ @Mock private CastController mCastController;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@@ -651,6 +657,7 @@
mNotificationRemoteInputManager,
mShadeExpansionStateManager,
mStatusBarKeyguardViewManager,
+ mLightBarController,
mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
@@ -672,7 +679,9 @@
mFeatureFlags,
mInteractionJankMonitor,
mShadeLog,
- mKeyguardFaceAuthInteractor
+ mKeyguardFaceAuthInteractor,
+ mShadeRepository,
+ mCastController
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 908f7cb..1cf3873 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -69,6 +69,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -82,12 +83,16 @@
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import dagger.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -98,8 +103,6 @@
import java.util.List;
-import dagger.Lazy;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -131,6 +134,7 @@
@Mock private PulseExpansionHandler mPulseExpansionHandler;
@Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private LightBarController mLightBarController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@@ -154,6 +158,7 @@
@Mock private ShadeLogger mShadeLogger;
@Mock private DumpManager mDumpManager;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private CastController mCastController;
private SysuiStatusBarStateController mStatusBarStateController;
@@ -220,6 +225,7 @@
mNotificationRemoteInputManager,
mShadeExpansionStateManager,
mStatusBarKeyguardViewManager,
+ mLightBarController,
mNotificationStackScrollLayoutController,
mLockscreenShadeTransitionController,
mNotificationShadeDepthController,
@@ -241,7 +247,9 @@
mFeatureFlags,
mInteractionJankMonitor,
mShadeLogger,
- mock(KeyguardFaceAuthInteractor.class)
+ mock(KeyguardFaceAuthInteractor.class),
+ mock(ShadeRepository.class),
+ mCastController
);
mFragmentListener = mQsController.getQsFragmentListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
new file mode 100644
index 0000000..8f2c93b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.shade.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.model.ShadeModel
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+class ShadeRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: ShadeRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = ShadeRepositoryImpl(shadeExpansionStateManager)
+ }
+
+ @Test
+ fun shadeExpansionChangeEvent() =
+ testScope.runTest {
+ var latest: ShadeModel? = null
+ val job = underTest.shadeModel.onEach { latest = it }.launchIn(this)
+ runCurrent()
+ assertThat(latest?.expansionAmount).isEqualTo(0f)
+ assertThat(latest?.isExpanded).isEqualTo(false)
+ assertThat(latest?.isUserDragging).isEqualTo(false)
+
+ val captor = withArgCaptor {
+ verify(shadeExpansionStateManager).addExpansionListener(capture())
+ }
+
+ captor.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(
+ fraction = 1f,
+ expanded = true,
+ tracking = false,
+ dragDownPxAmount = 0f,
+ )
+ )
+ runCurrent()
+ assertThat(latest?.expansionAmount).isEqualTo(1f)
+ assertThat(latest?.isExpanded).isEqualTo(true)
+ assertThat(latest?.isUserDragging).isEqualTo(false)
+
+ captor.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(
+ fraction = .67f,
+ expanded = false,
+ tracking = true,
+ dragDownPxAmount = 0f,
+ )
+ )
+ runCurrent()
+ assertThat(latest?.expansionAmount).isEqualTo(.67f)
+ assertThat(latest?.isExpanded).isEqualTo(false)
+ assertThat(latest?.isUserDragging).isEqualTo(true)
+
+ job.cancel()
+ }
+
+ @Test
+ fun updateQsExpansion() =
+ testScope.runTest {
+ assertThat(underTest.qsExpansion.value).isEqualTo(0f)
+
+ underTest.setQsExpansion(.5f)
+ assertThat(underTest.qsExpansion.value).isEqualTo(.5f)
+
+ underTest.setQsExpansion(.82f)
+ assertThat(underTest.qsExpansion.value).isEqualTo(.82f)
+
+ underTest.setQsExpansion(1f)
+ assertThat(underTest.qsExpansion.value).isEqualTo(1f)
+ }
+
+ @Test
+ fun updateUdfpsTransitionToFullShadeProgress() =
+ testScope.runTest {
+ assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
+
+ underTest.setUdfpsTransitionToFullShadeProgress(.5f)
+ assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(.5f)
+
+ underTest.setUdfpsTransitionToFullShadeProgress(.82f)
+ assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(.82f)
+
+ underTest.setUdfpsTransitionToFullShadeProgress(1f)
+ assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(1f)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 78f5bf2..eef4470 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -43,6 +43,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -172,7 +174,7 @@
}
@Test
- fun clockIdConflict_ErrorWithoutCrash() {
+ fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
@@ -199,6 +201,8 @@
assertEquals(registry.createExampleClock("clock_2"), mockClock)
assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
+ verify(mockPluginLifecycle1, never()).unloadPlugin()
+ verify(mockPluginLifecycle2, times(2)).unloadPlugin()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 2106da8..2351f760 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -15,6 +15,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -127,6 +128,7 @@
},
qsTransitionControllerFactory = { qsTransitionController },
activityStarter = activityStarter,
+ shadeRepository = FakeShadeRepository(),
)
transitionController.addCallback(transitionControllerCallback)
whenever(nsslController.view).thenReturn(stackscroller)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index beaf300..c49f179 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -356,6 +356,7 @@
@Test
fun ignoreShadeBlurUntilHidden_schedulesFrame() {
notificationShadeDepthController.blursDisabledForAppLaunch = true
+ verify(blurUtils).prepareBlur(any(), anyInt())
verify(choreographer)
.postFrameCallback(eq(notificationShadeDepthController.updateBlurCallback))
}
@@ -419,6 +420,7 @@
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
verify(wallpaperController).setNotificationShadeZoom(eq(1f))
+ verify(blurUtils).prepareBlur(any(), eq(0))
verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 9186c35..4c83194 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -617,24 +617,9 @@
}
@Test
- public void applyRoundnessAndInv_should_be_immediately_applied_on_childrenContainer_legacy()
- throws Exception {
- ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
- group.useRoundnessSourceTypes(false);
- Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f);
- Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
-
- group.requestBottomRoundness(1f, SourceType.from(""), false);
-
- Assert.assertEquals(1f, group.getBottomRoundness(), 0.001f);
- Assert.assertEquals(1f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
- }
-
- @Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_childrenContainer()
throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
- group.useRoundnessSourceTypes(true);
Assert.assertEquals(0f, group.getBottomRoundness(), 0.001f);
Assert.assertEquals(0f, group.getChildrenContainer().getBottomRoundness(), 0.001f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index e41929f..be976a1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -158,55 +157,7 @@
}
@Test
- public void addNotification_shouldResetOnScrollRoundness() throws Exception {
- ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.OnScroll);
-
- mChildrenContainer.addNotification(row, 0);
-
- Assert.assertEquals(0f, row.getTopRoundness(), /* delta = */ 0f);
- Assert.assertEquals(0f, row.getBottomRoundness(), /* delta = */ 0f);
- }
-
- @Test
- public void addNotification_shouldNotResetOtherRoundness() throws Exception {
- ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.DefaultValue);
- ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.OnDismissAnimation);
-
- mChildrenContainer.addNotification(row1, 0);
- mChildrenContainer.addNotification(row2, 0);
-
- Assert.assertEquals(1f, row1.getTopRoundness(), /* delta = */ 0f);
- Assert.assertEquals(1f, row1.getBottomRoundness(), /* delta = */ 0f);
- Assert.assertEquals(1f, row2.getTopRoundness(), /* delta = */ 0f);
- Assert.assertEquals(1f, row2.getBottomRoundness(), /* delta = */ 0f);
- }
-
- @Test
- public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child_legacy() {
- mChildrenContainer.useRoundnessSourceTypes(false);
- List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren();
- ExpandableNotificationRow notificationRow = children.get(children.size() - 1);
- Assert.assertEquals(0f, mChildrenContainer.getBottomRoundness(), 0.001f);
- Assert.assertEquals(0f, notificationRow.getBottomRoundness(), 0.001f);
-
- mChildrenContainer.requestBottomRoundness(1f, SourceType.from(""), false);
-
- Assert.assertEquals(1f, mChildrenContainer.getBottomRoundness(), 0.001f);
- Assert.assertEquals(1f, notificationRow.getBottomRoundness(), 0.001f);
- }
-
- @Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_last_child() {
- mChildrenContainer.useRoundnessSourceTypes(true);
List<ExpandableNotificationRow> children = mChildrenContainer.getAttachedChildren();
ExpandableNotificationRow notificationRow = children.get(children.size() - 1);
Assert.assertEquals(0f, mChildrenContainer.getBottomRoundness(), 0.001f);
@@ -220,8 +171,6 @@
@Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_header() {
- mChildrenContainer.useRoundnessSourceTypes(true);
-
NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper();
Assert.assertEquals(0f, header.getTopRoundness(), 0.001f);
@@ -232,7 +181,6 @@
@Test
public void applyRoundnessAndInvalidate_should_be_immediately_applied_on_headerLowPriority() {
- mChildrenContainer.useRoundnessSourceTypes(true);
mChildrenContainer.setIsLowPriority(true);
NotificationHeaderViewWrapper header = mChildrenContainer.getNotificationHeaderWrapper();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
deleted file mode 100644
index bd0a556..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.util.DeviceConfigProxy;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.util.HashSet;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class NotificationRoundnessManagerTest extends SysuiTestCase {
-
- private NotificationRoundnessManager mRoundnessManager;
- private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
- private Runnable mRoundnessCallback = mock(Runnable.class);
- private ExpandableNotificationRow mFirst;
- private ExpandableNotificationRow mSecond;
- private NotificationRoundnessLogger mLogger = mock(NotificationRoundnessLogger.class);
- private float mSmallRadiusRatio;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- final Resources resources = mContext.getResources();
- mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
- / resources.getDimension(R.dimen.notification_corner_radius);
- mRoundnessManager = new NotificationRoundnessManager(
- new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
- mLogger,
- mock(DumpManager.class),
- mock(FeatureFlags.class));
- allowTestableLooperAsMainThread();
- NotificationTestHelper testHelper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- mFirst = testHelper.createRow();
- mFirst.setHeadsUpAnimatingAwayListener(animatingAway
- -> mRoundnessManager.updateView(mFirst, false));
- mSecond = testHelper.createRow();
- mSecond.setHeadsUpAnimatingAwayListener(animatingAway
- -> mRoundnessManager.updateView(mSecond, false));
- mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
- mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(null, null)
- });
- mRoundnessManager.setExpanded(1.0f, 1.0f);
- mRoundnessManager.setShouldRoundPulsingViews(true);
- reset(mRoundnessCallback);
- }
-
- @Test
- public void testCallbackCalledWhenSecondChanged() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mSecond),
- createSection(null, null)
- });
- verify(mRoundnessCallback, atLeast(1)).run();
- }
-
- @Test
- public void testCallbackCalledWhenFirstChanged() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mFirst),
- createSection(null, null)
- });
- verify(mRoundnessCallback, atLeast(1)).run();
- }
-
- @Test
- public void testCallbackCalledWhenSecondSectionFirstChanged() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(mSecond, null)
- });
- verify(mRoundnessCallback, atLeast(1)).run();
- }
-
- @Test
- public void testCallbackCalledWhenSecondSectionLastChanged() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(null, mSecond)
- });
- verify(mRoundnessCallback, atLeast(1)).run();
- }
-
- @Test
- public void testCallbackNotCalledWhenFirstChangesSections() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(null, mFirst),
- createSection(mFirst, null)
- });
- verify(mRoundnessCallback, never()).run();
- }
-
- @Test
- public void testRoundnessSetOnLast() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(1.0f, mSecond.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, mSecond.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundnessPulsing() throws Exception {
- // Let's create a notification that's neither the first or last item of the stack,
- // this way we'll ensure that it won't have any rounded corners by default.
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mSecond),
- createSection(null, null)
- });
- NotificationTestHelper testHelper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- ExpandableNotificationRow row = testHelper.createRow();
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.getRow()).thenReturn(row);
-
- when(testHelper.getStatusBarStateController().isDozing()).thenReturn(true);
- row.setHeadsUp(true);
- mRoundnessManager.updateView(entry.getRow(), false);
- Assert.assertEquals(1f, row.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1f, row.getTopRoundness(), 0.0f);
-
- row.setHeadsUp(false);
- mRoundnessManager.updateView(entry.getRow(), false);
- Assert.assertEquals(mSmallRadiusRatio, row.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, row.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundnessSetOnSecondSectionLast() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(null, mSecond)
- });
- Assert.assertEquals(1.0f, mSecond.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, mSecond.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundnessSetOnSecondSectionFirst() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(mSecond, null)
- });
- Assert.assertEquals(mSmallRadiusRatio, mSecond.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mSecond.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundnessSetOnNew() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, null),
- createSection(null, null)
- });
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testCompleteReplacement() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testNotCalledWhenRemoved() {
- mFirst.setRemoved();
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(1.0f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundedWhenPinnedAndCollapsed() {
- mFirst.setPinned(true);
- mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(1.0f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundedWhenGoingAwayAndCollapsed() {
- mFirst.setHeadsUpAnimatingAway(true);
- mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(1.0f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundedNormalRoundingWhenExpanded() {
- mFirst.setHeadsUpAnimatingAway(true);
- mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testTrackingHeadsUpRoundedIfPushingUp() {
- mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
- mRoundnessManager.setTrackingHeadsUp(mFirst);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(1.0f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testTrackingHeadsUpPartiallyRoundedIfPushingDown() {
- mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
- mRoundnessManager.setTrackingHeadsUp(mFirst);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- Assert.assertEquals(0.5f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(0.5f, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testRoundingUpdatedWhenAnimatingAwayTrue() {
- mRoundnessManager.setExpanded(0.0f, 0.0f);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- mFirst.setHeadsUpAnimatingAway(true);
- Assert.assertEquals(1.0f, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(1.0f, mFirst.getTopRoundness(), 0.0f);
- }
-
-
- @Test
- public void testRoundingUpdatedWhenAnimatingAwayFalse() {
- mRoundnessManager.setExpanded(0.0f, 0.0f);
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mSecond, mSecond),
- createSection(null, null)
- });
- mFirst.setHeadsUpAnimatingAway(true);
- mFirst.setHeadsUpAnimatingAway(false);
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getBottomRoundness(), 0.0f);
- Assert.assertEquals(mSmallRadiusRatio, mFirst.getTopRoundness(), 0.0f);
- }
-
- @Test
- public void testNoViewsFirstOrLastInSectionWhenSecondSectionEmpty() {
- Assert.assertTrue(mFirst.isFirstInSection());
- Assert.assertTrue(mFirst.isLastInSection());
- }
-
- @Test
- public void testNoViewsFirstOrLastInSectionWhenFirstSectionEmpty() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(null, null),
- createSection(mSecond, mSecond)
- });
- Assert.assertTrue(mSecond.isFirstInSection());
- Assert.assertTrue(mSecond.isLastInSection());
- }
-
- @Test
- public void testFirstAndLastViewsInSectionSetWhenBothSectionsNonEmpty() {
- mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
- createSection(mFirst, mFirst),
- createSection(mSecond, mSecond)
- });
- Assert.assertTrue(mFirst.isFirstInSection());
- Assert.assertTrue(mFirst.isLastInSection());
- Assert.assertTrue(mSecond.isFirstInSection());
- Assert.assertTrue(mSecond.isLastInSection());
- }
-
- @Test
- public void testLoggingOnRoundingUpdate() {
- NotificationSection[] sections = new NotificationSection[]{
- createSection(mFirst, mSecond),
- createSection(null, null)
- };
- mRoundnessManager.updateRoundedChildren(sections);
- verify(mLogger).onSectionCornersUpdated(sections, /*anyChanged=*/ true);
- verify(mLogger, atLeast(1)).onCornersUpdated(eq(mFirst), anyBoolean(),
- anyBoolean(), anyBoolean(), anyBoolean());
- verify(mLogger, atLeast(1)).onCornersUpdated(eq(mSecond), anyBoolean(),
- anyBoolean(), anyBoolean(), anyBoolean());
- }
-
- private NotificationSection createSection(ExpandableNotificationRow first,
- ExpandableNotificationRow last) {
- NotificationSection section = mock(NotificationSection.class);
- when(section.getFirstVisibleChild()).thenReturn(first);
- when(section.getLastVisibleChild()).thenReturn(last);
- return section;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 30da08e..f38881c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -96,8 +96,7 @@
mIncomingHeaderController,
mPeopleHeaderController,
mAlertingHeaderController,
- mSilentHeaderController,
- mFeatureFlag
+ mSilentHeaderController
);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index b1d3daa..05b70eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -12,7 +12,6 @@
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.LegacySourceType
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
@@ -359,39 +358,6 @@
)
}
- @Test
- fun resetOnScrollRoundness_shouldSetOnScrollTo0() {
- val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.OnScroll)
-
- NotificationShelf.resetLegacyOnScrollRoundness(row)
-
- assertEquals(0f, row.topRoundness)
- assertEquals(0f, row.bottomRoundness)
- }
-
- @Test
- fun resetOnScrollRoundness_shouldNotResetOtherRoundness() {
- val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.DefaultValue)
- val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
- /* topRoundness = */ 1f,
- /* bottomRoundness = */ 1f,
- /* sourceType = */ LegacySourceType.OnDismissAnimation)
-
- NotificationShelf.resetLegacyOnScrollRoundness(row1)
- NotificationShelf.resetLegacyOnScrollRoundness(row2)
-
- assertEquals(1f, row1.topRoundness)
- assertEquals(1f, row1.bottomRoundness)
- assertEquals(1f, row2.topRoundness)
- assertEquals(1f, row2.bottomRoundness)
- }
-
private fun setFractionToShade(fraction: Float) {
whenever(ambientState.fractionToShade).thenReturn(fraction)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 81a3f12..45725ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -6,7 +6,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -30,14 +29,7 @@
NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
}
- private fun notificationTargetsHelper(
- notificationGroupCorner: Boolean = true,
- ) =
- NotificationTargetsHelper(
- FakeFeatureFlags().apply {
- set(Flags.USE_ROUNDNESS_SOURCETYPES, notificationGroupCorner)
- }
- )
+ private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
@Test
fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 205cebd..2f1e372 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -34,8 +34,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -82,7 +80,6 @@
private KeyguardStateController mKeyguardStateController;
private CommandQueue mCommandQueue;
private NotificationRoundnessManager mNotificationRoundnessManager;
- private FeatureFlags mFeatureFlag;
@Before
public void setUp() throws Exception {
@@ -103,9 +100,7 @@
mKeyguardStateController = mock(KeyguardStateController.class);
mCommandQueue = mock(CommandQueue.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- mFeatureFlag = mock(FeatureFlags.class);
when(mShadeViewController.getShadeHeadsUpTracker()).thenReturn(mShadeHeadsUpTracker);
- when(mFeatureFlag.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)).thenReturn(true);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
@@ -118,7 +113,6 @@
mStackScrollerController,
mShadeViewController,
mNotificationRoundnessManager,
- mFeatureFlag,
mHeadsUpStatusBarView,
new Clock(mContext, null),
Optional.of(mOperatorNameView));
@@ -202,7 +196,6 @@
mStackScrollerController,
mShadeViewController,
mNotificationRoundnessManager,
- mFeatureFlag,
mHeadsUpStatusBarView,
new Clock(mContext, null),
Optional.empty());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 529519a..a501556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -22,21 +22,31 @@
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Color;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.ColorInt;
import androidx.test.filters.SmallTest;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.util.ContrastColorUtil;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -53,13 +63,25 @@
@TestableLooper.RunWithLooper
public class LightBarControllerTest extends SysuiTestCase {
+ private static final GradientColors COLORS_LIGHT = makeColors(Color.WHITE);
+ private static final GradientColors COLORS_DARK = makeColors(Color.BLACK);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private LightBarTransitionsController mLightBarTransitionsController;
+ private LightBarTransitionsController mNavBarController;
private SysuiDarkIconDispatcher mStatusBarIconController;
private LightBarController mLightBarController;
+ /** Allow testing with NEW_LIGHT_BAR_LOGIC flag in different states */
+ protected boolean testNewLightBarLogic() {
+ return false;
+ }
+
@Before
public void setup() {
+ mFeatureFlags.set(Flags.NEW_LIGHT_BAR_LOGIC, testNewLightBarLogic());
mStatusBarIconController = mock(SysuiDarkIconDispatcher.class);
+ mNavBarController = mock(LightBarTransitionsController.class);
+ when(mNavBarController.supportsIconTintForNavMode(anyInt())).thenReturn(true);
mLightBarTransitionsController = mock(LightBarTransitionsController.class);
when(mStatusBarIconController.getTransitionsController()).thenReturn(
mLightBarTransitionsController);
@@ -68,10 +90,19 @@
mStatusBarIconController,
mock(BatteryController.class),
mock(NavigationModeController.class),
+ mFeatureFlags,
mock(DumpManager.class),
new FakeDisplayTracker(mContext));
}
+ private static GradientColors makeColors(@ColorInt int bgColor) {
+ GradientColors colors = new GradientColors();
+ colors.setMainColor(bgColor);
+ colors.setSecondaryColor(bgColor);
+ colors.setSupportsDarkText(!ContrastColorUtil.isColorDark(bgColor));
+ return colors;
+ }
+
@Test
public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() {
final Rect firstBounds = new Rect(0, 0, 1, 1);
@@ -177,4 +208,54 @@
false /* navbarColorManagedByIme */);
verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
}
+
+ @Test
+ public void validateNavBarChangesUpdateIcons() {
+ assumeTrue(testNewLightBarLogic()); // Only run in the new suite
+
+ // On the launcher in dark mode buttons are light
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK);
+ mLightBarController.onNavigationBarAppearanceChanged(
+ 0, /* nbModeChanged = */ true,
+ MODE_TRANSPARENT, /* navbarColorManagedByIme = */ false);
+ verifyNavBarIconsUnchanged(); // no changes yet; not attached
+
+ // Initial state is set when controller is set
+ mLightBarController.setNavigationBar(mNavBarController);
+ verifyNavBarIconsDarkSetTo(false);
+
+ // Changing the color of the transparent scrim has no effect
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_LIGHT);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // Showing the notification shade with white scrim requires dark icons
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_LIGHT);
+ verifyNavBarIconsDarkSetTo(true);
+
+ // Expanded QS always provides a black background, so icons become light again
+ mLightBarController.setQsExpanded(true);
+ verifyNavBarIconsDarkSetTo(false);
+
+ // Tapping the QS tile to change to dark theme has no effect in this state
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 1f, COLORS_DARK);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // collapsing QS in dark mode doesn't affect button color
+ mLightBarController.setQsExpanded(false);
+ verifyNavBarIconsUnchanged(); // still light
+
+ // Closing the shade has no affect
+ mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK);
+ verifyNavBarIconsUnchanged(); // still light
+ }
+
+ private void verifyNavBarIconsUnchanged() {
+ verify(mNavBarController, never()).setIconsDark(anyBoolean(), anyBoolean());
+ }
+
+ private void verifyNavBarIconsDarkSetTo(boolean iconsDark) {
+ verify(mNavBarController).setIconsDark(eq(iconsDark), anyBoolean());
+ verify(mNavBarController, never()).setIconsDark(eq(!iconsDark), anyBoolean());
+ clearInvocations(mNavBarController);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt
new file mode 100644
index 0000000..d9c2cfa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.flags.Flags.NEW_LIGHT_BAR_LOGIC
+
+/**
+ * This file only needs to live as long as [NEW_LIGHT_BAR_LOGIC] does. When we delete that flag, we
+ * can roll this back into the old test.
+ */
+@SmallTest
+class LightBarControllerWithNewLogicTest : LightBarControllerTest() {
+ override fun testNewLightBarLogic(): Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index b80b825..c282c1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -21,6 +21,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
+import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -49,7 +51,7 @@
fun calculateWidthFor_oneIcon_widthForOneIcon() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f),
/* actual= */ 30f)
@@ -59,7 +61,7 @@
fun calculateWidthFor_fourIcons_widthForFourIcons() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f),
/* actual= */ 60f)
@@ -69,7 +71,7 @@
fun calculateWidthFor_fiveIcons_widthForFourIcons() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f),
/* actual= */ 60f)
}
@@ -78,7 +80,7 @@
fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val icon = mockStatusBarIcon()
iconContainer.addView(icon)
@@ -99,7 +101,7 @@
fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val iconOne = mockStatusBarIcon()
val iconTwo = mockStatusBarIcon()
@@ -128,7 +130,7 @@
fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() {
iconContainer.setActualPaddingStart(10f)
iconContainer.setActualPaddingEnd(10f)
- iconContainer.setIconSize(10);
+ iconContainer.setIconSize(10)
val iconOne = mockStatusBarIcon()
val iconTwo = mockStatusBarIcon()
@@ -154,6 +156,55 @@
}
@Test
+ fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() {
+ iconContainer.setActualPaddingStart(0f)
+ iconContainer.setActualPaddingEnd(0f)
+ iconContainer.setActualLayoutWidth(30)
+ iconContainer.setIconSize(10)
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ assertEquals(3, iconContainer.childCount)
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation)
+ assertFalse(iconContainer.areIconsOverflowing())
+ }
+
+ @Test
+ fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() {
+ iconContainer.setActualPaddingStart(0f)
+ iconContainer.setActualPaddingEnd(0f)
+ iconContainer.setActualLayoutWidth(35)
+ iconContainer.setIconSize(10)
+
+ val iconOne = mockStatusBarIcon()
+ val iconTwo = mockStatusBarIcon()
+ val iconThree = mockStatusBarIcon()
+ val iconFour = mockStatusBarIcon()
+
+ iconContainer.addView(iconOne)
+ iconContainer.addView(iconTwo)
+ iconContainer.addView(iconThree)
+ iconContainer.addView(iconFour)
+ assertEquals(4, iconContainer.childCount)
+
+ iconContainer.calculateIconXTranslations()
+ assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation)
+ assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation)
+ assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState)
+ assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState)
+ assertTrue(iconContainer.areIconsOverflowing())
+ }
+
+ @Test
fun shouldForceOverflow_appearingAboveSpeedBump_true() {
val forceOverflow = iconContainer.shouldForceOverflow(
/* i= */ 1,
@@ -161,7 +212,7 @@
/* iconAppearAmount= */ 1f,
/* maxVisibleIcons= */ 5
)
- assertTrue(forceOverflow);
+ assertTrue(forceOverflow)
}
@Test
@@ -172,7 +223,7 @@
/* iconAppearAmount= */ 0f,
/* maxVisibleIcons= */ 5
)
- assertTrue(forceOverflow);
+ assertTrue(forceOverflow)
}
@Test
@@ -183,7 +234,7 @@
/* iconAppearAmount= */ 0f,
/* maxVisibleIcons= */ 5
)
- assertFalse(forceOverflow);
+ assertFalse(forceOverflow)
}
@Test
@@ -210,6 +261,17 @@
}
@Test
+ fun isOverflowing_lastChildXGreaterThanDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 9f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
val isOverflowing = iconContainer.isOverflowing(
/* isLastChild= */ true,
@@ -253,7 +315,7 @@
assertTrue(isOverflowing)
}
- private fun mockStatusBarIcon() : StatusBarIconView {
+ private fun mockStatusBarIcon(): StatusBarIconView {
val iconView = mock(StatusBarIconView::class.java)
whenever(iconView.width).thenReturn(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6be0e2d..e56f0d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -84,6 +84,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.google.common.truth.Truth;
@@ -151,13 +152,15 @@
private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Captor
private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
- when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
+ when(mKeyguardMessageAreaFactory.create(any()))
.thenReturn(mKeyguardMessageAreaController);
when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
@@ -909,4 +912,26 @@
// THEN the alternateBouncer doesn't hide
verify(mAlternateBouncerInteractor, never()).hide();
}
+
+ @Test
+ public void onDeviceUnlocked_hideAlternateBouncerAndClearMessageArea() {
+ reset(mKeyguardUpdateMonitor);
+ reset(mKeyguardMessageAreaController);
+
+ // GIVEN keyguard state controller callback is registered
+ verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
+
+ // GIVEN alternate bouncer state = not visible
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
+
+ // WHEN the device is unlocked
+ mKeyguardStateControllerCallback.getValue().onUnlockedChanged();
+
+ // THEN the false visibility state is propagated to the keyguardUpdateMonitor
+ verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false));
+
+ // THEN message area visibility updated to FALSE with empty message
+ verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
+ verify(mKeyguardMessageAreaController).setMessage(eq(""));
+ }
}
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 3ec9690..bde05b9 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
@@ -53,6 +53,7 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -92,13 +93,15 @@
private val mobileMappings = FakeMobileMappingsProxy()
private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
+ private val testDispatcher = UnconfinedTestDispatcher()
private val scope = CoroutineScope(IMMEDIATE)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- logFactory = TableLogBufferFactory(dumpManager, FakeSystemClock())
+ logFactory =
+ TableLogBufferFactory(dumpManager, FakeSystemClock(), mock(), testDispatcher, scope)
// Never start in demo mode
whenever(demoModeController.isInDemoMode).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 37fac34..7573b28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -61,11 +61,18 @@
internal class DemoMobileConnectionParameterizedTest(private val testCase: TestCase) :
SysuiTestCase() {
- private val logFactory = TableLogBufferFactory(mock(), FakeSystemClock())
-
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
+ private val logFactory =
+ TableLogBufferFactory(
+ mock(),
+ FakeSystemClock(),
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
+
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 1251dfa..efaf152 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -54,13 +54,20 @@
@SmallTest
class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
private val dumpManager: DumpManager = mock()
- private val logFactory = TableLogBufferFactory(dumpManager, FakeSystemClock())
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
+ private val logFactory =
+ TableLogBufferFactory(
+ dumpManager,
+ FakeSystemClock(),
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
private lateinit var underTest: DemoMobileConnectionsRepository
private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 9f77744..b701fbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -66,7 +66,15 @@
private val systemClock = FakeSystemClock()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
- private val tableLogBuffer = TableLogBuffer(maxSize = 100, name = "TestName", systemClock)
+ private val tableLogBuffer =
+ TableLogBuffer(
+ maxSize = 100,
+ name = "TestName",
+ systemClock,
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
@@ -294,7 +302,14 @@
@Test
fun factory_reusesLogBuffersForSameConnection() =
testScope.runTest {
- val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+ val realLoggerFactory =
+ TableLogBufferFactory(
+ mock(),
+ FakeSystemClock(),
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
val factory =
FullMobileConnectionRepository.Factory(
@@ -329,7 +344,14 @@
@Test
fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
testScope.runTest {
- val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
+ val realLoggerFactory =
+ TableLogBufferFactory(
+ mock(),
+ FakeSystemClock(),
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
val factory =
FullMobileConnectionRepository.Factory(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index c84c9c0..6e1ab58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -61,6 +61,16 @@
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
+ private val tableLogBuffer =
+ TableLogBuffer(
+ 8,
+ "MobileIconsInteractorTest",
+ FakeSystemClock(),
+ mock(),
+ testDispatcher,
+ testScope.backgroundScope,
+ )
+
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
@Before
@@ -687,16 +697,14 @@
}
companion object {
- private val tableLogBuffer =
- TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
private const val SUB_1_ID = 1
private val SUB_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
- private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer)
+ private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID, mock())
private const val SUB_2_ID = 2
private val SUB_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
- private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
+ private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, mock())
private const val SUB_3_ID = 3
private val SUB_3_OPP =
@@ -705,7 +713,7 @@
isOpportunistic = true,
groupUuid = ParcelUuid(UUID.randomUUID()),
)
- private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
+ private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, mock())
private const val SUB_4_ID = 4
private val SUB_4_OPP =
@@ -714,6 +722,6 @@
isOpportunistic = true,
groupUuid = ParcelUuid(UUID.randomUUID()),
)
- private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
+ private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, mock())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index db50163..b8e4306 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.policy;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -125,4 +126,20 @@
fail("Concurrent modification exception");
}
}
+
+ @Test
+ public void hasConnectedCastDevice_connected() {
+ CastController.CastDevice castDevice = new CastController.CastDevice();
+ castDevice.state = CastController.CastDevice.STATE_CONNECTED;
+ mController.startCasting(castDevice);
+ assertTrue(mController.hasConnectedCastDevice());
+ }
+
+ @Test
+ public void hasConnectedCastDevice_notConnected() {
+ CastController.CastDevice castDevice = new CastController.CastDevice();
+ castDevice.state = CastController.CastDevice.STATE_CONNECTING;
+ mController.startCasting(castDevice);
+ assertTrue(mController.hasConnectedCastDevice());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 1510ee8..47a86b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -380,7 +380,7 @@
mPositioner,
mock(DisplayController.class),
mOneHandedOptional,
- mock(DragAndDropController.class),
+ Optional.of(mock(DragAndDropController.class)),
syncExecutor,
mock(Handler.class),
mTaskViewTransitions,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index c3bb771..14c3f3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -69,7 +69,7 @@
BubblePositioner positioner,
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
- DragAndDropController dragAndDropController,
+ Optional<DragAndDropController> dragAndDropController,
ShellExecutor shellMainExecutor,
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
new file mode 100644
index 0000000..d9012a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.biometrics.data.repository
+
+import android.hardware.biometrics.SensorLocationInternal
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
+
+ private val _isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isInitialized = _isInitialized.asStateFlow()
+
+ private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
+ override val sensorId: StateFlow<Int> = _sensorId.asStateFlow()
+
+ private val _strength: MutableStateFlow<SensorStrength> =
+ MutableStateFlow(SensorStrength.CONVENIENCE)
+ override val strength = _strength.asStateFlow()
+
+ private val _sensorType: MutableStateFlow<FingerprintSensorType> =
+ MutableStateFlow(FingerprintSensorType.UNKNOWN)
+ override val sensorType: StateFlow<FingerprintSensorType> = _sensorType.asStateFlow()
+
+ private val _sensorLocation: MutableStateFlow<SensorLocationInternal> =
+ MutableStateFlow(SensorLocationInternal.DEFAULT)
+ override val sensorLocation = _sensorLocation.asStateFlow()
+
+ fun setProperties(
+ sensorId: Int,
+ strength: SensorStrength,
+ sensorType: FingerprintSensorType,
+ sensorLocation: SensorLocationInternal
+ ) {
+ _sensorId.value = sensorId
+ _strength.value = strength
+ _sensorType.value = sensorType
+ _sensorLocation.value = sensorLocation
+ _isInitialized.value = true
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
new file mode 100644
index 0000000..fd91391
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeRearDisplayStateRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeRearDisplayStateRepository : RearDisplayStateRepository {
+ private val _isInRearDisplayMode = MutableStateFlow<Boolean>(false)
+ override val isInRearDisplayMode: StateFlow<Boolean> = _isInRearDisplayMode.asStateFlow()
+
+ fun setIsInRearDisplayMode(isInRearDisplayMode: Boolean) {
+ _isInRearDisplayMode.value = isInRearDisplayMode
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 2c0a8fd..492e542 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -21,13 +21,27 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-/** Fake implementation of [KeyguardRepository] */
+/** Fake implementation of [ShadeRepository] */
class FakeShadeRepository : ShadeRepository {
private val _shadeModel = MutableStateFlow(ShadeModel())
override val shadeModel: Flow<ShadeModel> = _shadeModel
+ private val _qsExpansion = MutableStateFlow(0f)
+ override val qsExpansion = _qsExpansion
+
+ private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
+ override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}
+
+ override fun setQsExpansion(qsExpansion: Float) {
+ _qsExpansion.value = qsExpansion
+ }
+
+ override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
+ _udfpsTransitionToFullShadeProgress.value = progress
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
index f6b24da..84ace7c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -51,4 +51,9 @@
public void stopCasting(CastDevice device) {
}
+
+ @Override
+ public boolean hasConnectedCastDevice() {
+ return false;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a3b4a0f..d36c959 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1012,8 +1012,7 @@
+ Binder.getCallingPid() + " for device id " + deviceId
+ " with package names " + Arrays.toString(client.mPackageNames));
}
- return IntPair.of(mProxyManager.getStateLocked(deviceId,
- mUiAutomationManager.isUiAutomationRunningLocked()),
+ return IntPair.of(mProxyManager.getStateLocked(deviceId),
client.mLastSentRelevantEventTypes);
}
mGlobalClients.register(callback, client);
@@ -1028,8 +1027,7 @@
+ Binder.getCallingPid() + " for device id " + deviceId
+ " with package names " + Arrays.toString(client.mPackageNames));
}
- return IntPair.of(mProxyManager.getStateLocked(deviceId,
- mUiAutomationManager.isUiAutomationRunningLocked()),
+ return IntPair.of(mProxyManager.getStateLocked(deviceId),
client.mLastSentRelevantEventTypes);
}
userState.mUserClients.register(callback, client);
@@ -4024,9 +4022,8 @@
final long identity = Binder.clearCallingIdentity();
try {
- mProxyManager.registerProxy(client, displayId, mContext,
- sIdCounter++, mMainHandler, mSecurityPolicy, this, getTraceManager(),
- mWindowManagerService);
+ mProxyManager.registerProxy(client, displayId, sIdCounter++, mSecurityPolicy,
+ this, getTraceManager(), mWindowManagerService);
synchronized (mLock) {
notifyClearAccessibilityCacheLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index d417197..6dc8fb3 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -61,7 +61,6 @@
* proxy connection will belong to a separate user state.
*
* TODO(241117292): Remove or cut down during simultaneous user refactoring.
- * TODO(262244375): Add unit tests.
*/
public class ProxyManager {
private static final boolean DEBUG = false;
@@ -128,7 +127,7 @@
RemoteCallbackList<IAccessibilityManagerClient> getCurrentUserClientsLocked();
}
- ProxyManager(Object lock, AccessibilityWindowManager awm,
+ public ProxyManager(Object lock, AccessibilityWindowManager awm,
Context context, Handler mainHandler, UiAutomationManager uiAutomationManager,
SystemSupport systemSupport) {
mLock = lock;
@@ -144,9 +143,7 @@
* Creates the service connection.
*/
public void registerProxy(IAccessibilityServiceClient client, int displayId,
- Context context,
- int id, Handler mainHandler,
- AccessibilitySecurityPolicy securityPolicy,
+ int id, AccessibilitySecurityPolicy securityPolicy,
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
AccessibilityTrace trace,
WindowManagerInternal windowManagerInternal) throws RemoteException {
@@ -169,8 +166,8 @@
info.setComponentName(new ComponentName(PROXY_COMPONENT_PACKAGE_NAME,
componentClassDisplayName));
ProxyAccessibilityServiceConnection connection =
- new ProxyAccessibilityServiceConnection(context, info.getComponentName(), info,
- id, mainHandler, mLock, securityPolicy, systemSupport, trace,
+ new ProxyAccessibilityServiceConnection(mContext, info.getComponentName(), info,
+ id, mMainHandler, mLock, securityPolicy, systemSupport, trace,
windowManagerInternal,
mA11yWindowManager, displayId, deviceId);
@@ -263,7 +260,7 @@
* When the connection is removed from tracking in ProxyManager, propagate changes to other a11y
* system components like the input filter and IAccessibilityManagerClients.
*/
- public void updateStateForRemovedDisplay(int displayId, int deviceId) {
+ private void updateStateForRemovedDisplay(int displayId, int deviceId) {
mA11yWindowManager.stopTrackingDisplayProxy(displayId);
// A11yInputFilter isn't thread-safe, so post on the system thread.
mMainHandler.post(
@@ -360,8 +357,9 @@
/**
* If there is at least one proxy, accessibility is enabled.
*/
- public int getStateLocked(int deviceId, boolean automationRunning) {
+ public int getStateLocked(int deviceId) {
int clientState = 0;
+ final boolean automationRunning = mUiAutomationManager.isUiAutomationRunningLocked();
if (automationRunning) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
@@ -387,7 +385,7 @@
/**
* If there is at least one proxy, accessibility is enabled.
*/
- public int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
+ private int getStateForDisplayIdLocked(ProxyAccessibilityServiceConnection proxy) {
int clientState = 0;
if (proxy != null) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
@@ -409,14 +407,14 @@
/**
* Gets the last state for a device.
*/
- public int getLastSentStateLocked(int deviceId) {
+ private int getLastSentStateLocked(int deviceId) {
return mLastStates.get(deviceId, 0);
}
/**
* Sets the last state for a device.
*/
- public void setLastStateLocked(int deviceId, int proxyState) {
+ private void setLastStateLocked(int deviceId, int proxyState) {
mLastStates.put(deviceId, proxyState);
}
@@ -429,7 +427,7 @@
* Virtual Device, the app clients will get the aggregated event types for all proxy-ed displays
* belonging to a VirtualDevice.
*/
- public void updateRelevantEventTypesLocked(int deviceId) {
+ private void updateRelevantEventTypesLocked(int deviceId) {
if (!isProxyedDeviceId(deviceId)) {
return;
}
@@ -452,7 +450,7 @@
/**
* Returns the relevant event types for a Client.
*/
- int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
+ public int computeRelevantEventTypesLocked(AccessibilityManagerService.Client client) {
int relevantEventTypes = 0;
for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) {
final ProxyAccessibilityServiceConnection proxy =
@@ -578,9 +576,8 @@
/**
* Updates the states of the app AccessibilityManagers.
*/
- public void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
- final int proxyState = getStateLocked(deviceId,
- mUiAutomationManager.isUiAutomationRunningLocked());
+ private void scheduleUpdateProxyClientsIfNeededLocked(int deviceId) {
+ final int proxyState = getStateLocked(deviceId);
if (DEBUG) {
Slog.v(LOG_TAG, "State for device id " + deviceId + " is " + proxyState);
Slog.v(LOG_TAG, "Last state for device id " + deviceId + " is "
@@ -606,7 +603,7 @@
*
* @see AccessibilityManager.AccessibilityServicesStateChangeListener
*/
- public void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
+ private void scheduleNotifyProxyClientsOfServicesStateChangeLocked(int deviceId) {
if (DEBUG) {
Slog.v(LOG_TAG, "Notify services state change at device id " + deviceId);
}
@@ -625,7 +622,7 @@
/**
* Updates the focus appearance of AccessibilityManagerClients.
*/
- public void updateFocusAppearanceLocked(int deviceId) {
+ private void updateFocusAppearanceLocked(int deviceId) {
if (DEBUG) {
Slog.v(LOG_TAG, "Update proxy focus appearance at device id " + deviceId);
}
@@ -764,7 +761,7 @@
/**
* Sets a Client device id if the app uid belongs to the virtual device.
*/
- public void updateDeviceIdsIfNeededLocked(int deviceId) {
+ private void updateDeviceIdsIfNeededLocked(int deviceId) {
final RemoteCallbackList<IAccessibilityManagerClient> userClients =
mSystemSupport.getCurrentUserClientsLocked();
final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
@@ -777,7 +774,7 @@
/**
* Updates the device ids of IAccessibilityManagerClients if needed.
*/
- public void updateDeviceIdsIfNeededLocked(int deviceId,
+ private void updateDeviceIdsIfNeededLocked(int deviceId,
@NonNull RemoteCallbackList<IAccessibilityManagerClient> clients) {
final VirtualDeviceManagerInternal localVdm = getLocalVdm();
if (localVdm == null) {
@@ -809,14 +806,17 @@
}
}
- void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
+ /**
+ * Sets the input filter for enabling and disabling features for proxy displays.
+ */
+ public void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
if (DEBUG) {
Slog.v(LOG_TAG, "Set proxy input filter to " + filter);
}
mA11yInputFilter = filter;
}
- VirtualDeviceManagerInternal getLocalVdm() {
+ private VirtualDeviceManagerInternal getLocalVdm() {
if (mLocalVdm == null) {
mLocalVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index a13df47..9747579 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -36,6 +36,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
+import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
@@ -302,8 +303,10 @@
case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
return takeScreenshot();
case AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK:
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK,
- InputDevice.SOURCE_KEYBOARD);
+ if (!AccessibilityUtils.interceptHeadsetHookForActiveCall(mContext)) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK,
+ InputDevice.SOURCE_KEYBOARD);
+ }
return true;
case AccessibilityService.GLOBAL_ACTION_DPAD_UP:
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP,
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index bc5d645..ac77043 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -16,13 +16,18 @@
package com.android.server.autofill;
+import static com.android.server.autofill.Helper.sDebug;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
import android.content.ComponentName;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.metrics.LogMaker;
+import android.os.UserManager;
import android.service.autofill.Dataset;
import android.service.autofill.InternalSanitizer;
import android.service.autofill.SaveInfo;
@@ -30,6 +35,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
@@ -37,6 +43,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
+import com.android.server.utils.Slogf;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -281,6 +288,38 @@
return true;
}
+ /**
+ * Gets a context with the proper display id.
+ *
+ * <p>For most cases it will return the provided context, but on devices that
+ * {@link UserManager#isVisibleBackgroundUsersEnabled() support visible background users}, it
+ * will return a context with the display pased as parameter.
+ */
+ static Context getDisplayContext(Context context, int displayId) {
+ if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+ return context;
+ }
+ if (context.getDisplayId() == displayId) {
+ if (sDebug) {
+ Slogf.d(TAG, "getDisplayContext(): context %s already has displayId %d", context,
+ displayId);
+ }
+ return context;
+ }
+ if (sDebug) {
+ Slogf.d(TAG, "Creating context for display %d", displayId);
+ }
+ Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
+ if (display == null) {
+ Slogf.wtf(TAG, "Could not get context with displayId %d, Autofill operations will "
+ + "probably fail)", displayId);
+ return context;
+ }
+
+ return context.createDisplayContext(display);
+ }
+
+
private interface ViewNodeFilter {
boolean matches(ViewNode node);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3ab5bca..f83d734 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -164,10 +164,12 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -214,7 +216,12 @@
private final AutofillManagerServiceImpl mService;
private final Handler mHandler;
private final AutoFillUI mUi;
- @NonNull private final Context mContext;
+
+ /**
+ * Context associated with the session, it has the same {@link Context#getDisplayId() displayId}
+ * of the activity being autofilled.
+ */
+ private final Context mContext;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -1352,7 +1359,9 @@
mHasCallback = hasCallback;
mUiLatencyHistory = uiLatencyHistory;
mWtfHistory = wtfHistory;
- mContext = context;
+ int displayId = LocalServices.getService(ActivityTaskManagerInternal.class)
+ .getDisplayId(activityToken);
+ mContext = Helper.getDisplayContext(context, displayId);
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
@@ -3401,7 +3410,7 @@
final long saveUiDisplayStartTimestamp = SystemClock.elapsedRealtime();
getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
mService.getServicePackageName(), saveInfo, this,
- mComponentName, this, userId, mPendingSaveUi, isUpdate, mCompatMode,
+ mComponentName, this, mContext, mPendingSaveUi, isUpdate, mCompatMode,
response.getShowSaveDialogIcon());
mSaveEventLogger.maybeSetLatencySaveUiDisplayMillis(
SystemClock.elapsedRealtime()- saveUiDisplayStartTimestamp);
@@ -4272,7 +4281,7 @@
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- targetLabel, targetIcon, this, userId, id, mCompatMode);
+ targetLabel, targetIcon, this, mContext, id, mCompatMode);
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetCountShown(
@@ -5453,6 +5462,7 @@
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
pw.print(prefix); pw.print("flags: "); pw.println(mFlags);
+ pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId());
pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState));
pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index cfd66f1..b3cbe45 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -53,6 +52,7 @@
import com.android.server.UiModeManagerInternal;
import com.android.server.UiThread;
import com.android.server.autofill.Helper;
+import com.android.server.utils.Slogf;
import java.io.PrintWriter;
@@ -198,6 +198,7 @@
* @param serviceIcon icon of autofill service
* @param callback identifier for the caller
* @param userId the user associated wit the session
+ * @param context context with the proper state (like display id) to show the UI
* @param sessionId id of the autofill session
* @param compatMode whether the app is being autofilled in compatibility mode.
*/
@@ -205,11 +206,11 @@
@Nullable String filterText, @Nullable String servicePackageName,
@NonNull ComponentName componentName, @NonNull CharSequence serviceLabel,
@NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback,
- @UserIdInt int userId, int sessionId, boolean compatMode) {
+ @NonNull Context context, int sessionId, boolean compatMode) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
- Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars, userId="
- + userId);
+ Slogf.d(TAG, "showFillUi(): id=%s, filter=%d chars, displayId=%d", focusedId, size,
+ context.getDisplayId());
}
final LogMaker log = Helper
.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
@@ -224,10 +225,8 @@
return;
}
hideAllUiThread(callback);
- mFillUi = new FillUi(mContext, userId, response, focusedId,
- filterText, mOverlayControl, serviceLabel, serviceIcon,
- mUiModeMgr.isNightMode(),
- new FillUi.Callback() {
+ mFillUi = new FillUi(context, response, focusedId, filterText, mOverlayControl,
+ serviceLabel, serviceIcon, mUiModeMgr.isNightMode(), new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
log.setType(MetricsEvent.TYPE_DETAIL);
@@ -325,12 +324,12 @@
public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
@Nullable String servicePackageName, @NonNull SaveInfo info,
@NonNull ValueFinder valueFinder, @NonNull ComponentName componentName,
- @NonNull AutoFillUiCallback callback, @UserIdInt int userId,
+ @NonNull AutoFillUiCallback callback, @NonNull Context context,
@NonNull PendingUi pendingSaveUi, boolean isUpdate, boolean compatMode,
boolean showServiceIcon) {
if (sVerbose) {
- Slog.v(TAG, "showSaveUi(update=" + isUpdate + ") for " + componentName.toShortString()
- + " and user " + userId + ": " + info);
+ Slogf.v(TAG, "showSaveUi(update=%b) for %s and display %d: %s", isUpdate,
+ componentName.toShortString(), context.getDisplayId(), info);
}
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
@@ -350,7 +349,7 @@
}
hideAllUiThread(callback);
mSaveUiCallback = callback;
- mSaveUi = new SaveUi(mContext, userId, pendingSaveUi, serviceLabel, serviceIcon,
+ mSaveUi = new SaveUi(context, pendingSaveUi, serviceLabel, serviceIcon,
servicePackageName, componentName, info, valueFinder, mOverlayControl,
new SaveUi.OnSaveListener() {
@Override
diff --git a/services/autofill/java/com/android/server/autofill/ui/DisplayHelper.java b/services/autofill/java/com/android/server/autofill/ui/DisplayHelper.java
deleted file mode 100644
index 5353480..0000000
--- a/services/autofill/java/com/android/server/autofill/ui/DisplayHelper.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill.ui;
-
-import static com.android.server.autofill.Helper.sDebug;
-
-import android.annotation.UserIdInt;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.UserManager;
-import android.view.Display;
-
-import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
-import com.android.server.utils.Slogf;
-
-/**
- * Helper for display-related needs.
- */
-final class DisplayHelper {
-
- private static final String TAG = "AutofillDisplayHelper";
-
- private static final UserManagerInternal sUmi = LocalServices
- .getService(UserManagerInternal.class);
-
- /**
- * Gets a context with the proper display id set for the given user.
- *
- * <p>For most cases it will return the provided context, but on devices that
- * {@link UserManager#isVisibleBackgroundUsersEnabled() support visible background users}, it
- * will return a context with the display the user started visible on.
- */
- static Context getDisplayContext(Context context, @UserIdInt int userId) {
- if (!UserManager.isVisibleBackgroundUsersEnabled()) {
- return context;
- }
- int displayId = sUmi.getMainDisplayAssignedToUser(userId);
- if (sDebug) {
- Slogf.d(TAG, "Creating context for display %d for user %d", displayId, userId);
- }
- Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
- if (display == null) {
- Slogf.wtf(TAG, "Could not get display with id %d (which is associated with user %d; "
- + "FillUi operations will probably fail", displayId, userId);
- return context;
- }
-
- return context.createDisplayContext(display);
- }
-
- private DisplayHelper() {
- throw new UnsupportedOperationException("Contains only static methods");
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index b651ae59..129ce72 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
@@ -61,6 +60,7 @@
import com.android.server.UiThread;
import com.android.server.autofill.AutofillManagerService;
import com.android.server.autofill.Helper;
+import com.android.server.utils.Slogf;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -134,14 +134,15 @@
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
- FillUi(@NonNull Context systemContext, @UserIdInt int userId, @NonNull FillResponse response,
+ FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @Nullable String filterText,
@NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
@NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) {
- if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+ if (sVerbose) {
+ Slogf.v(TAG, "nightMode: %b displayId: %d", nightMode, context.getDisplayId());
+ }
mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mCallback = callback;
- Context context = DisplayHelper.getDisplayContext(systemContext, userId);
mFullScreen = isFullScreen(context);
mContext = new ContextThemeWrapper(context, mThemeId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index aec0bdf..1204259 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -71,6 +70,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.UiThread;
import com.android.server.autofill.Helper;
+import com.android.server.utils.Slogf;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -173,14 +173,15 @@
private boolean mDestroyed;
- SaveUi(@NonNull Context systemContext, @UserIdInt int userId, @NonNull PendingUi pendingUi,
+ SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
@Nullable String servicePackageName, @NonNull ComponentName componentName,
@NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
@NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener,
boolean nightMode, boolean isUpdate, boolean compatMode, boolean showServiceIcon) {
- Context context = DisplayHelper.getDisplayContext(systemContext, userId);
- if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
+ if (sVerbose) {
+ Slogf.v(TAG, "nightMode: %b displayId: %d", nightMode, context.getDisplayId());
+ }
mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mPendingUi = pendingUi;
mListener = new OneActionThenDestroyListener(listener);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index d8fbd08..0172eaf 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -107,6 +107,9 @@
import com.android.server.SystemService;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
import com.android.server.companion.transport.CompanionTransportManager;
import com.android.server.pm.UserManagerInternal;
@@ -116,6 +119,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -199,6 +203,8 @@
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
new RemoteCallbackList<>();
+ private CrossDeviceSyncController mCrossDeviceSyncController;
+
public CompanionDeviceManagerService(Context context) {
super(context);
@@ -238,6 +244,8 @@
mTransportManager = new CompanionTransportManager(context, mAssociationStore);
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
+ // TODO(b/279663946): move context sync to a dedicated system service
+ mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager);
// Publish "binder" service.
final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl();
@@ -1353,6 +1361,39 @@
public void removeInactiveSelfManagedAssociations() {
CompanionDeviceManagerService.this.removeInactiveSelfManagedAssociations();
}
+
+ @Override
+ public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.registerCallMetadataSyncCallback(callback);
+ }
+ }
+
+ @Override
+ public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncToAllDevicesForUserId(userId, calls);
+ }
+ }
+
+ @Override
+ public void crossDeviceSync(AssociationInfo associationInfo,
+ Collection<CrossDeviceCall> calls) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncToSingleDevice(associationInfo, calls);
+ }
+ }
+
+ @Override
+ public void sendCrossDeviceSyncMessage(int associationId, byte[] message) {
+ if (CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCrossDeviceSyncController.syncMessageToDevice(associationId, message);
+ }
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
index 3649240..3b108e6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java
@@ -16,12 +16,41 @@
package com.android.server.companion;
+import android.companion.AssociationInfo;
+
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceCall;
+import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
+
+import java.util.Collection;
+
/**
* Companion Device Manager Local System Service Interface.
*/
-interface CompanionDeviceManagerServiceInternal {
+public interface CompanionDeviceManagerServiceInternal {
/**
* @see CompanionDeviceManagerService#removeInactiveSelfManagedAssociations
*/
void removeInactiveSelfManagedAssociations();
+
+ /**
+ * Registers a callback from an InCallService / ConnectionService to CDM to process sync
+ * requests and perform call control actions.
+ */
+ void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback);
+
+ /**
+ * Requests a sync from an InCallService / ConnectionService to CDM, for the given association
+ * and message.
+ */
+ void sendCrossDeviceSyncMessage(int associationId, byte[] message);
+
+ /**
+ * Requests a sync from an InCallService to CDM, for the given user and call metadata.
+ */
+ void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls);
+
+ /**
+ * Requests a sync from an InCallService to CDM, for the given association and call metadata.
+ */
+ void crossDeviceSync(AssociationInfo associationInfo, Collection<CrossDeviceCall> calls);
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
index ae4766a..443a732 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java
@@ -17,15 +17,20 @@
package com.android.server.companion.datatransfer.contextsync;
import android.annotation.Nullable;
+import android.companion.AssociationInfo;
import android.telecom.Call;
import android.telecom.InCallService;
import android.telecom.TelecomManager;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.companion.CompanionDeviceConfig;
+import com.android.server.companion.CompanionDeviceManagerServiceInternal;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
@@ -35,90 +40,132 @@
*/
public class CallMetadataSyncInCallService extends InCallService {
+ private static final String TAG = "CallMetadataIcs";
private static final long NOT_VALID = -1L;
+ private CompanionDeviceManagerServiceInternal mCdmsi;
+
@VisibleForTesting
final Map<Call, CrossDeviceCall> mCurrentCalls = new HashMap<>();
- @VisibleForTesting
- boolean mShouldSync;
+ @VisibleForTesting int mNumberOfActiveSyncAssociations;
final Call.Callback mTelecomCallback = new Call.Callback() {
@Override
public void onDetailsChanged(Call call, Call.Details details) {
- mCurrentCalls.get(call).updateCallDetails(details);
- }
- };
- final CallMetadataSyncCallback mCallMetadataSyncCallback = new CallMetadataSyncCallback() {
- @Override
- void processCallControlAction(int crossDeviceCallId, int callControlAction) {
- final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
- mCurrentCalls.values());
- switch (callControlAction) {
- case android.companion.Telecom.Call.ACCEPT:
- if (crossDeviceCall != null) {
- crossDeviceCall.doAccept();
- }
- break;
- case android.companion.Telecom.Call.REJECT:
- if (crossDeviceCall != null) {
- crossDeviceCall.doReject();
- }
- break;
- case android.companion.Telecom.Call.SILENCE:
- doSilence();
- break;
- case android.companion.Telecom.Call.MUTE:
- doMute();
- break;
- case android.companion.Telecom.Call.UNMUTE:
- doUnmute();
- break;
- case android.companion.Telecom.Call.END:
- if (crossDeviceCall != null) {
- crossDeviceCall.doEnd();
- }
- break;
- case android.companion.Telecom.Call.PUT_ON_HOLD:
- if (crossDeviceCall != null) {
- crossDeviceCall.doPutOnHold();
- }
- break;
- case android.companion.Telecom.Call.TAKE_OFF_HOLD:
- if (crossDeviceCall != null) {
- crossDeviceCall.doTakeOffHold();
- }
- break;
- default:
- }
- }
-
- @Override
- void requestCrossDeviceSync(int userId) {
- }
-
- @Override
- void updateStatus(int userId, boolean shouldSyncCallMetadata) {
- if (userId == getUserId()) {
- mShouldSync = shouldSyncCallMetadata;
- if (shouldSyncCallMetadata) {
- initializeCalls();
+ if (mNumberOfActiveSyncAssociations > 0) {
+ final CrossDeviceCall crossDeviceCall = mCurrentCalls.get(call);
+ if (crossDeviceCall != null) {
+ crossDeviceCall.updateCallDetails(details);
+ sync(getUserId());
} else {
- mCurrentCalls.clear();
+ Slog.w(TAG, "Could not update details for nonexistent call");
}
}
}
};
+ final CrossDeviceSyncControllerCallback
+ mCrossDeviceSyncControllerCallback = new CrossDeviceSyncControllerCallback() {
+ @Override
+ void processContextSyncMessage(int associationId,
+ CallMetadataSyncData callMetadataSyncData) {
+ final Iterator<CallMetadataSyncData.Call> iterator =
+ callMetadataSyncData.getRequests().iterator();
+ while (iterator.hasNext()) {
+ final CallMetadataSyncData.Call call = iterator.next();
+ if (call.getId() != 0) {
+ // The call is already assigned an id; treat as control invocations.
+ for (int control : call.getControls()) {
+ processCallControlAction(call.getId(), control);
+ }
+ }
+ iterator.remove();
+ }
+ }
+
+ private void processCallControlAction(long crossDeviceCallId,
+ int callControlAction) {
+ final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId,
+ mCurrentCalls.values());
+ switch (callControlAction) {
+ case android.companion.Telecom.Call.ACCEPT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doAccept();
+ }
+ break;
+ case android.companion.Telecom.Call.REJECT:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doReject();
+ }
+ break;
+ case android.companion.Telecom.Call.SILENCE:
+ doSilence();
+ break;
+ case android.companion.Telecom.Call.MUTE:
+ doMute();
+ break;
+ case android.companion.Telecom.Call.UNMUTE:
+ doUnmute();
+ break;
+ case android.companion.Telecom.Call.END:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doEnd();
+ }
+ break;
+ case android.companion.Telecom.Call.PUT_ON_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doPutOnHold();
+ }
+ break;
+ case android.companion.Telecom.Call.TAKE_OFF_HOLD:
+ if (crossDeviceCall != null) {
+ crossDeviceCall.doTakeOffHold();
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ void requestCrossDeviceSync(AssociationInfo associationInfo) {
+ if (associationInfo.getUserId() == getUserId()) {
+ sync(associationInfo);
+ }
+ }
+
+ @Override
+ void updateNumberOfActiveSyncAssociations(int userId, boolean added) {
+ if (userId == getUserId()) {
+ final boolean wasActivelySyncing = mNumberOfActiveSyncAssociations > 0;
+ if (added) {
+ mNumberOfActiveSyncAssociations++;
+ } else {
+ mNumberOfActiveSyncAssociations--;
+ }
+ if (!wasActivelySyncing && mNumberOfActiveSyncAssociations > 0) {
+ initializeCalls();
+ } else if (wasActivelySyncing && mNumberOfActiveSyncAssociations <= 0) {
+ mCurrentCalls.clear();
+ }
+ }
+ }
+ };
@Override
public void onCreate() {
super.onCreate();
- initializeCalls();
+ if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ mCdmsi = LocalServices.getService(CompanionDeviceManagerServiceInternal.class);
+ mCdmsi.registerCallMetadataSyncCallback(mCrossDeviceSyncControllerCallback);
+ }
}
private void initializeCalls() {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.putAll(getCalls().stream().collect(Collectors.toMap(call -> call,
call -> new CrossDeviceCall(getPackageManager(), call, getCallAudioState()))));
+ mCurrentCalls.keySet().forEach(call -> call.registerCallback(mTelecomCallback,
+ getMainThreadHandler()));
+ sync(getUserId());
}
}
@@ -139,33 +186,39 @@
@Override
public void onCallAdded(Call call) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.put(call,
new CrossDeviceCall(getPackageManager(), call, getCallAudioState()));
+ call.registerCallback(mTelecomCallback);
+ sync(getUserId());
}
}
@Override
public void onCallRemoved(Call call) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.remove(call);
+ call.unregisterCallback(mTelecomCallback);
+ sync(getUserId());
}
}
@Override
public void onMuteStateChanged(boolean isMuted) {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted));
+ sync(getUserId());
}
}
@Override
public void onSilenceRinger() {
if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)
- && mShouldSync) {
+ && mNumberOfActiveSyncAssociations > 0) {
mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging());
+ sync(getUserId());
}
}
@@ -183,4 +236,12 @@
telecomManager.silenceRinger();
}
}
+
+ private void sync(int userId) {
+ mCdmsi.crossDeviceSync(userId, mCurrentCalls.values());
+ }
+
+ private void sync(AssociationInfo associationInfo) {
+ mCdmsi.crossDeviceSync(associationInfo, mCurrentCalls.values());
+ }
}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
index dd0bbf2..ac981d4 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -16,12 +16,14 @@
package com.android.server.companion.datatransfer.contextsync;
+import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.telecom.Call;
import android.telecom.CallAudioState;
import android.telecom.VideoProfile;
@@ -46,7 +48,7 @@
private static final AtomicLong sNextId = new AtomicLong(1);
private final long mId;
- private final Call mCall;
+ private Call mCall;
@VisibleForTesting boolean mIsEnterprise;
@VisibleForTesting boolean mIsOtt;
private final String mCallingAppPackageName;
@@ -58,17 +60,23 @@
private boolean mIsMuted;
private final Set<Integer> mControls = new HashSet<>();
- public CrossDeviceCall(PackageManager packageManager, Call call,
+ public CrossDeviceCall(PackageManager packageManager, @NonNull Call call,
+ CallAudioState callAudioState) {
+ this(packageManager, call.getDetails(), callAudioState);
+ mCall = call;
+ final Bundle extras = new Bundle();
+ extras.putLong(EXTRA_CALL_ID, mId);
+ call.putExtras(extras);
+ }
+
+ CrossDeviceCall(PackageManager packageManager, Call.Details callDetails,
CallAudioState callAudioState) {
mId = sNextId.getAndIncrement();
- mCall = call;
- mCallingAppPackageName = call != null
- ? call.getDetails().getAccountHandle().getComponentName().getPackageName() : null;
- mIsOtt = call != null
- && (call.getDetails().getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
+ mCallingAppPackageName =
+ callDetails.getAccountHandle().getComponentName().getPackageName();
+ mIsOtt = (callDetails.getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
== Call.Details.PROPERTY_SELF_MANAGED;
- mIsEnterprise = call != null
- && (call.getDetails().getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
+ mIsEnterprise = (callDetails.getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL)
== Call.Details.PROPERTY_ENTERPRISE_CALL;
try {
final ApplicationInfo applicationInfo = packageManager
@@ -81,9 +89,7 @@
Slog.e(TAG, "Could not get application info for package " + mCallingAppPackageName, e);
}
mIsMuted = callAudioState != null && callAudioState.isMuted();
- if (call != null) {
- updateCallDetails(call.getDetails());
- }
+ updateCallDetails(callDetails);
}
private byte[] renderDrawableToByteArray(Drawable drawable) {
@@ -108,10 +114,10 @@
final Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
drawable.draw(canvas);
+ return renderBitmapToByteArray(bitmap);
} finally {
bitmap.recycle();
}
- return renderBitmapToByteArray(bitmap);
}
private byte[] renderBitmapToByteArray(Bitmap bitmap) {
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
index 3d8fb7a..adc5faf 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java
@@ -16,27 +16,32 @@
package com.android.server.companion.datatransfer.contextsync;
+import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_CONTEXT_SYNC;
+
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
+import android.companion.IOnMessageReceivedListener;
+import android.companion.IOnTransportsChangedListener;
import android.companion.Telecom;
-import android.companion.Telecom.Call;
import android.content.Context;
+import android.os.Binder;
import android.os.UserHandle;
-import android.util.Pair;
import android.util.Slog;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoParseException;
+import android.util.proto.ProtoUtils;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.companion.CompanionDeviceConfig;
+import com.android.server.companion.transport.CompanionTransportManager;
+
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -45,149 +50,308 @@
public class CrossDeviceSyncController {
private static final String TAG = "CrossDeviceSyncController";
- private static final int BYTE_ARRAY_SIZE = 64;
+
+ private static final int VERSION_1 = 1;
+ private static final int CURRENT_VERSION = VERSION_1;
private final Context mContext;
- private final Callback mCdmCallback;
- private final Map<Integer, List<AssociationInfo>> mUserIdToAssociationInfo = new HashMap<>();
- private final Map<Integer, Pair<InputStream, OutputStream>> mAssociationIdToStreams =
- new HashMap<>();
+ private final CompanionTransportManager mCompanionTransportManager;
+ private final List<AssociationInfo> mConnectedAssociations = new ArrayList<>();
private final Set<Integer> mBlocklist = new HashSet<>();
- private CallMetadataSyncCallback mInCallServiceCallMetadataSyncCallback;
+ private CrossDeviceSyncControllerCallback mCrossDeviceSyncControllerCallback;
- public CrossDeviceSyncController(Context context, Callback callback) {
+ public CrossDeviceSyncController(Context context,
+ CompanionTransportManager companionTransportManager) {
mContext = context;
- mCdmCallback = callback;
+ mCompanionTransportManager = companionTransportManager;
+ mCompanionTransportManager.addListener(new IOnTransportsChangedListener.Stub() {
+ @Override
+ public void onTransportsChanged(List<AssociationInfo> newAssociations) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!CompanionDeviceConfig.isEnabled(
+ CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) {
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ final List<AssociationInfo> existingAssociations = new ArrayList<>(
+ mConnectedAssociations);
+ mConnectedAssociations.clear();
+ mConnectedAssociations.addAll(newAssociations);
+
+ if (mCrossDeviceSyncControllerCallback == null) {
+ Slog.w(TAG, "No callback to report transports changed");
+ return;
+ }
+ for (AssociationInfo associationInfo : newAssociations) {
+ if (!existingAssociations.contains(associationInfo)
+ && !isAssociationBlocked(associationInfo.getId())) {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+ }
+ }
+ for (AssociationInfo associationInfo : existingAssociations) {
+ if (!newAssociations.contains(associationInfo)) {
+ if (isAssociationBlocked(associationInfo.getId())) {
+ mBlocklist.remove(associationInfo.getId());
+ } else {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ false);
+ }
+ }
+ }
+ }
+ });
+ mCompanionTransportManager.addListener(MESSAGE_REQUEST_CONTEXT_SYNC,
+ new IOnMessageReceivedListener.Stub() {
+ @Override
+ public void onMessageReceived(int associationId, byte[] data) {
+ if (mCrossDeviceSyncControllerCallback == null) {
+ Slog.w(TAG, "No callback to process context sync message");
+ return;
+ }
+ mCrossDeviceSyncControllerCallback.processContextSyncMessage(associationId,
+ processTelecomDataFromSync(data));
+ }
+ });
+ }
+
+ private boolean isAssociationBlocked(int associationId) {
+ return mBlocklist.contains(associationId);
}
/** Registers the call metadata callback. */
- public void registerCallMetadataSyncCallback(CallMetadataSyncCallback callback) {
- mInCallServiceCallMetadataSyncCallback = callback;
+ public void registerCallMetadataSyncCallback(CrossDeviceSyncControllerCallback callback) {
+ mCrossDeviceSyncControllerCallback = callback;
+ for (AssociationInfo associationInfo : mConnectedAssociations) {
+ if (!isAssociationBlocked(associationInfo.getId())) {
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
+ }
+ }
}
/** Allow specific associated devices to enable / disable syncing. */
public void setSyncEnabled(AssociationInfo associationInfo, boolean enabled) {
if (enabled) {
- if (mBlocklist.contains(associationInfo.getId())) {
+ if (isAssociationBlocked(associationInfo.getId())) {
mBlocklist.remove(associationInfo.getId());
- openChannel(associationInfo);
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ true);
+ mCrossDeviceSyncControllerCallback.requestCrossDeviceSync(associationInfo);
}
} else {
- if (!mBlocklist.contains(associationInfo.getId())) {
+ if (!isAssociationBlocked(associationInfo.getId())) {
mBlocklist.add(associationInfo.getId());
- closeChannel(associationInfo);
+ mCrossDeviceSyncControllerCallback.updateNumberOfActiveSyncAssociations(
+ associationInfo.getUserId(), /* added= */ false);
+ // Send empty message to device to clear its data (otherwise it will get stale)
+ syncMessageToDevice(associationInfo.getId(), createEmptyMessage());
}
}
}
- /**
- * Opens channels to newly associated devices, and closes channels to newly disassociated
- * devices.
- *
- * TODO(b/265466098): this needs to be limited to just connected devices
- */
- public void onAssociationsChanged(int userId, List<AssociationInfo> newAssociationInfoList) {
- final List<AssociationInfo> existingAssociationInfoList = mUserIdToAssociationInfo.get(
- userId);
- // Close channels to newly-disconnected devices.
- for (AssociationInfo existingAssociationInfo : existingAssociationInfoList) {
- if (!newAssociationInfoList.contains(existingAssociationInfo) && !mBlocklist.contains(
- existingAssociationInfo.getId())) {
- closeChannel(existingAssociationInfo);
- }
- }
- // Open channels to newly-connected devices.
- for (AssociationInfo newAssociationInfo : newAssociationInfoList) {
- if (!existingAssociationInfoList.contains(newAssociationInfo) && !mBlocklist.contains(
- newAssociationInfo.getId())) {
- openChannel(newAssociationInfo);
- }
- }
- mUserIdToAssociationInfo.put(userId, newAssociationInfoList);
- }
-
private boolean isAdminBlocked(int userId) {
return mContext.getSystemService(DevicePolicyManager.class)
.getBluetoothContactSharingDisabled(UserHandle.of(userId));
}
- /** Stop reading, close streams, and close secure channel. */
- private void closeChannel(AssociationInfo associationInfo) {
- // TODO(b/265466098): stop reading from secure channel
- final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
- associationInfo.getId());
- if (streams != null) {
- try {
- if (streams.first != null) {
- streams.first.close();
- }
- if (streams.second != null) {
- streams.second.close();
- }
- } catch (IOException e) {
- Slog.e(TAG, "Could not close streams for association " + associationInfo.getId(),
- e);
- }
- }
- mCdmCallback.closeSecureChannel(associationInfo.getId());
- }
-
- /** Sync initial snapshot and start reading. */
- private void openChannel(AssociationInfo associationInfo) {
- final InputStream is = new ByteArrayInputStream(new byte[BYTE_ARRAY_SIZE]);
- final OutputStream os = new ByteArrayOutputStream(BYTE_ARRAY_SIZE);
- mAssociationIdToStreams.put(associationInfo.getId(), new Pair<>(is, os));
- mCdmCallback.createSecureChannel(associationInfo.getId(), is, os);
- // TODO(b/265466098): only requestSync for this specific association / connection?
- mInCallServiceCallMetadataSyncCallback.requestCrossDeviceSync(associationInfo.getUserId());
- // TODO(b/265466098): start reading from secure channel
- }
-
/**
* Sync data to associated devices.
*
* @param userId The user whose data should be synced.
* @param calls The full list of current calls for all users.
*/
- public void crossDeviceSync(int userId, Collection<CrossDeviceCall> calls) {
- final boolean isAdminBlocked = isAdminBlocked(userId);
- for (AssociationInfo associationInfo : mUserIdToAssociationInfo.get(userId)) {
- final Pair<InputStream, OutputStream> streams = mAssociationIdToStreams.get(
- associationInfo.getId());
- final ProtoOutputStream pos = new ProtoOutputStream(streams.second);
- final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
- for (CrossDeviceCall call : calls) {
- final long callsToken = pos.start(Telecom.CALLS);
- pos.write(Call.ID, call.getId());
- final long originToken = pos.start(Call.ORIGIN);
- pos.write(Call.Origin.CALLER_ID, call.getReadableCallerId(isAdminBlocked));
- pos.write(Call.Origin.APP_ICON, call.getCallingAppIcon());
- pos.write(Call.Origin.APP_NAME, call.getCallingAppName());
- pos.end(originToken);
- pos.write(Call.STATUS, call.getStatus());
- for (int control : call.getControls()) {
- pos.write(Call.CONTROLS_AVAILABLE, control);
- }
- pos.end(callsToken);
+ public void syncToAllDevicesForUserId(int userId, Collection<CrossDeviceCall> calls) {
+ final Set<Integer> associationIds = new HashSet<>();
+ for (AssociationInfo associationInfo : mConnectedAssociations) {
+ if (associationInfo.getUserId() == userId && !isAssociationBlocked(
+ associationInfo.getId())) {
+ associationIds.add(associationInfo.getId());
}
- pos.end(telecomToken);
- pos.flush();
}
+ if (associationIds.isEmpty()) {
+ Slog.w(TAG, "No eligible devices to sync to");
+ return;
+ }
+
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC,
+ createCallUpdateMessage(calls, userId),
+ associationIds.stream().mapToInt(Integer::intValue).toArray());
}
/**
- * Callback to be implemented by CompanionDeviceManagerService.
+ * Sync data to associated devices.
+ *
+ * @param associationInfo The association whose data should be synced.
+ * @param calls The full list of current calls for all users.
*/
- public interface Callback {
- /**
- * Create a secure channel to send messages.
- */
- void createSecureChannel(int associationId, InputStream input, OutputStream output);
+ public void syncToSingleDevice(AssociationInfo associationInfo,
+ Collection<CrossDeviceCall> calls) {
+ if (isAssociationBlocked(associationInfo.getId())) {
+ Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
+ return;
+ }
- /**
- * Close the secure channel created previously.
- */
- void closeSecureChannel(int associationId);
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC,
+ createCallUpdateMessage(calls, associationInfo.getUserId()),
+ new int[]{associationInfo.getId()});
+ }
+
+ /**
+ * Sync data to associated devices.
+ *
+ * @param associationId The association whose data should be synced.
+ * @param message The message to sync.
+ */
+ public void syncMessageToDevice(int associationId, byte[] message) {
+ if (isAssociationBlocked(associationId)) {
+ Slog.e(TAG, "Cannot sync to requested device; connection is blocked");
+ return;
+ }
+
+ mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, message,
+ new int[]{associationId});
+ }
+
+ @VisibleForTesting
+ CallMetadataSyncData processTelecomDataFromSync(byte[] data) {
+ final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData();
+ final ProtoInputStream pis = new ProtoInputStream(data);
+ try {
+ int version = -1;
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ContextSyncMessage.VERSION:
+ version = pis.readInt(ContextSyncMessage.VERSION);
+ Slog.e(TAG, "Processing context sync message version " + version);
+ break;
+ case (int) ContextSyncMessage.TELECOM:
+ if (version == VERSION_1) {
+ final long telecomToken = pis.start(ContextSyncMessage.TELECOM);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) Telecom.CALLS) {
+ final long callsToken = pis.start(Telecom.CALLS);
+ callMetadataSyncData.addCall(processCallDataFromSync(pis));
+ pis.end(callsToken);
+ } else if (pis.getFieldNumber() == (int) Telecom.REQUESTS) {
+ final long requestsToken = pis.start(Telecom.REQUESTS);
+ callMetadataSyncData.addRequest(processCallDataFromSync(pis));
+ pis.end(requestsToken);
+ } else {
+ Slog.e(TAG, "Unhandled field in Telecom:"
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ pis.end(telecomToken);
+ } else {
+ Slog.e(TAG, "Cannot process unsupported version " + version);
+ }
+ break;
+ default:
+ Slog.e(TAG, "Unhandled field in ContextSyncMessage:"
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ } catch (IOException | ProtoParseException e) {
+ throw new RuntimeException(e);
+ }
+ return callMetadataSyncData;
+ }
+
+ @VisibleForTesting
+ CallMetadataSyncData.Call processCallDataFromSync(ProtoInputStream pis) throws IOException {
+ final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Telecom.Call.ID:
+ call.setId(pis.readLong(Telecom.Call.ID));
+ break;
+ case (int) Telecom.Call.ORIGIN:
+ final long originToken = pis.start(Telecom.Call.ORIGIN);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Telecom.Call.Origin.APP_ICON:
+ call.setAppIcon(pis.readBytes(Telecom.Call.Origin.APP_ICON));
+ break;
+ case (int) Telecom.Call.Origin.APP_NAME:
+ call.setAppName(pis.readString(Telecom.Call.Origin.APP_NAME));
+ break;
+ case (int) Telecom.Call.Origin.CALLER_ID:
+ call.setCallerId(pis.readString(Telecom.Call.Origin.CALLER_ID));
+ break;
+ case (int) Telecom.Call.Origin.APP_IDENTIFIER:
+ call.setAppIdentifier(
+ pis.readString(Telecom.Call.Origin.APP_IDENTIFIER));
+ break;
+ default:
+ Slog.e(TAG, "Unhandled field in Origin:"
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ pis.end(originToken);
+ break;
+ case (int) Telecom.Call.STATUS:
+ call.setStatus(pis.readInt(Telecom.Call.STATUS));
+ break;
+ case (int) Telecom.Call.CONTROLS:
+ call.addControl(pis.readInt(Telecom.Call.CONTROLS));
+ break;
+ default:
+ Slog.e(TAG,
+ "Unhandled field in Telecom:" + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ return call;
+ }
+
+ @VisibleForTesting
+ byte[] createCallUpdateMessage(Collection<CrossDeviceCall> calls, int userId) {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
+ for (CrossDeviceCall call : calls) {
+ final long callsToken = pos.start(Telecom.CALLS);
+ pos.write(Telecom.Call.ID, call.getId());
+ final long originToken = pos.start(Telecom.Call.ORIGIN);
+ pos.write(Telecom.Call.Origin.CALLER_ID,
+ call.getReadableCallerId(isAdminBlocked(userId)));
+ pos.write(Telecom.Call.Origin.APP_ICON, call.getCallingAppIcon());
+ pos.write(Telecom.Call.Origin.APP_NAME, call.getCallingAppName());
+ pos.write(Telecom.Call.Origin.APP_IDENTIFIER, call.getCallingAppPackageName());
+ pos.end(originToken);
+ pos.write(Telecom.Call.STATUS, call.getStatus());
+ for (int control : call.getControls()) {
+ pos.write(Telecom.Call.CONTROLS, control);
+ }
+ pos.end(callsToken);
+ }
+ pos.end(telecomToken);
+ return pos.getBytes();
+ }
+
+ /** Create a call control message. */
+ public static byte[] createCallControlMessage(long callId, int control) {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ final long telecomToken = pos.start(ContextSyncMessage.TELECOM);
+ final long requestsToken = pos.start(Telecom.REQUESTS);
+ pos.write(Telecom.Call.ID, callId);
+ pos.write(Telecom.Call.CONTROLS, control);
+ pos.end(requestsToken);
+ pos.end(telecomToken);
+ return pos.getBytes();
+ }
+
+ /** Create an empty context sync message, used to clear state. */
+ public static byte[] createEmptyMessage() {
+ final ProtoOutputStream pos = new ProtoOutputStream();
+ pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION);
+ return pos.getBytes();
}
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
similarity index 67%
rename from services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
rename to services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
index 7c339d2..31e10a8 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncCallback.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java
@@ -16,12 +16,14 @@
package com.android.server.companion.datatransfer.contextsync;
+import android.companion.AssociationInfo;
+
/** Callback for call metadata syncing. */
-public abstract class CallMetadataSyncCallback {
+public abstract class CrossDeviceSyncControllerCallback {
- abstract void processCallControlAction(int crossDeviceCallId, int callControlAction);
+ void processContextSyncMessage(int associationId, CallMetadataSyncData callMetadataSyncData) {}
- abstract void requestCrossDeviceSync(int userId);
+ void requestCrossDeviceSync(AssociationInfo associationInfo) {}
- abstract void updateStatus(int userId, boolean shouldSyncCallMetadata);
+ void updateNumberOfActiveSyncAssociations(int userId, boolean added) {}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e76b628..a641e85 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -3534,21 +3534,29 @@
// - Sanitized ServiceState sent to all other apps with READ_PHONE_STATE
// - Sanitized ServiceState sent to all other apps with READ_PRIVILEGED_PHONE_STATE but not
// READ_PHONE_STATE
- BroadcastOptions options = createServiceStateBroadcastOptions(subId, phoneId);
+ //
+ // Create a unique delivery group key for each variant for SERVICE_STATE broadcast so
+ // that a new broadcast only replaces the pending broadcasts of the same variant.
+ // In order to create a unique delivery group key, append tag of the form
+ // "I:Included-permissions[,E:Excluded-permissions][,lbp]"
+ // Note: Given that location-bypass-packages are static, we can just append "lbp" to the
+ // tag to create a unique delivery group but if location-bypass-packages become dynamic
+ // in the future, we would need to create a unique key for each group of
+ // location-bypass-packages.
if (LocationAccessPolicy.isLocationModeEnabled(mContext, mContext.getUserId())) {
Intent fullIntent = createServiceStateIntent(state, subId, phoneId, false);
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
fullIntent,
new String[]{Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_FINE_LOCATION},
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:RA"));
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
fullIntent,
new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.ACCESS_FINE_LOCATION},
new String[]{Manifest.permission.READ_PHONE_STATE},
null,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:RPA,E:R"));
Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true);
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
@@ -3556,14 +3564,14 @@
new String[]{Manifest.permission.READ_PHONE_STATE},
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
null,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:R,E:A"));
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
sanitizedIntent,
new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
new String[]{Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_FINE_LOCATION},
null,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:RP,E:RA"));
} else {
String[] locationBypassPackages = Binder.withCleanCallingIdentity(() ->
LocationAccessPolicy.getLocationBypassPackages(mContext));
@@ -3573,13 +3581,13 @@
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
fullIntent,
new String[]{Manifest.permission.READ_PHONE_STATE},
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:R"));
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
fullIntent,
new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
new String[]{Manifest.permission.READ_PHONE_STATE},
null,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:RP,E:R"));
}
Intent sanitizedIntent = createServiceStateIntent(state, subId, phoneId, true);
@@ -3588,13 +3596,13 @@
new String[]{Manifest.permission.READ_PHONE_STATE},
new String[]{/* no excluded permissions */},
locationBypassPackages,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:R,lbp"));
mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(
sanitizedIntent,
new String[]{Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
new String[]{Manifest.permission.READ_PHONE_STATE},
locationBypassPackages,
- options);
+ createServiceStateBroadcastOptions(subId, phoneId, "I:RP,E:R,lbp"));
}
}
@@ -3616,12 +3624,14 @@
return intent;
}
- private BroadcastOptions createServiceStateBroadcastOptions(int subId, int phoneId) {
+ private BroadcastOptions createServiceStateBroadcastOptions(int subId, int phoneId,
+ String tag) {
return new BroadcastOptions()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
// Use a combination of subId and phoneId as the key so that older broadcasts
// with same subId and phoneId will get discarded.
- .setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, subId + "-" + phoneId)
+ .setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE,
+ subId + "-" + phoneId + "-" + tag)
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index df3c95b..cf09fde 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -762,6 +762,15 @@
}
}
+ private static void traceInstant(@NonNull String message, @NonNull ServiceRecord service) {
+ if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ return;
+ }
+ final String serviceName = (service.getComponentName() != null)
+ ? service.getComponentName().toShortString() : "(?)";
+ Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, message + serviceName);
+ }
+
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId, boolean isSdkSandboxService,
@@ -818,6 +827,9 @@
}
ServiceRecord r = res.record;
+
+ traceInstant("startService(): ", r);
+
// Note, when startService() or startForegroundService() is called on an already
// running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
// ForegroundServiceStartNotAllowedException), even when the service is already timed
@@ -1407,6 +1419,7 @@
}
private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) {
+ traceInstant("stopService(): ", service);
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopServiceLocked()");
if (service.delayed) {
@@ -1946,6 +1959,7 @@
if (notification == null) {
throw new IllegalArgumentException("null notification");
}
+ traceInstant("startForeground(): ", r);
final int foregroundServiceStartType = foregroundServiceType;
// Instant apps need permission to create foreground services.
if (r.appInfo.isInstantApp()) {
@@ -2484,6 +2498,7 @@
}
} else {
if (r.isForeground) {
+ traceInstant("stopForeground(): ", r);
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
decActiveForegroundAppLocked(smap, r);
@@ -3311,6 +3326,7 @@
Slog.i(TAG_SERVICE, "Short FGS started: " + sr);
}
}
+ traceInstant("short FGS start/extend: ", sr);
sr.setShortFgsInfo(SystemClock.uptimeMillis());
// We'll restart the timeout.
@@ -3356,10 +3372,11 @@
return;
}
Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr);
- final long now = SystemClock.uptimeMillis();
+ traceInstant("short FGS timeout: ", sr);
+
logFGSStateChangeLocked(sr,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
- now > sr.mFgsEnterTime ? (int) (now - sr.mFgsEnterTime) : 0,
+ nowUptime > sr.mFgsEnterTime ? (int) (nowUptime - sr.mFgsEnterTime) : 0,
FGS_STOP_REASON_UNKNOWN,
FGS_TYPE_POLICY_CHECK_UNKNOWN);
try {
@@ -3410,6 +3427,7 @@
}
Slog.e(TAG_SERVICE, "Short FGS procstate demoted: " + sr);
+ traceInstant("short FGS demote: ", sr);
mAm.updateOomAdjLocked(sr.app, OOM_ADJ_REASON_SHORT_FGS_TIMEOUT);
}
@@ -3440,6 +3458,9 @@
} else {
Slog.e(TAG_SERVICE, message);
}
+
+ traceInstant("short FGS ANR: ", sr);
+
mAm.appNotResponding(sr.app, tr);
// TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
@@ -3866,7 +3887,7 @@
if (UserHandle.isCore(callingUid)) {
return;
}
- final int callingUserId = UserHandle.getCallingUserId();
+ final int callingUserId = UserHandle.getUserId(callingUid);
if (callingUserId == userId
|| !mAm.mUserController.isSameProfileGroup(callingUserId, userId)) {
return;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a54e8e9..1a5d425 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5190,7 +5190,10 @@
throw new IllegalArgumentException(
"Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
}
- if (PendingIntent.isNewMutableDisallowedImplicitPendingIntent(flags, intent)) {
+ boolean isActivityResultType =
+ type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT;
+ if (PendingIntent.isNewMutableDisallowedImplicitPendingIntent(flags, intent,
+ isActivityResultType)) {
boolean isChangeEnabled = CompatChanges.isChangeEnabled(
PendingIntent.BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT,
owningUid);
@@ -6915,7 +6918,7 @@
mActivityTaskManager.unhandledBack();
}
- // TODO: Move to ContentProviderHelper?
+ // TODO: Replace this method with one that returns a bound IContentProvider.
public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
final int userId = UserHandle.getCallingUserId();
@@ -6944,6 +6947,16 @@
Log.e(TAG, "Cannot find package for uid: " + uid);
return null;
}
+
+ final ApplicationInfo appInfo = mPackageManagerInt.getApplicationInfo(
+ androidPackage.getPackageName(), /*flags*/0, Process.SYSTEM_UID,
+ UserHandle.USER_SYSTEM);
+ if (!appInfo.isVendor() && !appInfo.isSystemApp() && !appInfo.isSystemExt()
+ && !appInfo.isProduct()) {
+ Log.e(TAG, "openContentUri may only be used by vendor/system/product.");
+ return null;
+ }
+
final AttributionSource attributionSource = new AttributionSource(
Binder.getCallingUid(), androidPackage.getPackageName(), null);
pfd = cph.provider.openFile(attributionSource, uri, "r", null);
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index f29a2e1..c687184a 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1199,6 +1199,9 @@
.sendToTarget();
}
+ mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked(
+ numCached + numEmpty, now);
+
if (mService.mConstants.USE_MODERN_TRIM) {
// Modern trim is not sent based on lowmem state
// Dispatch UI_HIDDEN to processes that need it
@@ -1316,8 +1319,6 @@
profile.setTrimMemoryLevel(0);
});
}
- mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked(
- numCached + numEmpty, now);
return allChanged;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d140403..6360e2a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -167,7 +167,7 @@
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private static final int MAX_LOW_POWER_STATS_SIZE = 16384;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 32768;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private static final String EMPTY = "Empty";
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 0767218..5c68e67 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -242,8 +242,7 @@
*/
@Nullable
public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
- int recordIndex, boolean wouldBeSkipped,
- @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
+ int recordIndex, @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
// When updateDeferredStates() has already applied a deferred state to
// all pending items, apply to this new broadcast too
if (mLastDeferredStates && record.deferUntilActive
@@ -252,8 +251,7 @@
}
if (record.isReplacePending()) {
- final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex,
- wouldBeSkipped);
+ final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex);
if (replacedBroadcastRecord != null) {
return replacedBroadcastRecord;
}
@@ -264,14 +262,13 @@
SomeArgs newBroadcastArgs = SomeArgs.obtain();
newBroadcastArgs.arg1 = record;
newBroadcastArgs.argi1 = recordIndex;
- newBroadcastArgs.argi2 = (wouldBeSkipped ? 1 : 0);
// Cross-broadcast prioritization policy: some broadcasts might warrant being
// issued ahead of others that are already pending, for example if this new
// broadcast is in a different delivery class or is tied to a direct user interaction
// with implicit responsiveness expectations.
getQueueForBroadcast(record).addLast(newBroadcastArgs);
- onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
+ onBroadcastEnqueued(record, recordIndex);
return null;
}
@@ -285,10 +282,9 @@
* wasn't any broadcast that was replaced.
*/
@Nullable
- private BroadcastRecord replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
- boolean wouldBeSkipped) {
+ private BroadcastRecord replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex) {
final ArrayDeque<SomeArgs> queue = getQueueForBroadcast(record);
- return replaceBroadcastInQueue(queue, record, recordIndex, wouldBeSkipped);
+ return replaceBroadcastInQueue(queue, record, recordIndex);
}
/**
@@ -302,15 +298,13 @@
*/
@Nullable
private BroadcastRecord replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
- @NonNull BroadcastRecord record, int recordIndex,
- boolean wouldBeSkipped) {
+ @NonNull BroadcastRecord record, int recordIndex) {
final Iterator<SomeArgs> it = queue.descendingIterator();
final Object receiver = record.receivers.get(recordIndex);
while (it.hasNext()) {
final SomeArgs args = it.next();
final BroadcastRecord testRecord = (BroadcastRecord) args.arg1;
final int testRecordIndex = args.argi1;
- final boolean testWouldBeSkipped = (args.argi2 == 1);
final Object testReceiver = testRecord.receivers.get(testRecordIndex);
if ((record.callingUid == testRecord.callingUid)
&& (record.userId == testRecord.userId)
@@ -320,10 +314,9 @@
// Exact match found; perform in-place swap
args.arg1 = record;
args.argi1 = recordIndex;
- args.argi2 = (wouldBeSkipped ? 1 : 0);
record.copyEnqueueTimeFrom(testRecord);
- onBroadcastDequeued(testRecord, testRecordIndex, testWouldBeSkipped);
- onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
+ onBroadcastDequeued(testRecord, testRecordIndex);
+ onBroadcastEnqueued(record, recordIndex);
return testRecord;
}
}
@@ -383,13 +376,12 @@
final SomeArgs args = it.next();
final BroadcastRecord record = (BroadcastRecord) args.arg1;
final int recordIndex = args.argi1;
- final boolean recordWouldBeSkipped = (args.argi2 == 1);
if (predicate.test(record, recordIndex)) {
consumer.accept(record, recordIndex);
if (andRemove) {
args.recycle();
it.remove();
- onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
+ onBroadcastDequeued(record, recordIndex);
} else {
// Even if we're leaving broadcast in queue, it may have
// been mutated in such a way to change our runnable time
@@ -539,12 +531,11 @@
final SomeArgs next = removeNextBroadcast();
mActive = (BroadcastRecord) next.arg1;
mActiveIndex = next.argi1;
- final boolean wouldBeSkipped = (next.argi2 == 1);
mActiveCountSinceIdle++;
mActiveViaColdStart = false;
mActiveWasStopped = false;
next.recycle();
- onBroadcastDequeued(mActive, mActiveIndex, wouldBeSkipped);
+ onBroadcastDequeued(mActive, mActiveIndex);
}
/**
@@ -561,8 +552,7 @@
/**
* Update summary statistics when the given record has been enqueued.
*/
- private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex,
- boolean wouldBeSkipped) {
+ private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex) {
mCountEnqueued++;
if (record.deferUntilActive) {
mCountDeferred++;
@@ -594,8 +584,7 @@
if (record.callerInstrumented) {
mCountInstrumented++;
}
- if (!wouldBeSkipped
- && (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
+ if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
mCountManifest++;
}
invalidateRunnableAt();
@@ -604,8 +593,7 @@
/**
* Update summary statistics when the given record has been dequeued.
*/
- private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex,
- boolean wouldBeSkipped) {
+ private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex) {
mCountEnqueued--;
if (record.deferUntilActive) {
mCountDeferred--;
@@ -637,8 +625,7 @@
if (record.callerInstrumented) {
mCountInstrumented--;
}
- if (!wouldBeSkipped
- && (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
+ if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
mCountManifest--;
}
invalidateRunnableAt();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8735f8a..96e1523 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -606,24 +606,18 @@
final BroadcastProcessQueue queue = getOrCreateProcessQueue(
getReceiverProcessName(receiver), getReceiverUid(receiver));
- boolean wouldBeSkipped = false;
- if (receiver instanceof ResolveInfo) {
- // If the app is running but would not have been started if the process weren't
- // running, we're going to deliver the broadcast but mark that it's not a manifest
- // broadcast that would have started the app. This allows BroadcastProcessQueue to
- // defer the broadcast as though it were a normal runtime receiver.
- wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
- if (wouldBeSkipped && queue.app == null && !queue.getActiveViaColdStart()) {
- // Skip receiver if there's no running app, the app is not being started, and
- // the app wouldn't be launched for this broadcast
- setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
- "skipped by policy to avoid cold start");
- continue;
- }
+ // If this receiver is going to be skipped, skip it now itself and don't even enqueue
+ // it.
+ final boolean wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
+ if (wouldBeSkipped) {
+ setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
+ "skipped by policy at enqueue");
+ continue;
}
+
enqueuedBroadcast = true;
final BroadcastRecord replacedBroadcast = queue.enqueueOrReplaceBroadcast(
- r, i, wouldBeSkipped, mBroadcastConsumerDeferApply);
+ r, i, mBroadcastConsumerDeferApply);
if (replacedBroadcast != null) {
replacedBroadcasts.add(replacedBroadcast);
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 1426cfd..3bc5de9 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -2100,9 +2100,12 @@
final boolean frozen;
final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
- opt.setPendingFreeze(false);
-
synchronized (mProcLock) {
+ // someone has canceled this freeze
+ if (!opt.isPendingFreeze()) {
+ return;
+ }
+ opt.setPendingFreeze(false);
pid = proc.getPid();
if (mFreezerOverride) {
@@ -2148,7 +2151,6 @@
try {
traceAppFreeze(proc.processName, pid, -1);
Process.setProcessFrozen(pid, proc.uid, true);
-
opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
opt.setFrozen(true);
opt.setHasCollectedFrozenPSS(false);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 78aafeb..6551db9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -643,6 +643,7 @@
pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime=");
TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw);
+ pw.println();
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a181402..b2fdee7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3747,7 +3747,8 @@
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
- switchingFromSystemUserMessage, switchingToSystemUserMessage);
+ switchingFromSystemUserMessage, switchingToSystemUserMessage,
+ getWindowManager());
mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 649305f..412fbe79 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -38,6 +37,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
@@ -51,6 +51,7 @@
import com.android.internal.R;
import com.android.internal.util.ObjectUtils;
import com.android.internal.util.UserIcons;
+import com.android.server.wm.WindowManagerService;
/**
* Dialog to show during the user switch. This dialog shows target user's name and their profile
@@ -70,11 +71,14 @@
protected final UserInfo mNewUser;
private final String mSwitchingFromSystemUserMessage;
private final String mSwitchingToSystemUserMessage;
+ private final WindowManagerService mWindowManager;
protected final Context mContext;
private final int mTraceCookie;
+ private final boolean mNeedToFreezeScreen;
UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
- String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
+ WindowManagerService windowManager) {
// TODO(b/278857848): Make full screen user switcher cover top part of the screen as well.
// This problem is seen only on phones, it works fine on tablets.
super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
@@ -84,8 +88,10 @@
mNewUser = newUser;
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
- mDisableAnimations = ActivityManager.isLowRamDeviceStatic() || SystemProperties.getBoolean(
+ mDisableAnimations = SystemProperties.getBoolean(
"debug.usercontroller.disable_user_switching_dialog_animations", false);
+ mWindowManager = windowManager;
+ mNeedToFreezeScreen = !mDisableAnimations && !isUserSetupComplete(newUser);
mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id;
inflateContent();
@@ -167,6 +173,11 @@
: res.getString(R.string.user_switching_message, mNewUser.name);
}
+ private boolean isUserSetupComplete(UserInfo user) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, /* default= */ 0, user.id) == 1;
+ }
+
@Override
public void show() {
asyncTraceBegin("", 0);
@@ -176,29 +187,24 @@
@Override
public void dismiss() {
super.dismiss();
+ stopFreezingScreen();
asyncTraceEnd("", 0);
}
public void show(@NonNull Runnable onShown) {
if (DEBUG) Slog.d(TAG, "show called");
show();
-
- if (mDisableAnimations) {
+ startShowAnimation(() -> {
+ startFreezingScreen();
onShown.run();
- } else {
- startShowAnimation(onShown);
- }
+ });
}
public void dismiss(@Nullable Runnable onDismissed) {
if (DEBUG) Slog.d(TAG, "dismiss called");
-
if (onDismissed == null) {
// no animation needed
dismiss();
- } else if (mDisableAnimations) {
- dismiss();
- onDismissed.run();
} else {
startDismissAnimation(() -> {
dismiss();
@@ -207,7 +213,31 @@
}
}
+ private void startFreezingScreen() {
+ if (!mNeedToFreezeScreen) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "startFreezingScreen");
+ Trace.traceBegin(TRACE_TAG, "startFreezingScreen");
+ mWindowManager.startFreezingScreen(0, 0);
+ Trace.traceEnd(TRACE_TAG);
+ }
+
+ private void stopFreezingScreen() {
+ if (!mNeedToFreezeScreen) {
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "stopFreezingScreen");
+ Trace.traceBegin(TRACE_TAG, "stopFreezingScreen");
+ mWindowManager.stopFreezingScreen();
+ Trace.traceEnd(TRACE_TAG);
+ }
+
private void startShowAnimation(Runnable onAnimationEnd) {
+ if (mDisableAnimations) {
+ onAnimationEnd.run();
+ return;
+ }
asyncTraceBegin("-showAnimation", 1);
startDialogAnimation(new AlphaAnimation(0, 1), () -> {
asyncTraceEnd("-showAnimation", 1);
@@ -222,6 +252,11 @@
}
private void startDismissAnimation(Runnable onAnimationEnd) {
+ if (mDisableAnimations || mNeedToFreezeScreen) {
+ // animations are disabled or screen is frozen, no need to play an animation
+ onAnimationEnd.run();
+ return;
+ }
asyncTraceBegin("-dismissAnimation", 3);
startDialogAnimation(new AlphaAnimation(1, 0), () -> {
asyncTraceEnd("-dismissAnimation", 3);
@@ -231,8 +266,11 @@
}
private void startProgressAnimation(Runnable onAnimationEnd) {
- final ImageView progressCircular = findViewById(R.id.progress_circular);
- final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) progressCircular.getDrawable();
+ final AnimatedVectorDrawable avd = getSpinnerAVD();
+ if (mDisableAnimations || avd == null) {
+ onAnimationEnd.run();
+ return;
+ }
avd.registerAnimationCallback(new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
@@ -242,7 +280,23 @@
avd.start();
}
+ private AnimatedVectorDrawable getSpinnerAVD() {
+ final ImageView view = findViewById(R.id.progress_circular);
+ if (view != null) {
+ final Drawable drawable = view.getDrawable();
+ if (drawable instanceof AnimatedVectorDrawable) {
+ return (AnimatedVectorDrawable) drawable;
+ }
+ }
+ return null;
+ }
+
private void startDialogAnimation(Animation animation, Runnable onAnimationEnd) {
+ final View view = findViewById(R.id.content);
+ if (mDisableAnimations || view == null) {
+ onAnimationEnd.run();
+ return;
+ }
animation.setDuration(DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
@@ -260,7 +314,7 @@
}
});
- findViewById(R.id.content).startAnimation(animation);
+ view.startAnimation(animation);
}
private void asyncTraceBegin(String subTag, int subCookie) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 13c42eb..ada92f5 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -957,6 +957,10 @@
}
}
+ /*package*/ void postSetA2dpSuspended(boolean enable, String eventSource) {
+ sendILMsgNoDelay(MSG_IL_SET_A2DP_SUSPENDED, SENDMSG_QUEUE, (enable ? 1 : 0), eventSource);
+ }
+
/*package*/ void setA2dpSuspended(boolean enable, boolean internal, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setA2dpSuspended source: " + eventSource + ", enable: "
@@ -985,6 +989,11 @@
}
}
+ /*package*/ void postSetLeAudioSuspended(boolean enable, String eventSource) {
+ sendILMsgNoDelay(
+ MSG_IL_SET_LEAUDIO_SUSPENDED, SENDMSG_QUEUE, (enable ? 1 : 0), eventSource);
+ }
+
/*package*/ void setLeAudioSuspended(boolean enable, boolean internal, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setLeAudioSuspended source: " + eventSource + ", enable: "
@@ -1795,6 +1804,12 @@
final int capturePreset = msg.arg1;
mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
} break;
+ case MSG_IL_SET_A2DP_SUSPENDED: {
+ setA2dpSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj);
+ } break;
+ case MSG_IL_SET_LEAUDIO_SUSPENDED: {
+ setLeAudioSuspended((msg.arg1 == 1), false /*internal*/, (String) msg.obj);
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1869,6 +1884,8 @@
private static final int MSG_IL_SAVE_NDEF_DEVICE_FOR_STRATEGY = 47;
private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
+ private static final int MSG_IL_SET_A2DP_SUSPENDED = 50;
+ private static final int MSG_IL_SET_LEAUDIO_SUSPENDED = 51;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3487fc2..2039325 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6412,7 +6412,7 @@
final String eventSource = new StringBuilder("setA2dpSuspended(").append(enable)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- mDeviceBroker.setA2dpSuspended(enable, false /*internal*/, eventSource);
+ mDeviceBroker.postSetA2dpSuspended(enable, eventSource);
}
/** @see AudioManager#setA2dpSuspended(boolean) */
@@ -6422,7 +6422,7 @@
final String eventSource = new StringBuilder("setLeAudioSuspended(").append(enable)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- mDeviceBroker.setLeAudioSuspended(enable, false /*internal*/, eventSource);
+ mDeviceBroker.postSetLeAudioSuspended(enable, eventSource);
}
/** @see AudioManager#isBluetoothScoOn()
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 7cdea8d..9429b4c 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -57,6 +57,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -168,7 +169,7 @@
@NonNull private final AudioHandler mAudioHandler;
@NonNull private final ISafeHearingVolumeController mVolumeController;
- private final boolean mEnableCsd;
+ private final AtomicBoolean mEnableCsd = new AtomicBoolean(false);
private final Object mCsdStateLock = new Object();
@@ -195,7 +196,7 @@
private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() {
public void onMomentaryExposure(float currentMel, int deviceId) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback");
return;
}
@@ -222,7 +223,7 @@
}
public void onNewCsdValue(float currentCsd, SoundDoseRecord[] records) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
Log.w(TAG, "onNewCsdValue: csd not supported, ignoring value");
return;
}
@@ -272,8 +273,6 @@
mContext = context;
- mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default);
- initCsd();
initSafeVolumes();
mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
@@ -285,6 +284,10 @@
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
R.integer.config_safe_media_volume_index) * 10;
+ mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ // Csd will be initially disabled until the mcc is read in onConfigureSafeMedia()
+ initCsd();
+
mAlarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
}
@@ -310,7 +313,7 @@
}
float getOutputRs2UpperBound() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return 0.f;
}
@@ -329,7 +332,7 @@
}
void setOutputRs2UpperBound(float rs2Value) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -347,7 +350,7 @@
}
float getCsd() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return -1.f;
}
@@ -366,7 +369,7 @@
}
void setCsd(float csd) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -400,7 +403,7 @@
}
void resetCsdTimeouts() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -416,7 +419,7 @@
}
void forceUseFrameworkMel(boolean useFrameworkMel) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -434,7 +437,7 @@
}
void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -454,7 +457,7 @@
}
boolean isCsdEnabled() {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return false;
}
@@ -697,8 +700,8 @@
}
/*package*/ void dump(PrintWriter pw) {
- pw.print(" mEnableCsd="); pw.println(mEnableCsd);
- if (mEnableCsd) {
+ pw.print(" mEnableCsd="); pw.println(mEnableCsd.get());
+ if (mEnableCsd.get()) {
synchronized (mCsdStateLock) {
pw.print(" mCurrentCsd="); pw.println(mCurrentCsd);
}
@@ -719,9 +722,11 @@
pw.println();
}
- /*package*/void reset() {
+ /*package*/void reset() {
Log.d(TAG, "Reset the sound dose helper");
- mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+
+ mSoundDose.compareAndExchange(/*expectedValue=*/null,
+ AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
synchronized (mCsdStateLock) {
try {
@@ -743,7 +748,7 @@
private void updateDoseAttenuation(int newIndex, int device, int streamType,
boolean isAbsoluteVolume) {
- if (!mEnableCsd) {
+ if (!mEnableCsd.get()) {
return;
}
@@ -775,17 +780,19 @@
}
private void initCsd() {
- if (!mEnableCsd) {
- final ISoundDose soundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback);
- if (soundDose == null) {
- Log.w(TAG, "ISoundDose instance is null.");
- return;
- }
- try {
- soundDose.disableCsd();
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot disable CSD", e);
- }
+ ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "ISoundDose instance is null.");
+ return;
+ }
+
+ try {
+ soundDose.setCsdEnabled(mEnableCsd.get());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot disable CSD", e);
+ }
+
+ if (!mEnableCsd.get()) {
return;
}
@@ -829,7 +836,6 @@
SystemProperties.getBoolean("audio.safemedia.force", false)
|| mContext.getResources().getBoolean(
com.android.internal.R.bool.config_safe_media_volume_enabled);
-
boolean safeMediaVolumeBypass =
SystemProperties.getBoolean("audio.safemedia.bypass", false);
@@ -860,6 +866,13 @@
mAudioHandler.obtainMessage(MSG_PERSIST_SAFE_VOLUME_STATE,
persistedState, /*arg2=*/0,
/*obj=*/null), /*delay=*/0);
+
+ boolean newEnableCsd = SystemProperties.getBoolean("audio.safemedia.force", false)
+ || mContext.getResources().getBoolean(
+ R.bool.config_safe_sound_dosage_enabled);
+ if (mEnableCsd.compareAndSet(!newEnableCsd, newEnableCsd)) {
+ initCsd();
+ }
}
}
}
@@ -913,7 +926,7 @@
// legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
// instead of computing it from the volume curves
if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
- || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) {
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) {
return mSafeMediaVolumeIndex;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 5127d26..bf5e8ee 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -704,7 +704,8 @@
}
BiometricFrameworkStatsLogger.getInstance().authenticate(
- mBiometricContext.updateContext(new OperationContextExt(), isCrypto()),
+ mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
+ isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
@@ -734,7 +735,8 @@
}
// Auth canceled
BiometricFrameworkStatsLogger.getInstance().error(
- mBiometricContext.updateContext(new OperationContextExt(), isCrypto()),
+ mBiometricContext.updateContext(new OperationContextExt(true /* isBP */),
+ isCrypto()),
statsModality(),
BiometricsProtoEnums.ACTION_AUTHENTICATE,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index d1de80b..4d821e9 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -46,15 +46,17 @@
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@Surface.Rotation private int mOrientation = Surface.ROTATION_0;
private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN;
+ private final boolean mIsBP;
- /** Create a new empty context. */
- public OperationContextExt() {
- this(new OperationContext());
+ /** Create a context. */
+ public OperationContextExt(boolean isBP) {
+ this(new OperationContext(), isBP);
}
/** Create a wrapped context. */
- public OperationContextExt(@NonNull OperationContext context) {
+ public OperationContextExt(@NonNull OperationContext context, boolean isBP) {
mAidlContext = context;
+ mIsBP = isBP;
}
/**
@@ -268,18 +270,20 @@
}
private void setFirstSessionId(@NonNull BiometricContext biometricContext) {
- mSessionInfo = biometricContext.getKeyguardEntrySessionInfo();
- if (mSessionInfo != null) {
- mAidlContext.id = mSessionInfo.getId();
- mAidlContext.reason = OperationReason.KEYGUARD;
- return;
- }
-
- mSessionInfo = biometricContext.getBiometricPromptSessionInfo();
- if (mSessionInfo != null) {
- mAidlContext.id = mSessionInfo.getId();
- mAidlContext.reason = OperationReason.BIOMETRIC_PROMPT;
- return;
+ if (mIsBP) {
+ mSessionInfo = biometricContext.getBiometricPromptSessionInfo();
+ if (mSessionInfo != null) {
+ mAidlContext.id = mSessionInfo.getId();
+ mAidlContext.reason = OperationReason.BIOMETRIC_PROMPT;
+ return;
+ }
+ } else {
+ mSessionInfo = biometricContext.getKeyguardEntrySessionInfo();
+ if (mSessionInfo != null) {
+ mAidlContext.id = mSessionInfo.getId();
+ mAidlContext.reason = OperationReason.KEYGUARD;
+ return;
+ }
}
// no session
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 7b9fc36..05ca6e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -133,10 +133,6 @@
binderDiedInternal(clearListener);
}
- public boolean isBiometricPrompt() {
- return getCookie() != 0;
- }
-
public long getOperationId() {
return mOperationId;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index d0a4807..03658ce 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -37,7 +37,7 @@
protected final Supplier<T> mLazyDaemon;
@NonNull
- private final OperationContextExt mOperationContext = new OperationContextExt();
+ private final OperationContextExt mOperationContext;
/**
* @param context system_server context
@@ -58,6 +58,7 @@
super(context, token, listener, userId, owner, cookie, sensorId,
biometricLogger, biometricContext);
mLazyDaemon = lazyDaemon;
+ mOperationContext = new OperationContextExt(isBiometricPrompt());
}
@Nullable
@@ -85,6 +86,10 @@
unsubscribeBiometricContext();
}
+ public boolean isBiometricPrompt() {
+ return getCookie() != 0;
+ }
+
protected OperationContextExt getOperationContext() {
return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
}
diff --git a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
index 38b3233..4b847a2 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioServiceUserController.java
@@ -39,18 +39,24 @@
*/
public static boolean isCurrentOrSystemUser() {
int callingUser = Binder.getCallingUserHandle().getIdentifier();
+ return callingUser == getCurrentUser() || callingUser == UserHandle.USER_SYSTEM;
+ }
+
+ /**
+ * Get current foreground user for Broadcast Radio Service
+ *
+ * @return foreground user id.
+ */
+ public static int getCurrentUser() {
final long identity = Binder.clearCallingIdentity();
+ int userId = UserHandle.USER_NULL;
try {
- int currentUser = ActivityManager.getCurrentUser();
- if (callingUser != currentUser && callingUser != UserHandle.USER_SYSTEM) {
- return false;
- }
- return true;
+ userId = ActivityManager.getCurrentUser();
} catch (RuntimeException e) {
// Activity manager not running, nothing we can do assume user 0.
} finally {
Binder.restoreCallingIdentity(identity);
}
- return false;
+ return userId;
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index aab815c..adea13f 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -102,6 +102,8 @@
return new UnsupportedOperationException(action + ": NOT_SUPPORTED");
case Result.TIMEOUT:
return new ParcelableException(new RuntimeException(action + ": TIMEOUT"));
+ case Result.CANCELED:
+ return new IllegalStateException(action + ": CANCELED");
default:
return new ParcelableException(new RuntimeException(
action + ": unknown error (" + result + ")"));
@@ -123,6 +125,8 @@
return RadioTuner.TUNER_RESULT_NOT_SUPPORTED;
case Result.TIMEOUT:
return RadioTuner.TUNER_RESULT_TIMEOUT;
+ case Result.CANCELED:
+ return RadioTuner.TUNER_RESULT_CANCELED;
case Result.UNKNOWN_ERROR:
default:
return RadioTuner.TUNER_RESULT_UNKNOWN_ERROR;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 132fb8e..7c87c6c 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -36,11 +36,13 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
import java.util.ArrayList;
@@ -419,8 +421,13 @@
@GuardedBy("mLock")
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
+ int currentUserId = RadioServiceUserController.getCurrentUser();
List<TunerSession> deadSessions = null;
for (int i = 0; i < mAidlTunerSessions.size(); i++) {
+ if (mAidlTunerSessions.valueAt(i).mUserId != currentUserId
+ && mAidlTunerSessions.valueAt(i).mUserId != UserHandle.USER_SYSTEM) {
+ continue;
+ }
try {
runnable.run(mAidlTunerSessions.valueAt(i).mCallback,
mAidlTunerSessions.valueAt(i).getUid());
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index 0a3823f..beff7bd 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -46,6 +46,7 @@
private final RadioLogger mLogger;
private final RadioModule mModule;
+ final int mUserId;
final android.hardware.radio.ITunerCallback mCallback;
private final int mUid;
private final IBroadcastRadio mService;
@@ -65,6 +66,7 @@
android.hardware.radio.ITunerCallback callback) {
mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
+ mUserId = Binder.getCallingUserHandle().getIdentifier();
mCallback = Objects.requireNonNull(callback, "callback cannot be null");
mUid = Binder.getCallingUid();
mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 59a8154..7b5cb898 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -38,12 +38,14 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.broadcastradio.RadioServiceUserController;
import java.util.ArrayList;
import java.util.HashSet;
@@ -374,8 +376,13 @@
@GuardedBy("mLock")
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
+ int currentUserId = RadioServiceUserController.getCurrentUser();
List<TunerSession> deadSessions = null;
for (TunerSession tunerSession : mAidlTunerSessions) {
+ if (tunerSession.mUserId != currentUserId && tunerSession.mUserId
+ != UserHandle.USER_SYSTEM) {
+ continue;
+ }
try {
runnable.run(tunerSession.mCallback);
} catch (DeadObjectException ex) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 204b964..1efc4a5 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -26,6 +26,7 @@
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
+import android.os.Binder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -52,6 +53,7 @@
private final RadioModule mModule;
private final ITunerSession mHwSession;
+ final int mUserId;
final android.hardware.radio.ITunerCallback mCallback;
@GuardedBy("mLock")
@@ -68,6 +70,7 @@
@NonNull android.hardware.radio.ITunerCallback callback) {
mModule = Objects.requireNonNull(module);
mHwSession = Objects.requireNonNull(hwSession);
+ mUserId = Binder.getCallingUserHandle().getIdentifier();
mCallback = Objects.requireNonNull(callback);
mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
index df8cfad..7ea2c1b 100644
--- a/services/core/java/com/android/server/cpu/CpuMonitorService.java
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -653,8 +653,17 @@
}
public int getAverageAvailableCpuFreqPercent() {
- return (int) ((totalNormalizedAvailableCpuFreqKHz * 100.0)
+ int percent = (int) ((totalNormalizedAvailableCpuFreqKHz * 100.0)
/ totalOnlineMaxCpuFreqKHz);
+ if (percent < 0) {
+ // TODO(b/279478586): This case should never happen. But this case happens
+ // rarely on certain hardware, which indicates a deeper issue. Once this
+ // issue is reproduced, use this log to debug the issue and fix it.
+ Slogf.wtf(TAG, "Computed negative CPU availability percent(%d) for %s ",
+ percent, toString());
+ return 0;
+ }
+ return percent;
}
@Override
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 378363c..774087c 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -366,7 +366,12 @@
return getAutomaticScreenBrightness(null);
}
- float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
+ /**
+ * @return The current brightness recommendation calculated from the current conditions.
+ * @param brightnessEvent Event object to populate with details about why the specific
+ * brightness was chosen.
+ */
+ public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
if (brightnessEvent != null) {
brightnessEvent.setLux(
mAmbientLuxValid ? mAmbientLux : PowerManager.BRIGHTNESS_INVALID_FLOAT);
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index a4bd6a6..46cd496 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -191,7 +191,7 @@
}
if (!(createEglContext(isProtected) && createEglSurface(isProtected, isWideColor)
- && setScreenshotTextureAndSetViewport(hardwareBuffer))) {
+ && setScreenshotTextureAndSetViewport(hardwareBuffer, displayInfo.rotation))) {
dismiss();
return false;
}
@@ -500,7 +500,8 @@
}
private boolean setScreenshotTextureAndSetViewport(
- ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer) {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer,
+ @Surface.Rotation int rotation) {
if (!attachEglContext()) {
return false;
}
@@ -525,14 +526,22 @@
s.release();
st.release();
}
+ // if screen is rotated, map texture starting different corner
+ int indexDelta = (rotation == Surface.ROTATION_90) ? 2
+ : (rotation == Surface.ROTATION_180) ? 4
+ : (rotation == Surface.ROTATION_270) ? 6 : 0;
// Set up texture coordinates for a quad.
// We might need to change this if the texture ends up being
// a different size from the display for some reason.
- mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f);
- mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f);
- mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f);
- mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f);
+ mTexCoordBuffer.put(indexDelta, 0f);
+ mTexCoordBuffer.put(indexDelta + 1, 0f);
+ mTexCoordBuffer.put((indexDelta + 2) % 8, 0f);
+ mTexCoordBuffer.put((indexDelta + 3) % 8, 1f);
+ mTexCoordBuffer.put((indexDelta + 4) % 8, 1f);
+ mTexCoordBuffer.put((indexDelta + 5) % 8, 1f);
+ mTexCoordBuffer.put((indexDelta + 6) % 8, 1f);
+ mTexCoordBuffer.put((indexDelta + 7) % 8, 0f);
// Set up our viewport.
GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 26b6cb0..5771a04 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2548,7 +2548,6 @@
final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token)
.setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
- .setUseIdentityTransform(true)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.build();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 0d89ba8..0861cb5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -83,6 +83,7 @@
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Controls the power state of the display.
@@ -605,7 +606,7 @@
mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
mThermalBrightnessThrottlingDataId =
- logicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
+ logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
if (mDisplayId == Display.DEFAULT_DISPLAY) {
mBatteryStats = BatteryStatsService.getService();
@@ -955,7 +956,7 @@
&& mLogicalDisplay.getPrimaryDisplayDeviceLocked()
.getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
final String thermalBrightnessThrottlingDataId =
- mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
+ mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
mHandler.postAtTime(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
@@ -972,8 +973,8 @@
// last command that was sent to change it's state. Let's assume it is unknown so
// that we trigger a change immediately.
mPowerState.resetScreenState();
- } else if (
- !mThermalBrightnessThrottlingDataId.equals(thermalBrightnessThrottlingDataId)) {
+ } else if (!Objects.equals(mThermalBrightnessThrottlingDataId,
+ thermalBrightnessThrottlingDataId)) {
changed = true;
mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
@@ -2189,7 +2190,8 @@
() -> {
sendUpdatePowerState();
postBrightnessChangeRunnable();
- }, mUniqueDisplayId, mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(),
+ }, mUniqueDisplayId,
+ mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 9e8c47f..3b3d5da 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -85,6 +85,7 @@
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Controls the power state of the display.
@@ -488,7 +489,7 @@
mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(context, mDisplayId);
mTag = "DisplayPowerController2[" + mDisplayId + "]";
mThermalBrightnessThrottlingDataId =
- logicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
+ logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -760,7 +761,7 @@
&& mLogicalDisplay.getPrimaryDisplayDeviceLocked()
.getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
final String thermalBrightnessThrottlingDataId =
- mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
+ mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
mHandler.postAtTime(() -> {
boolean changed = false;
@@ -778,8 +779,8 @@
// last command that was sent to change it's state. Let's assume it is unknown so
// that we trigger a change immediately.
mPowerState.resetScreenState();
- } else if (
- !mThermalBrightnessThrottlingDataId.equals(thermalBrightnessThrottlingDataId)) {
+ } else if (!Objects.equals(mThermalBrightnessThrottlingDataId,
+ thermalBrightnessThrottlingDataId)) {
changed = true;
mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
@@ -1301,7 +1302,8 @@
int brightnessAdjustmentFlags = 0;
if (Float.isNaN(brightnessState)) {
if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) {
- brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness();
+ brightnessState = mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
+ mTempBrightnessEvent);
if (BrightnessUtils.isValidBrightnessValue(brightnessState)
|| brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
rawBrightnessState = mAutomaticBrightnessController
@@ -1820,7 +1822,8 @@
() -> {
sendUpdatePowerState();
postBrightnessChangeRunnable();
- }, mUniqueDisplayId, mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked(),
+ }, mUniqueDisplayId,
+ mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0b6d1c8..e5c50e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -203,6 +203,7 @@
mIsEnabled = true;
mIsInTransition = false;
mThermalBrightnessThrottlingDataId = DisplayDeviceConfig.DEFAULT_ID;
+ mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
}
public void setDevicePositionLocked(int position) {
@@ -514,6 +515,7 @@
mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate;
mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling;
+ mBaseDisplayInfo.thermalBrightnessThrottlingDataId = mThermalBrightnessThrottlingDataId;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
@@ -886,19 +888,14 @@
}
/**
- * @return The ID of the brightness throttling data that this display should use.
- */
- public String getThermalBrightnessThrottlingDataIdLocked() {
- return mThermalBrightnessThrottlingDataId;
- }
-
- /**
* @param brightnessThrottlingDataId The ID of the brightness throttling data that this
* display should use.
*/
public void setThermalBrightnessThrottlingDataIdLocked(String brightnessThrottlingDataId) {
- mThermalBrightnessThrottlingDataId =
- brightnessThrottlingDataId;
+ if (!Objects.equals(brightnessThrottlingDataId, mThermalBrightnessThrottlingDataId)) {
+ mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
+ mDirty = true;
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index a3f8c4d..7574de8 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -437,6 +437,7 @@
* persist the nit value, the nit value for the default display will be loaded.
*/
private void loadNitBasedBrightnessSetting() {
+ float currentBrightnessSetting = Float.NaN;
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float brightnessNitsForDefaultDisplay =
mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
@@ -445,15 +446,17 @@
brightnessNitsForDefaultDisplay);
if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) {
mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
- synchronized (mLock) {
- mCurrentScreenBrightness = brightnessForDefaultDisplay;
- }
- return;
+ currentBrightnessSetting = brightnessForDefaultDisplay;
}
}
}
+
+ if (Float.isNaN(currentBrightnessSetting)) {
+ currentBrightnessSetting = getScreenBrightnessSetting();
+ }
+
synchronized (mLock) {
- mCurrentScreenBrightness = getScreenBrightnessSetting();
+ mCurrentScreenBrightness = currentBrightnessSetting;
}
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index f6cf866..95cbf98 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -25,6 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.AutomaticBrightnessController;
+import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.BrightnessUtils;
@@ -252,10 +253,12 @@
/**
* Evaluates the target automatic brightness of the associated display.
+ * @param brightnessEvent Event object to populate with details about why the specific
+ * brightness was chosen.
*/
- public float getAutomaticScreenBrightness() {
+ public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
float brightness = (mAutomaticBrightnessController != null)
- ? mAutomaticBrightnessController.getAutomaticScreenBrightness()
+ ? mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)
: PowerManager.BRIGHTNESS_INVALID_FLOAT;
adjustAutomaticBrightnessStateIfValid(brightness);
return brightness;
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index fd94be9..17f928a 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -65,7 +65,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
@@ -1067,10 +1066,10 @@
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
- private final Uri mSmoothDisplaySetting =
- Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
- private final Uri mForcePeakRefreshRateSetting =
- Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
+ private final Uri mPeakRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ private final Uri mMinRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
private final Uri mLowPowerModeSetting =
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
private final Uri mMatchContentFrameRateSetting =
@@ -1106,8 +1105,9 @@
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
- mInjector.registerSmoothDisplayObserver(cr, this);
- mInjector.registerForcePeakRefreshRateObserver(cr, this);
+ mInjector.registerPeakRefreshRateObserver(cr, this);
+ cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
@@ -1149,8 +1149,8 @@
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
- if (mSmoothDisplaySetting.equals(uri)
- || mForcePeakRefreshRateSetting.equals(uri)) {
+ if (mPeakRefreshRateSetting.equals(uri)
+ || mMinRefreshRateSetting.equals(uri)) {
updateRefreshRateSettingLocked();
} else if (mLowPowerModeSetting.equals(uri)) {
updateLowPowerModeSettingLocked();
@@ -1205,9 +1205,12 @@
}
private void updateRefreshRateSettingLocked() {
- updateRefreshRateSettingLocked(RefreshRateSettingsUtils.getMinRefreshRate(mContext),
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, mDefaultPeakRefreshRate),
- mDefaultRefreshRate);
+ final ContentResolver cr = mContext.getContentResolver();
+ float minRefreshRate = Settings.System.getFloatForUser(cr,
+ Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+ float peakRefreshRate = Settings.System.getFloatForUser(cr,
+ Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
+ updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
private void updateRefreshRateSettingLocked(
@@ -2840,17 +2843,12 @@
}
interface Injector {
- Uri SMOOTH_DISPLAY_URI = Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
- Uri FORCE_PEAK_REFRESH_RATE_URI =
- Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
+ Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
- void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
+ void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
@@ -2890,16 +2888,9 @@
}
@Override
- public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- cr.registerContentObserver(SMOOTH_DISPLAY_URI, false /*notifyDescendants*/,
- observer, UserHandle.USER_SYSTEM);
- }
-
- @Override
- public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.registerContentObserver(FORCE_PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 220a438..b82129b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -35,6 +35,7 @@
ERROR_DESTINATION,
ERROR_PARAMETER,
ERROR_PARAMETER_SHORT,
+ ERROR_PARAMETER_LONG,
})
public @interface ValidationResult {};
@@ -43,6 +44,7 @@
static final int ERROR_DESTINATION = 2;
static final int ERROR_PARAMETER = 3;
static final int ERROR_PARAMETER_SHORT = 4;
+ static final int ERROR_PARAMETER_LONG = 5;
interface ParameterValidator {
/**
@@ -159,11 +161,13 @@
addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
new AsciiValidator(3), DEST_BROADCAST);
- ParameterValidator statusRequestValidator = new OneByteRangeValidator(0x01, 0x03);
+ ParameterValidator statusRequestValidator = new MinimumOneByteRangeValidator(0x01, 0x03);
addValidationInfo(
- Constants.MESSAGE_DECK_CONTROL, new OneByteRangeValidator(0x01, 0x04), DEST_DIRECT);
+ Constants.MESSAGE_DECK_CONTROL,
+ new MinimumOneByteRangeValidator(0x01, 0x04), DEST_DIRECT);
addValidationInfo(
- Constants.MESSAGE_DECK_STATUS, new OneByteRangeValidator(0x11, 0x1F), DEST_DIRECT);
+ Constants.MESSAGE_DECK_STATUS,
+ new MinimumOneByteRangeValidator(0x11, 0x1F), DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_GIVE_DECK_STATUS, statusRequestValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_PLAY, new PlayModeValidator(), DEST_DIRECT);
@@ -201,9 +205,11 @@
// Messages for the Device Menu Control.
addValidationInfo(
- Constants.MESSAGE_MENU_REQUEST, new OneByteRangeValidator(0x00, 0x02), DEST_DIRECT);
+ Constants.MESSAGE_MENU_REQUEST,
+ new MinimumOneByteRangeValidator(0x00, 0x02), DEST_DIRECT);
addValidationInfo(
- Constants.MESSAGE_MENU_STATUS, new OneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
+ Constants.MESSAGE_MENU_STATUS,
+ new MinimumOneByteRangeValidator(0x00, 0x01), DEST_DIRECT);
// Messages for the Remote Control Passthrough.
addValidationInfo(
@@ -214,7 +220,7 @@
// Messages for the Power Status.
addValidationInfo(
Constants.MESSAGE_REPORT_POWER_STATUS,
- new OneByteRangeValidator(0x00, 0x03),
+ new MinimumOneByteRangeValidator(0x00, 0x03),
DEST_DIRECT | DEST_BROADCAST);
// Messages for the General Protocol.
@@ -229,17 +235,17 @@
oneByteValidator, DEST_DIRECT);
addValidationInfo(
Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
- new OneByteRangeValidator(0x00, 0x01),
+ new MinimumOneByteRangeValidator(0x00, 0x01),
DEST_ALL);
addValidationInfo(
Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
- new OneByteRangeValidator(0x00, 0x01),
+ new SingleByteRangeValidator(0x00, 0x01),
DEST_DIRECT);
// Messages for the Audio Rate Control.
addValidationInfo(
Constants.MESSAGE_SET_AUDIO_RATE,
- new OneByteRangeValidator(0x00, 0x06),
+ new MinimumOneByteRangeValidator(0x00, 0x06),
DEST_DIRECT);
// Messages for Feature Discovery.
@@ -900,11 +906,14 @@
}
}
- /** Check if the given parameters are one byte parameters and within range. */
- private static class OneByteRangeValidator implements ParameterValidator {
+ /**
+ * Check if the given parameters are at least one byte parameters
+ * and the first byte is within range.
+ */
+ private static class MinimumOneByteRangeValidator implements ParameterValidator {
private final int mMinValue, mMaxValue;
- OneByteRangeValidator(int minValue, int maxValue) {
+ MinimumOneByteRangeValidator(int minValue, int maxValue) {
mMinValue = minValue;
mMaxValue = maxValue;
}
@@ -918,6 +927,26 @@
}
}
+ /** Check if the given parameters are exactly one byte parameters and within range. */
+ private static class SingleByteRangeValidator implements ParameterValidator {
+ private final int mMinValue, mMaxValue;
+
+ SingleByteRangeValidator(int minValue, int maxValue) {
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ } else if (params.length > 1) {
+ return ERROR_PARAMETER_LONG;
+ }
+ return toErrorCode(isWithinRange(params[0], mMinValue, mMaxValue));
+ }
+ }
+
/**
* Check if the given Analogue Timer message parameters are valid. Valid parameters should
* adhere to message description of Analogue Timer defined in CEC 1.4 Specification : Message
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 75fe63a..9cd5272 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1611,6 +1611,7 @@
@HdmiCecMessageValidator.ValidationResult
int validationResult = message.getValidationResult();
if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER
+ || validationResult == HdmiCecMessageValidator.ERROR_PARAMETER_LONG
|| !verifyPhysicalAddresses(message)) {
return Constants.ABORT_INVALID_OPERAND;
} else if (validationResult != HdmiCecMessageValidator.OK
diff --git a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java
index 76ea05e..66ce5c7 100644
--- a/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameBaseResolver.java
@@ -268,7 +268,7 @@
}
if (enabled) {
Slog.i(TAG, "disabling default service for user " + userId);
- mDefaultServicesDisabled.removeAt(userId);
+ mDefaultServicesDisabled.delete(userId);
} else {
Slog.i(TAG, "enabling default service for user " + userId);
mDefaultServicesDisabled.put(userId, true);
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 5b21669..153e9c1 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -52,25 +52,27 @@
mHandler = handler;
mNative = nativeIms;
mObservers = Map.ofEntries(
- Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SPEED),
- (reason) -> updateMousePointerSpeed()),
- Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
- (reason) -> updateTouchpadPointerSpeed()),
- Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
- (reason) -> updateTouchpadNaturalScrollingEnabled()),
- Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_TO_CLICK),
- (reason) -> updateTouchpadTapToClickEnabled()),
- Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE),
- (reason) -> updateTouchpadRightClickZoneEnabled()),
- Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
- (reason) -> updateShowTouches()),
- Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON),
- (reason) -> updateAccessibilityLargePointer()),
- Map.entry(Settings.Secure.getUriFor(Settings.Secure.LONG_PRESS_TIMEOUT),
- (reason) -> updateDeepPressStatus(reason)),
- Map.entry(
- Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
- (reason) -> updateMaximumObscuringOpacityForTouch()));
+ Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SPEED),
+ (reason) -> updateMousePointerSpeed()),
+ Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
+ (reason) -> updateTouchpadPointerSpeed()),
+ Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
+ (reason) -> updateTouchpadNaturalScrollingEnabled()),
+ Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_TO_CLICK),
+ (reason) -> updateTouchpadTapToClickEnabled()),
+ Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE),
+ (reason) -> updateTouchpadRightClickZoneEnabled()),
+ Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
+ (reason) -> updateShowTouches()),
+ Map.entry(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON),
+ (reason) -> updateAccessibilityLargePointer()),
+ Map.entry(Settings.Secure.getUriFor(Settings.Secure.LONG_PRESS_TIMEOUT),
+ (reason) -> updateLongPressTimeout(reason)),
+ Map.entry(
+ Settings.Global.getUriFor(
+ Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
+ (reason) -> updateMaximumObscuringOpacityForTouch()));
}
/**
@@ -151,8 +153,13 @@
mNative.reloadPointerIcons();
}
- private void updateDeepPressStatus(String reason) {
- // Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value
+ private void updateLongPressTimeout(String reason) {
+ // Some key gesture timeouts are based on the long press timeout, so update key gesture
+ // timeouts when the value changes. See ViewConfiguration#getKeyRepeatTimeout().
+ mNative.notifyKeyGestureTimeoutsChanged();
+
+ // Update the deep press status.
+ // Not using ViewConfiguration.getLongPressTimeout here because it may return a stale value.
final int timeout = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.LONG_PRESS_TIMEOUT, ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT,
UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index aeb2477..363bc94 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -28,6 +28,7 @@
import android.view.InputEvent;
import android.view.PointerIcon;
import android.view.VerifiedInputEvent;
+import android.view.ViewConfiguration;
import java.util.List;
@@ -198,8 +199,6 @@
void changeKeyboardLayoutAssociation();
- void notifyPointerDisplayIdChanged();
-
void setDisplayEligibilityForPointerCapture(int displayId, boolean enabled);
void setMotionClassifierEnabled(boolean enabled);
@@ -243,6 +242,15 @@
*/
void sysfsNodeChanged(String sysfsNodePath);
+ /**
+ * Notify there is a change in any of the key gesture timeouts, such as the key
+ * repeat timeout or key repeat delay.
+ *
+ * @see ViewConfiguration#getKeyRepeatTimeout()
+ * @see ViewConfiguration#getKeyRepeatDelay()
+ */
+ void notifyKeyGestureTimeoutsChanged();
+
/** The native implementation of InputManagerService methods. */
class NativeImpl implements NativeInputManagerService {
/** Pointer to native input manager service object, used by native code. */
@@ -452,9 +460,6 @@
public native void changeKeyboardLayoutAssociation();
@Override
- public native void notifyPointerDisplayIdChanged();
-
- @Override
public native void setDisplayEligibilityForPointerCapture(int displayId, boolean enabled);
@Override
@@ -493,5 +498,8 @@
@Override
public native void sysfsNodeChanged(String sysfsNodePath);
+
+ @Override
+ public native void notifyKeyGestureTimeoutsChanged();
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c70d555..57f8d14 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -856,13 +856,15 @@
@GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
- private static final class SoftInputShowHideHistory {
+ @VisibleForTesting
+ static final class SoftInputShowHideHistory {
private final Entry[] mEntries = new Entry[16];
private int mNextIndex = 0;
private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
- private static final class Entry {
+ static final class Entry {
final int mSequenceNumber = sSequenceNumber.getAndIncrement();
+ @Nullable
final ClientState mClientState;
@SoftInputModeFlags
final int mFocusedWindowSoftInputMode;
@@ -874,7 +876,7 @@
final boolean mInFullscreenMode;
@NonNull
final String mFocusedWindowName;
- @NonNull
+ @Nullable
final EditorInfo mEditorInfo;
@NonNull
final String mRequestWindowName;
@@ -953,9 +955,13 @@
pw.print(prefix);
pw.print(" editorInfo: ");
- pw.print(" inputType=" + entry.mEditorInfo.inputType);
- pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions);
- pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId);
+ if (entry.mEditorInfo != null) {
+ pw.print(" inputType=" + entry.mEditorInfo.inputType);
+ pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions);
+ pw.println(" fieldId (viewId)=" + entry.mEditorInfo.fieldId);
+ } else {
+ pw.println("null");
+ }
pw.print(prefix);
pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 22da042..f0ab815 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -11598,6 +11598,8 @@
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
listener.onNotificationPosted(sbnHolder, rankingUpdate);
+ } catch (android.os.DeadObjectException ex) {
+ Slog.wtf(TAG, "unable to notify listener (posted): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
}
@@ -11619,6 +11621,8 @@
reason = REASON_LISTENER_CANCEL;
}
listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
+ } catch (android.os.DeadObjectException ex) {
+ Slog.wtf(TAG, "unable to notify listener (removed): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
}
@@ -11629,6 +11633,8 @@
final INotificationListener listener = (INotificationListener) info.service;
try {
listener.onNotificationRankingUpdate(rankingUpdate);
+ } catch (android.os.DeadObjectException ex) {
+ Slog.wtf(TAG, "unable to notify listener (ranking update): " + info, ex);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (ranking update): " + info, ex);
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 48ee64f..8a28888 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -437,6 +437,16 @@
.addCategory(Intent.CATEGORY_DEFAULT)
.build();
+ private static final DefaultCrossProfileIntentFilter CLONE_TO_PARENT_PHOTOPICKER_SELECTION =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ 0x00000018, // 0x00000018 means FLAG_IS_PACKAGE_FOR_FILTER
+ // and FLAG_ALLOW_CHAINED_RESOLUTION set
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
/*
Allowing send action from clone to parent profile to share content from clone apps to parent
apps
@@ -611,7 +621,8 @@
CLONE_TO_PARENT_VIEW_ACTION,
CLONE_TO_PARENT_PICK_INSERT_ACTION,
CLONE_TO_PARENT_DIAL_DATA,
- CLONE_TO_PARENT_SMS_MMS
+ CLONE_TO_PARENT_SMS_MMS,
+ CLONE_TO_PARENT_PHOTOPICKER_SELECTION
);
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 1e0c95c..a267e8a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -118,6 +118,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
@@ -3975,6 +3976,24 @@
mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
+
+ // If this is a system app we hadn't seen before, and this is a first boot or OTA,
+ // we need to unstop it if it doesn't have a launcher entry.
+ if (mPm.mShouldStopSystemPackagesByDefault && scanResult.mRequest.mPkgSetting == null
+ && ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0)
+ && ((scanFlags & SCAN_AS_SYSTEM) != 0)) {
+ final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+ launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launcherIntent.setPackage(parsedPackage.getPackageName());
+ final List<ResolveInfo> launcherActivities =
+ mPm.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, null,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
+ if (launcherActivities.isEmpty()) {
+ scanResult.mPkgSetting.setStopped(false, 0);
+ }
+ }
+
if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 402fb30..9b1a80be 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1120,12 +1120,12 @@
return shortcutOverridesInfo;
}
- List<String> packagesToOverride =
+ Map<String, String> packagesToOverride =
DevicePolicyCache.getInstance().getLauncherShortcutOverrides();
- for (String packageName : packagesToOverride) {
+ for (Map.Entry<String, String> packageNames : packagesToOverride.entrySet()) {
Intent intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
- .setPackage(packageName);
+ .setPackage(packageNames.getValue());
List<LauncherActivityInfoInternal> possibleShortcutOverrides =
queryIntentLauncherActivities(
@@ -1135,7 +1135,8 @@
);
if (!possibleShortcutOverrides.isEmpty()) {
- shortcutOverridesInfo.put(packageName, possibleShortcutOverrides.get(0));
+ shortcutOverridesInfo.put(packageNames.getKey(),
+ possibleShortcutOverrides.get(0));
}
}
return shortcutOverridesInfo;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ef7d413..f78b611 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -928,6 +928,7 @@
boolean showUid = false;
boolean showVersionCode = false;
boolean listApexOnly = false;
+ boolean showStopped = false;
int uid = -1;
int defaultUserId = UserHandle.USER_ALL;
try {
@@ -985,6 +986,9 @@
case "--match-libraries":
getFlags |= PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES;
break;
+ case "--show-stopped":
+ showStopped = true;
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return -1;
@@ -1077,6 +1081,12 @@
stringBuilder.append(info.getLongVersionCode());
}
}
+ if (showStopped) {
+ stringBuilder.append(" stopped=");
+ stringBuilder.append(
+ ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0)
+ ? "true" : "false");
+ }
if (listInstaller) {
stringBuilder.append(" installer=");
stringBuilder.append(mInterface.getInstallerPackageName(info.packageName));
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c54b111..e0dbcbf 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4293,10 +4293,23 @@
// Non-Apex system apps, that are not included in the allowlist in
// initialNonStoppedSystemPackages, should be marked as stopped by default.
- final boolean shouldBeStopped = service.mShouldStopSystemPackagesByDefault
+ boolean shouldBeStopped = service.mShouldStopSystemPackagesByDefault
&& ps.isSystem()
&& !ps.isApex()
&& !service.mInitialNonStoppedSystemPackages.contains(ps.getPackageName());
+ if (shouldBeStopped) {
+ final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+ launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launcherIntent.setPackage(ps.getPackageName());
+ final List<ResolveInfo> launcherActivities =
+ service.snapshotComputer().queryIntentActivitiesInternal(launcherIntent,
+ null,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
+ if (launcherActivities.isEmpty()) {
+ shouldBeStopped = false;
+ }
+ }
ps.setStopped(shouldBeStopped, userHandle);
// If userTypeInstallablePackages is the *only* reason why we're not installing,
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b669ba2..84a9888 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1716,6 +1716,11 @@
if (si == null) {
return;
}
+
+ if (isCallerSystem()) {
+ return; // no check
+ }
+
if (!Objects.equals(callerPackage, si.getPackage())) {
android.util.EventLog.writeEvent(0x534e4554, "109824443", -1, "");
throw new SecurityException("Shortcut package name mismatch");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8433c47..79eed64 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1004,7 +1004,7 @@
return;
}
- final boolean interactive = Display.isOnState(mDefaultDisplay.getState());
+ final boolean interactive = mDefaultDisplayPolicy.isAwake();
Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
+ " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
@@ -2201,8 +2201,10 @@
// Match current screen state.
if (!mPowerManager.isInteractive()) {
- startedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
- finishedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ startedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+ finishedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
}
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@@ -4095,7 +4097,7 @@
// This could prevent some wrong state in multi-displays environment,
// the default display may turned off but interactive is true.
- final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+ final boolean isDefaultDisplayOn = mDefaultDisplayPolicy.isAwake();
final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
handleKeyGesture(event, interactiveAndOn);
@@ -4795,17 +4797,40 @@
}
};
+ @Override
+ public void startedWakingUpGlobal(@WakeReason int reason) {
+
+ }
+
+ @Override
+ public void finishedWakingUpGlobal(@WakeReason int reason) {
+
+ }
+
+ @Override
+ public void startedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
+ mDeviceGoingToSleep = true;
+ }
+
+ @Override
+ public void finishedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
+ mDeviceGoingToSleep = false;
+ }
+
// Called on the PowerManager's Notifier thread.
@Override
- public void startedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
+ public void startedGoingToSleep(int displayGroupId,
+ @PowerManager.GoToSleepReason int pmSleepReason) {
if (DEBUG_WAKEUP) {
- Slog.i(TAG, "Started going to sleep... (why="
+ Slog.i(TAG, "Started going to sleep... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.offReasonToString(
WindowManagerPolicyConstants.translateSleepReasonToOffReason(
pmSleepReason)) + ")");
}
+ if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
+ return;
+ }
- mDeviceGoingToSleep = true;
mRequestedOrSleepingDefaultDisplay = true;
if (mKeyguardDelegate != null) {
@@ -4815,17 +4840,20 @@
// Called on the PowerManager's Notifier thread.
@Override
- public void finishedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) {
+ public void finishedGoingToSleep(int displayGroupId,
+ @PowerManager.GoToSleepReason int pmSleepReason) {
+ if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
+ return;
+ }
EventLogTags.writeScreenToggled(0);
if (DEBUG_WAKEUP) {
- Slog.i(TAG, "Finished going to sleep... (why="
+ Slog.i(TAG, "Finished going to sleep... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.offReasonToString(
WindowManagerPolicyConstants.translateSleepReasonToOffReason(
pmSleepReason)) + ")");
}
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
- mDeviceGoingToSleep = false;
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.setAwake(false);
@@ -4850,26 +4878,18 @@
// Called on the PowerManager's Notifier thread.
@Override
- public void onPowerGroupWakefulnessChanged(int groupId, int wakefulness,
- @PowerManager.GoToSleepReason int pmSleepReason, int globalWakefulness) {
- if (wakefulness != globalWakefulness
- && wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
- && groupId == Display.DEFAULT_DISPLAY_GROUP
- && mKeyguardDelegate != null) {
- mKeyguardDelegate.doKeyguardTimeout(null);
- }
- }
-
- // Called on the PowerManager's Notifier thread.
- @Override
- public void startedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
- EventLogTags.writeScreenToggled(1);
+ public void startedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
if (DEBUG_WAKEUP) {
- Slog.i(TAG, "Started waking up... (why="
+ Slog.i(TAG, "Started waking up... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.onReasonToString(
- WindowManagerPolicyConstants.translateWakeReasonToOnReason(
- pmWakeReason)) + ")");
+ WindowManagerPolicyConstants.translateWakeReasonToOnReason(
+ pmWakeReason)) + ")");
}
+ if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
+ return;
+ }
+ EventLogTags.writeScreenToggled(1);
+
mDefaultDisplayPolicy.setAwake(true);
@@ -4892,13 +4912,16 @@
// Called on the PowerManager's Notifier thread.
@Override
- public void finishedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
+ public void finishedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
if (DEBUG_WAKEUP) {
- Slog.i(TAG, "Finished waking up... (why="
+ Slog.i(TAG, "Finished waking up... (groupId=" + displayGroupId + " why="
+ WindowManagerPolicyConstants.onReasonToString(
WindowManagerPolicyConstants.translateWakeReasonToOnReason(
pmWakeReason)) + ")");
}
+ if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP) {
+ return;
+ }
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedWakingUp();
@@ -5378,8 +5401,8 @@
}
}
mSideFpsEventHandler.onFingerprintSensorReady();
- startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
- finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
+ startedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);
+ finishedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);
int defaultDisplayState = mDisplayManager.getDisplay(DEFAULT_DISPLAY).getState();
boolean defaultDisplayOn = defaultDisplayState == Display.STATE_ON;
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 92f00113..9c3b38a 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -395,7 +395,7 @@
private class KeyHandler extends Handler {
KeyHandler() {
- super(Looper.getMainLooper());
+ super(Looper.myLooper());
}
@Override
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 7c3f1aa..887f946 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -67,10 +67,12 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.companion.virtual.VirtualDevice;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
@@ -762,50 +764,84 @@
void setAllowLockscreenWhenOn(int displayId, boolean allow);
/**
+ * Called when the global wakefulness is becoming awake.
+ *
+ * @param reason One of PowerManager.WAKE_REASON_*, detailing the reason for the change.
+ */
+ void startedWakingUpGlobal(@PowerManager.WakeReason int reason);
+
+ /**
+ * Called when the global wakefulness has finished becoming awake.
+ *
+ * @param reason One of PowerManager.WAKE_REASON_*, detailing the reason for the change.
+ */
+ void finishedWakingUpGlobal(@PowerManager.WakeReason int reason);
+
+ /**
+ * Called when the global wakefulness has started going to sleep.
+ *
+ * @param reason One of PowerManager.WAKE_REASON_*, detailing the reason for the change.
+ */
+ void startedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason);
+
+ /**
+ * Called when the global wakefulness has finished going to sleep.
+ *
+ * @param reason One of PowerManager.WAKE_REASON_*, detailing the reason for the change.
+ */
+ void finishedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason);
+
+ /**
* Called when the device has started waking up.
*
- * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason we're
- * waking up, such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
+ * @param displayGroupId The id of the display group that has started waking up. This will often
+ * be {@link Display#DEFAULT_DISPLAY_GROUP}, but it is possible for other
+ * display groups to exist, for example when there is a
+ * {@link VirtualDevice} with one or more {@link VirtualDisplay}s.
+ * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason this
+ * display group is waking up, such as WAKE_REASON_POWER_BUTTON or
+ * WAKE_REASON_GESTURE.
*/
- void startedWakingUp(@PowerManager.WakeReason int pmWakeReason);
+ void startedWakingUp(int displayGroupId, @PowerManager.WakeReason int pmWakeReason);
/**
* Called when the device has finished waking up.
*
- * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason we're
- * waking up, such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
+ * @param displayGroupId The id of the display group that has finished waking. This will often
+ * be {@link Display#DEFAULT_DISPLAY_GROUP}, but it is possible for other
+ * display groups to exist, for example when there is a
+ * {@link VirtualDevice} with one or more {@link VirtualDisplay}s.
+ * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the specific reason this
+ * display group is waking up, such as WAKE_REASON_POWER_BUTTON or
+ * WAKE_REASON_GESTURE.
*/
- void finishedWakingUp(@PowerManager.WakeReason int pmWakeReason);
+ void finishedWakingUp(int displayGroupId, @PowerManager.WakeReason int pmWakeReason);
/**
* Called when the device has started going to sleep.
*
+ * @param displayGroupId The id of the display group that has started going to sleep. This
+ * will often be {@link Display#DEFAULT_DISPLAY_GROUP}, but it is
+ * possible for other display groups to exist, for example when there is a
+ * {@link VirtualDevice} with one or more {@link VirtualDisplay}s.
* @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
- * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
+ * this display group is going to sleep, such as
+ * GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
*/
- public void startedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason);
+ void startedGoingToSleep(int displayGroupId, @PowerManager.GoToSleepReason int pmSleepReason);
/**
* Called when the device has finished going to sleep.
*
+ * @param displayGroupId The id of the display group that has finished going to sleep. This
+ * will often be {@link Display#DEFAULT_DISPLAY_GROUP}, but it is
+ * possible for other display groups to exist, for example when there is a
+ * {@link VirtualDevice} with one or more {@link VirtualDisplay}s.
* @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
- * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
+ * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or
+ * GO_TO_SLEEP_REASON_TIMEOUT.
*/
- public void finishedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason);
-
- /**
- * Called when a particular PowerGroup has changed wakefulness.
- *
- * @param groupId The id of the PowerGroup.
- * @param wakefulness One of PowerManagerInternal.WAKEFULNESS_* indicating the wake state for
- * the group
- * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the reason this
- * group is going to sleep.
- * @param globalWakefulness The global wakefulness, which may or may not match that of this
- * group. One of PowerManagerInternal.WAKEFULNESS_*
- */
- void onPowerGroupWakefulnessChanged(int groupId, int wakefulness,
- @PowerManager.GoToSleepReason int pmSleepReason, int globalWakefulness);
+ void finishedGoingToSleep(int displayGroupId, @PowerManager.GoToSleepReason int pmSleepReason);
/**
* Called when the display is about to turn on to show content.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index d0ed9bf..9bc0ee22 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -51,6 +51,7 @@
import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.annotations.VisibleForTesting;
@@ -87,7 +88,7 @@
* tell the system when we go to sleep so that it can lock the keyguard if needed.
* </p>
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public class Notifier {
private static final String TAG = "PowerManagerNotifier";
@@ -150,14 +151,21 @@
// begins charging wirelessly
private final boolean mShowWirelessChargingAnimationConfig;
- // The current interactive state. This is set as soon as an interactive state
+ // Encapsulates interactivity information about a particular display group.
+ private static class Interactivity {
+ public boolean isInteractive = true;
+ public int changeReason;
+ public long changeStartTime; // In SystemClock.uptimeMillis()
+ public boolean isChanging;
+ }
+
+ private final SparseArray<Interactivity> mInteractivityByGroupId = new SparseArray<>();
+
+ // The current global interactive state. This is set as soon as an interactive state
// transition begins so as to capture the reason that it happened. At some point
// this state will propagate to the pending state then eventually to the
// broadcasted state over the course of reporting the transition asynchronously.
- private boolean mInteractive = true;
- private int mInteractiveChangeReason;
- private long mInteractiveChangeStartTime; // In SystemClock.uptimeMillis()
- private boolean mInteractiveChanging;
+ private Interactivity mGlobalInteractivity = new Interactivity();
// The pending interactive state that we will eventually want to broadcast.
// This is designed so that we can collapse redundant sequences of awake/sleep
@@ -438,7 +446,8 @@
* which case it will assume that the state did not fully converge before the
* next transition began and will recover accordingly.
*/
- public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
+ public void onGlobalWakefulnessChangeStarted(final int wakefulness, int reason,
+ long eventTime) {
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
@@ -456,10 +465,10 @@
// Handle any early interactive state changes.
// Finish pending incomplete ones from a previous cycle.
- if (mInteractive != interactive) {
+ if (mGlobalInteractivity.isInteractive != interactive) {
// Finish up late behaviors if needed.
- if (mInteractiveChanging) {
- handleLateInteractiveChange();
+ if (mGlobalInteractivity.isChanging) {
+ handleLateGlobalInteractiveChange();
}
// Start input as soon as we start waking up or going to sleep.
@@ -475,11 +484,11 @@
FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
// Handle early behaviors.
- mInteractive = interactive;
- mInteractiveChangeReason = reason;
- mInteractiveChangeStartTime = eventTime;
- mInteractiveChanging = true;
- handleEarlyInteractiveChange();
+ mGlobalInteractivity.isInteractive = interactive;
+ mGlobalInteractivity.isChanging = true;
+ mGlobalInteractivity.changeReason = reason;
+ mGlobalInteractivity.changeStartTime = eventTime;
+ handleEarlyGlobalInteractiveChange();
}
}
@@ -490,10 +499,34 @@
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeFinished");
}
+ for (int i = 0; i < mInteractivityByGroupId.size(); i++) {
+ int groupId = mInteractivityByGroupId.keyAt(i);
+ Interactivity interactivity = mInteractivityByGroupId.valueAt(i);
+ if (interactivity.isChanging) {
+ interactivity.isChanging = false;
+ handleLateInteractiveChange(groupId);
+ }
+ }
+ if (mGlobalInteractivity.isChanging) {
+ mGlobalInteractivity.isChanging = false;
+ handleLateGlobalInteractiveChange();
+ }
+ }
- if (mInteractiveChanging) {
- mInteractiveChanging = false;
- handleLateInteractiveChange();
+
+ private void handleEarlyInteractiveChange(int groupId) {
+ synchronized (mLock) {
+ Interactivity interactivity = mInteractivityByGroupId.get(groupId);
+ if (interactivity == null) {
+ Slog.e(TAG, "no Interactivity entry for groupId:" + groupId);
+ return;
+ }
+ final int changeReason = interactivity.changeReason;
+ if (interactivity.isInteractive) {
+ mHandler.post(() -> mPolicy.startedWakingUp(groupId, changeReason));
+ } else {
+ mHandler.post(() -> mPolicy.startedGoingToSleep(groupId, changeReason));
+ }
}
}
@@ -501,13 +534,13 @@
* Handle early interactive state changes such as getting applications or the lock
* screen running and ready for the user to see (such as when turning on the screen).
*/
- private void handleEarlyInteractiveChange() {
+ private void handleEarlyGlobalInteractiveChange() {
synchronized (mLock) {
- if (mInteractive) {
+ if (mGlobalInteractivity.isInteractive) {
// Waking up...
mHandler.post(() -> {
- mPolicy.startedWakingUp(mInteractiveChangeReason);
mDisplayManagerInternal.onEarlyInteractivityChange(true /*isInteractive*/);
+ mPolicy.startedWakingUpGlobal(mGlobalInteractivity.changeReason);
});
// Send interactive broadcast.
@@ -516,37 +549,36 @@
updatePendingBroadcastLocked();
} else {
// Going to sleep...
- // Tell the policy that we started going to sleep.
mHandler.post(() -> {
- mPolicy.startedGoingToSleep(mInteractiveChangeReason);
mDisplayManagerInternal.onEarlyInteractivityChange(false /*isInteractive*/);
+ mPolicy.startedGoingToSleepGlobal(mGlobalInteractivity.changeReason);
});
}
}
}
/**
- * Handle late interactive state changes once they are finished so that the system can
- * finish pending transitions (such as turning the screen off) before causing
- * applications to change state visibly.
+ * Handle late global interactive state changes. Also see
+ * {@link #handleLateInteractiveChange(int)}.
*/
- private void handleLateInteractiveChange() {
+ private void handleLateGlobalInteractiveChange() {
synchronized (mLock) {
final int interactiveChangeLatency =
- (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
- if (mInteractive) {
+ (int) (SystemClock.uptimeMillis() - mGlobalInteractivity.changeStartTime);
+ if (mGlobalInteractivity.isInteractive) {
// Finished waking up...
mHandler.post(() -> {
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
log.setType(MetricsEvent.TYPE_OPEN);
log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(
- mInteractiveChangeReason));
+ mGlobalInteractivity.changeReason));
log.setLatency(interactiveChangeLatency);
- log.addTaggedData(
- MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
+ log.addTaggedData(MetricsEvent.FIELD_SCREEN_WAKE_REASON,
+ mGlobalInteractivity.changeReason);
MetricsLogger.action(log);
EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
- mPolicy.finishedWakingUp(mInteractiveChangeReason);
+
+ mPolicy.finishedWakingUpGlobal(mGlobalInteractivity.changeReason);
});
} else {
// Finished going to sleep...
@@ -563,18 +595,19 @@
// Tell the policy we finished going to sleep.
final int offReason = WindowManagerPolicyConstants.translateSleepReasonToOffReason(
- mInteractiveChangeReason);
+ mGlobalInteractivity.changeReason);
mHandler.post(() -> {
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
log.setType(MetricsEvent.TYPE_CLOSE);
log.setSubtype(offReason);
log.setLatency(interactiveChangeLatency);
- log.addTaggedData(
- MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
+ log.addTaggedData(MetricsEvent.FIELD_SCREEN_SLEEP_REASON,
+ mGlobalInteractivity.changeReason);
MetricsLogger.action(log);
EventLogTags.writePowerScreenState(
0, offReason, 0, 0, interactiveChangeLatency);
- mPolicy.finishedGoingToSleep(mInteractiveChangeReason);
+
+ mPolicy.finishedGoingToSleepGlobal(mGlobalInteractivity.changeReason);
});
// Send non-interactive broadcast.
@@ -586,12 +619,62 @@
}
/**
+ * Handle late interactive state changes once they are finished so that the system can
+ * finish pending transitions (such as turning the screen off) before causing
+ * applications to change state visibly.
+ */
+ private void handleLateInteractiveChange(int groupId) {
+ synchronized (mLock) {
+ Interactivity interactivity = mInteractivityByGroupId.get(groupId);
+ if (interactivity == null) {
+ Slog.e(TAG, "no Interactivity entry for groupId:" + groupId);
+ return;
+ }
+ final int changeReason = interactivity.changeReason;
+ if (interactivity.isInteractive) {
+ mHandler.post(() -> mPolicy.finishedWakingUp(groupId, changeReason));
+ } else {
+ mHandler.post(() -> mPolicy.finishedGoingToSleep(groupId, changeReason));
+ }
+ }
+ }
+
+ /**
* Called when an individual PowerGroup changes wakefulness.
*/
- public void onPowerGroupWakefulnessChanged(int groupId, int groupWakefulness, int changeReason,
- int globalWakefulness) {
- mHandler.post(() -> mPolicy.onPowerGroupWakefulnessChanged(groupId, groupWakefulness,
- changeReason, globalWakefulness));
+ public void onGroupWakefulnessChangeStarted(int groupId, int wakefulness, int changeReason,
+ long eventTime) {
+ final boolean isInteractive = PowerManagerInternal.isInteractive(wakefulness);
+
+ boolean isNewGroup = false;
+ Interactivity interactivity = mInteractivityByGroupId.get(groupId);
+ if (interactivity == null) {
+ isNewGroup = true;
+ interactivity = new Interactivity();
+ mInteractivityByGroupId.put(groupId, interactivity);
+ }
+ if (isNewGroup || interactivity.isInteractive != isInteractive) {
+ // Finish up late behaviors if needed.
+ if (interactivity.isChanging) {
+ handleLateInteractiveChange(groupId);
+ }
+
+ // Handle early behaviors.
+ interactivity.isInteractive = isInteractive;
+ interactivity.changeReason = changeReason;
+ interactivity.changeStartTime = eventTime;
+ interactivity.isChanging = true;
+ handleEarlyInteractiveChange(groupId);
+ }
+ }
+
+ /**
+ * Called when a PowerGroup has been removed.
+ *
+ * @param groupId which group was removed
+ */
+ public void onGroupRemoved(int groupId) {
+ mInteractivityByGroupId.remove(groupId);
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e392c24..9ff98be 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -686,6 +686,8 @@
@Override
public void onWakefulnessChangedLocked(int groupId, int wakefulness, long eventTime,
int reason, int uid, int opUid, String opPackageName, String details) {
+ mWakefulnessChanging = true;
+ mDirty |= DIRTY_WAKEFULNESS;
if (wakefulness == WAKEFULNESS_AWAKE) {
// Kick user activity to prevent newly awake group from timing out instantly.
// The dream may end without user activity if the dream app crashes / is updated,
@@ -696,9 +698,8 @@
PowerManager.USER_ACTIVITY_EVENT_OTHER, flags, uid);
}
mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
+ mNotifier.onGroupWakefulnessChangeStarted(groupId, wakefulness, reason, eventTime);
updateGlobalWakefulnessLocked(eventTime, reason, uid, opUid, opPackageName, details);
- mNotifier.onPowerGroupWakefulnessChanged(groupId, wakefulness, reason,
- getGlobalWakefulnessLocked());
updatePowerStateLocked();
}
}
@@ -2152,7 +2153,7 @@
mDozeStartInProgress &= (newWakefulness == WAKEFULNESS_DOZING);
if (mNotifier != null) {
- mNotifier.onWakefulnessChangeStarted(newWakefulness, reason, eventTime);
+ mNotifier.onGlobalWakefulnessChangeStarted(newWakefulness, reason, eventTime);
}
mAttentionDetector.onWakefulnessChangeStarted(newWakefulness);
@@ -2163,15 +2164,6 @@
if (sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
}
- PowerGroup defaultGroup = mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP);
- if (defaultGroup.getWakefulnessLocked() == WAKEFULNESS_DOZING) {
- // Workaround for b/187231320 where the AOD can get stuck in a "half on /
- // half off" state when a non-default-group VirtualDisplay causes the global
- // wakefulness to change to awake, even though the default display is
- // dozing. We set sandman summoned to restart dreaming to get it unstuck.
- // TODO(b/255688811) - fix this so that AOD never gets interrupted at all.
- defaultGroup.setSandmanSummonedLocked(true);
- }
break;
case WAKEFULNESS_ASLEEP:
@@ -2248,6 +2240,8 @@
@GuardedBy("mLock")
void onPowerGroupEventLocked(int event, PowerGroup powerGroup) {
+ mWakefulnessChanging = true;
+ mDirty |= DIRTY_WAKEFULNESS;
final int groupId = powerGroup.getGroupId();
if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED) {
mPowerGroups.delete(groupId);
@@ -2260,6 +2254,11 @@
// Kick user activity to prevent newly added group from timing out instantly.
userActivityNoUpdateLocked(powerGroup, mClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_OTHER, /* flags= */ 0, Process.SYSTEM_UID);
+ mNotifier.onGroupWakefulnessChangeStarted(groupId,
+ powerGroup.getWakefulnessLocked(), WAKE_REASON_DISPLAY_GROUP_ADDED,
+ mClock.uptimeMillis());
+ } else if (event == DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED) {
+ mNotifier.onGroupRemoved(groupId);
}
if (oldWakefulness != newWakefulness) {
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index b6ab351..4bbca33 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -27,9 +27,6 @@
import android.util.SparseIntArray;
import android.view.Surface;
-import java.util.LinkedList;
-import java.util.Queue;
-
/**
* Provides access to the low-level TV input hardware abstraction layer.
*/
@@ -64,6 +61,8 @@
private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
int generation);
private static native void nativeClose(long ptr);
+ private static native int nativeSetTvMessageEnabled(long ptr, int deviceId, int streamId,
+ int type, boolean enabled);
private final Object mLock = new Object();
private long mPtr = 0;
@@ -100,6 +99,25 @@
}
}
+ public int setTvMessageEnabled(int deviceId, TvStreamConfig streamConfig, int type,
+ boolean enabled) {
+ synchronized (mLock) {
+ if (mPtr == 0) {
+ return ERROR_NO_INIT;
+ }
+ int generation = mStreamConfigGenerations.get(deviceId, 0);
+ if (generation != streamConfig.getGeneration()) {
+ return ERROR_STALE_CONFIG;
+ }
+ if (nativeSetTvMessageEnabled(mPtr, deviceId, streamConfig.getStreamId(), type,
+ enabled) == 0) {
+ return SUCCESS;
+ } else {
+ return ERROR_UNKNOWN;
+ }
+ }
+ }
+
public int removeStream(int deviceId, TvStreamConfig streamConfig) {
synchronized (mLock) {
if (mPtr == 0) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 98dfb00..077f8d5 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -459,9 +459,11 @@
private int findDeviceIdForInputIdLocked(String inputId) {
for (int i = 0; i < mConnections.size(); ++i) {
- Connection connection = mConnections.get(i);
- if (connection.getInfoLocked().getId().equals(inputId)) {
- return i;
+ int key = mConnections.keyAt(i);
+ Connection connection = mConnections.get(key);
+ if (connection != null && connection.getInfoLocked() != null
+ && connection.getInfoLocked().getId().equals(inputId)) {
+ return key;
}
}
return -1;
@@ -489,6 +491,27 @@
return configsList;
}
+ public boolean setTvMessageEnabled(String inputId, int type,
+ boolean enabled) {
+ synchronized (mLock) {
+ int deviceId = findDeviceIdForInputIdLocked(inputId);
+ if (deviceId < 0) {
+ Slog.e(TAG, "Invalid inputId : " + inputId);
+ return false;
+ }
+
+ Connection connection = mConnections.get(deviceId);
+ boolean success = true;
+ for (TvStreamConfig config : connection.getConfigsLocked()) {
+ success = success
+ && mHal.setTvMessageEnabled(deviceId, config, type, enabled)
+ == TvInputHal.SUCCESS;
+ }
+
+ return success;
+ }
+ }
+
/**
* Take a snapshot of the given TV input into the provided Surface.
*/
@@ -764,6 +787,7 @@
+ " mHardwareInfo: " + mHardwareInfo
+ ", mInfo: " + mInfo
+ ", mCallback: " + mCallback
+ + ", mHardware: " + mHardware
+ ", mConfigs: " + Arrays.toString(mConfigs)
+ ", mCallingUid: " + mCallingUid
+ ", mResolvedUserId: " + mResolvedUserId
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index fd203bb..88c9042 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2154,6 +2154,9 @@
try {
synchronized (mLock) {
try {
+ final String inputId =
+ getSessionStateLocked(sessionToken, callingUid, userId).inputId;
+ mTvInputHardwareManager.setTvMessageEnabled(inputId, type, enabled);
getSessionLocked(sessionToken, callingUid, resolvedUserId)
.setTvMessageEnabled(type, enabled);
} catch (RemoteException | SessionNotFoundException e) {
@@ -2711,7 +2714,10 @@
.audioAddress("0")
.hdmiPortId(0)
.build();
- mTvInputHardwareManager.onDeviceAvailable(info, null);
+ TvStreamConfig[] configs = {
+ new TvStreamConfig.Builder().streamId(19001)
+ .generation(1).maxHeight(600).maxWidth(800).type(1).build()};
+ mTvInputHardwareManager.onDeviceAvailable(info, configs);
}
/**
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 51872b3..9020cb3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -803,6 +803,20 @@
protected WallpaperData mLastLockWallpaper;
private IWallpaperManagerCallback mKeyguardListener;
private boolean mWaitingForUnlock;
+
+ /**
+ * Flag set to true after reboot if the home wallpaper is waiting for the device to be unlocked.
+ * This happens for wallpapers that are not direct-boot aware; they can only be rendered after
+ * the user unlocks the device for the first time after a reboot. In the meantime, the default
+ * wallpaper is shown instead.
+ */
+ private boolean mHomeWallpaperWaitingForUnlock;
+
+ /**
+ * Flag set to true after reboot if the lock wallpaper is waiting for the device to be unlocked.
+ */
+ private boolean mLockWallpaperWaitingForUnlock;
+
private boolean mShuttingDown;
/**
@@ -1339,9 +1353,6 @@
void complete() {
// Only changes from home+lock to just home or lock need attention
- // If setting the wallpaper fails, this callback will be called
- // when the wallpaper is detached, in which case wallpapers may have
- // already changed. Make sure we're not overwriting a more recent wallpaper.
if (mNewWallpaper.mSystemWasBoth) {
if (DEBUG) {
Slog.v(TAG, "Handling change from system+lock wallpaper");
@@ -1364,7 +1375,8 @@
mOriginalSystem.wallpaperComponent;
lockWp.connection = mOriginalSystem.connection;
lockWp.connection.mWallpaper = lockWp;
- updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ mOriginalSystem.mWhich = FLAG_LOCK;
+ updateEngineFlags(mOriginalSystem);
notifyWallpaperColorsChanged(lockWp, FLAG_LOCK);
} else {
// Failed rename, use current system wp for both
@@ -1373,7 +1385,7 @@
}
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
currentSystem.mWhich = FLAG_SYSTEM | FLAG_LOCK;
- updateEngineFlags(currentSystem, FLAG_SYSTEM | FLAG_LOCK);
+ updateEngineFlags(currentSystem);
mLockWallpaperMap.remove(mNewWallpaper.userId);
}
} else {
@@ -1382,7 +1394,7 @@
Slog.v(TAG, "live system+lock to system success");
}
mOriginalSystem.mWhich = FLAG_LOCK;
- updateEngineFlags(mOriginalSystem, FLAG_LOCK);
+ updateEngineFlags(mOriginalSystem);
mLockWallpaperMap.put(mNewWallpaper.userId, mOriginalSystem);
mLastLockWallpaper = mOriginalSystem;
notifyWallpaperColorsChanged(mOriginalSystem, FLAG_LOCK);
@@ -1395,7 +1407,7 @@
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
if (currentSystem.wallpaperId == mOriginalSystem.wallpaperId) {
currentSystem.mWhich = FLAG_SYSTEM;
- updateEngineFlags(currentSystem, FLAG_SYSTEM);
+ updateEngineFlags(currentSystem);
}
}
}
@@ -1408,24 +1420,6 @@
Slog.v(TAG, "new lastLockWp: " + mLastLockWallpaper);
}
}
-
- private void updateEngineFlags(WallpaperData wallpaper, @SetWallpaperFlags int which) {
- if (wallpaper.connection == null) {
- return;
- }
- wallpaper.connection.forEachDisplayConnector(
- connector -> {
- try {
- if (connector.mEngine != null) {
- connector.mEngine.setWallpaperFlags(which);
- mWindowManagerInternal.setWallpaperShowWhenLocked(
- connector.mToken, (which & FLAG_LOCK) != 0);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to update wallpaper engine flags", e);
- }
- });
- }
}
class MyPackageMonitor extends PackageMonitor {
@@ -1790,7 +1784,23 @@
public void onUnlockUser(final int userId) {
synchronized (mLock) {
if (mCurrentUserId == userId) {
- if (mWaitingForUnlock) {
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ if (mHomeWallpaperWaitingForUnlock) {
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
+ // TODO(b/278261563): call notifyCallbacksLocked inside switchWallpaper
+ notifyCallbacksLocked(systemWallpaper);
+ }
+ if (mLockWallpaperWaitingForUnlock) {
+ final WallpaperData lockWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_LOCK);
+ switchWallpaper(lockWallpaper, null);
+ notifyCallbacksLocked(lockWallpaper);
+ }
+ }
+
+ if (mWaitingForUnlock && !mIsLockscreenLiveWallpaperEnabled) {
// the desired wallpaper is not direct-boot aware, load it now
final WallpaperData systemWallpaper =
getWallpaperSafeLocked(userId, FLAG_SYSTEM);
@@ -1845,13 +1855,23 @@
}
mCurrentUserId = userId;
systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
- lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ lockWallpaper = systemWallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)
+ ? systemWallpaper : getWallpaperSafeLocked(userId, FLAG_LOCK);
+ } else {
+ final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
+ lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+ }
+
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (systemWallpaper.wallpaperObserver == null) {
systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
systemWallpaper.wallpaperObserver.startWatching();
}
+ if (mIsLockscreenLiveWallpaperEnabled && lockWallpaper != systemWallpaper) {
+ switchWallpaper(lockWallpaper, null);
+ }
switchWallpaper(systemWallpaper, reply);
}
@@ -1870,6 +1890,11 @@
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
synchronized (mLock) {
mWaitingForUnlock = false;
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = false;
+ if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = false;
+ }
+
final ComponentName cname = wallpaper.wallpaperComponent != null ?
wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
if (!bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
@@ -1882,6 +1907,11 @@
} catch (RemoteException ignored) {
}
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ onSwitchWallpaperFailLocked(wallpaper, reply, si);
+ return;
+ }
+
if (si == null) {
Slog.w(TAG, "Failure starting previous wallpaper; clearing");
clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
@@ -1899,6 +1929,43 @@
}
}
+ /**
+ * Fallback method if a wallpaper fails to load on boot or after a user switch.
+ * Only called if mIsLockscreenLiveWallpaperEnabled is true.
+ */
+ private void onSwitchWallpaperFailLocked(
+ WallpaperData wallpaper, IRemoteCallback reply, ServiceInfo serviceInfo) {
+
+ if (serviceInfo == null) {
+ Slog.w(TAG, "Failure starting previous wallpaper; clearing");
+
+ if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+ } else {
+ clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+ }
+ return;
+ }
+ Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
+ // We might end up persisting the current wallpaper data
+ // while locked, so pretend like the component was actually
+ // bound into place
+ wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
+ final WallpaperData fallback = new WallpaperData(wallpaper.userId, wallpaper.mWhich);
+
+ // files from the previous static wallpaper may still be stored in memory.
+ // delete them in order to show the default wallpaper.
+ if (wallpaper.wallpaperFile.exists()) {
+ wallpaper.wallpaperFile.delete();
+ wallpaper.cropFile.delete();
+ }
+
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
+ if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = true;
+ if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = true;
+ }
+
@Override
public void clearWallpaper(String callingPackage, int which, int userId) {
if (DEBUG) Slog.v(TAG, "clearWallpaper");
@@ -3008,6 +3075,9 @@
newWallpaper.userId);
if (lockedWallpaper != null) {
detachWallpaperLocked(lockedWallpaper);
+ if (same) {
+ updateEngineFlags(newWallpaper);
+ }
}
mLockWallpaperMap.remove(newWallpaper.userId);
}
@@ -3343,6 +3413,27 @@
}
}
+ // Updates the given wallpaper's Engine so that its destination flags are the same as those of
+ // the wallpaper, e.g., after a wallpaper has been changed from displaying on home+lock to home
+ // or lock only.
+ private void updateEngineFlags(WallpaperData wallpaper) {
+ if (wallpaper.connection == null) {
+ return;
+ }
+ wallpaper.connection.forEachDisplayConnector(
+ connector -> {
+ try {
+ if (connector.mEngine != null) {
+ connector.mEngine.setWallpaperFlags(wallpaper.mWhich);
+ mWindowManagerInternal.setWallpaperShowWhenLocked(
+ connector.mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to update wallpaper engine flags", e);
+ }
+ });
+ }
+
private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
wallpaper.wallpaperComponent = null;
detachWallpaperLocked(wallpaper);
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index 32f7b96..5c929a9 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -239,7 +239,17 @@
}
return null;
}
- source.getBounds(mTmpRect);
+ mTmpRect.setEmpty();
+ if (source.mTransitionController.inFinishingTransition(source)) {
+ final Transition.ChangeInfo changeInfo = source.mTransitionController
+ .mFinishingTransition.mChanges.get(source);
+ if (changeInfo != null) {
+ mTmpRect.set(changeInfo.mAbsoluteBounds);
+ }
+ }
+ if (mTmpRect.isEmpty()) {
+ source.getBounds(mTmpRect);
+ }
mTmpRect.offsetTo(0, 0);
SurfaceControl[] excludeLayers;
final WindowState imeWindow = source.getDisplayContent().mInputMethodWindow;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f843b7c..c3cd3ec 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5296,6 +5296,10 @@
if (isCollecting) {
mTransitionController.collect(this);
} else {
+ // Failsafe to make sure that we show any activities that were incorrectly hidden
+ // during a transition. If this vis-change is a result of finishing, ignore it.
+ // Finish should only ever commit visibility=false, so we can check full containment
+ // rather than just direct membership.
inFinishingTransition = mTransitionController.inFinishingTransition(this);
if (!inFinishingTransition && !mDisplayContent.isSleeping()) {
Slog.e(TAG, "setVisibility=" + visible
@@ -5396,7 +5400,9 @@
return;
}
if (inFinishingTransition) {
- // Let the finishing transition commit the visibility.
+ // Let the finishing transition commit the visibility, but let the controller know
+ // about it so that we can recover from degenerate cases.
+ mTransitionController.mValidateCommitVis.add(this);
return;
}
// If we are preparing an app transition, then delay changing
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 4949ebc..3f4a775 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -755,4 +755,10 @@
/** Unregister a task stack listener so that it stops receiving callbacks. */;
public abstract void unregisterTaskStackListener(ITaskStackListener listener);
+
+ /**
+ * Gets the id of the display the activity was launched on.
+ * @param token The activity token.
+ */
+ public abstract int getDisplayId(IBinder token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index b377032..1f4606b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5816,6 +5816,18 @@
}
@Override
+ public int getDisplayId(IBinder token) {
+ synchronized (mGlobalLock) {
+ ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException(
+ "setFocusedActivity: No activity record matching token=" + token);
+ }
+ return r.getDisplayId();
+ }
+ }
+
+ @Override
public void registerScreenObserver(ScreenObserver observer) {
mScreenObservers.add(observer);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ef38e89..8bca106 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6045,7 +6045,7 @@
static boolean alwaysCreateRootTask(int windowingMode, int activityType) {
// Always create a root task for fullscreen, freeform, and multi windowing
// modes so that we can manage visual ordering and return types correctly.
- return activityType == ACTIVITY_TYPE_STANDARD
+ return (activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_RECENTS)
&& (windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_FREEFORM
|| windowingMode == WINDOWING_MODE_PINNED
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 747819e9..ce43628 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1882,6 +1882,12 @@
static final int DECOR_TYPES = Type.displayCutout() | Type.navigationBars();
+ /**
+ * The types that may affect display configuration. This excludes cutout because it is
+ * known from display info.
+ */
+ static final int CONFIG_TYPES = Type.statusBars() | Type.navigationBars();
+
private final DisplayContent mDisplayContent;
private final Info[] mInfoForRotation = new Info[4];
final Info mTmpInfo = new Info();
@@ -1921,7 +1927,7 @@
final DecorInsets.Info newInfo = mDecorInsets.mTmpInfo;
newInfo.update(mDisplayContent, rotation, dw, dh);
final DecorInsets.Info currentInfo = getDecorInsetsInfo(rotation, dw, dh);
- if (newInfo.mNonDecorFrame.equals(currentInfo.mNonDecorFrame)) {
+ if (newInfo.mConfigFrame.equals(currentInfo.mConfigFrame)) {
return false;
}
mDecorInsets.invalidate();
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index a5e652c..a3d233d 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -123,6 +123,7 @@
public final boolean isDefaultDisplay;
private final boolean mSupportAutoRotation;
+ private final boolean mAllowRotationResolver;
private final int mLidOpenRotation;
private final int mCarDockRotation;
private final int mDeskDockRotation;
@@ -272,6 +273,8 @@
mSupportAutoRotation =
mContext.getResources().getBoolean(R.bool.config_supportAutoRotation);
+ mAllowRotationResolver =
+ mContext.getResources().getBoolean(R.bool.config_allowRotationResolver);
mLidOpenRotation = readRotation(R.integer.config_lidOpenRotation);
mCarDockRotation = readRotation(R.integer.config_carDockRotation);
mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
@@ -2041,7 +2044,8 @@
@Override
public boolean isRotationResolverEnabled() {
- return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+ return mAllowRotationResolver
+ && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
&& mCameraRotationMode == CAMERA_ROTATION_ENABLED
&& !mService.mPowerManager.isPowerSaveMode();
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
index 1fb97f9..2fb9869 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java
@@ -114,7 +114,7 @@
* @see DisplayWindowPolicyController#keepActivityOnWindowFlagsChanged(ActivityInfo, int, int)
*/
boolean keepActivityOnWindowFlagsChanged(ActivityInfo aInfo, int flagChanges,
- int privateFlagChanges) {
+ int privateFlagChanges, int flagValues, int privateFlagValues) {
if (mDisplayWindowPolicyController == null) {
return true;
}
@@ -125,7 +125,7 @@
}
return mDisplayWindowPolicyController.keepActivityOnWindowFlagsChanged(
- aInfo, flagChanges, privateFlagChanges);
+ aInfo, flagValues, privateFlagValues);
}
/** Update the top activity and the uids of non-finishing activity */
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f5079d3..e47787e 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -33,6 +33,7 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Process.SYSTEM_UID;
+import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
@@ -209,7 +210,8 @@
private final PointerEventListener mListener = new PointerEventListener() {
@Override
public void onPointerEvent(MotionEvent ev) {
- if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN) {
+ if (!mFreezeTaskListReordering || ev.getAction() != MotionEvent.ACTION_DOWN
+ || ev.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE) {
// Skip if we aren't freezing or starting a gesture
return;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3f4296a..d3edeae 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3239,7 +3239,7 @@
if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId)
!= null) {
mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
- task.getTaskInfo());
+ task.getTaskInfo(), userId);
}
}, true /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 49d064f..9324e29 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -144,7 +144,7 @@
};
private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
- l.onTaskProfileLocked((RunningTaskInfo) m.obj);
+ l.onTaskProfileLocked((RunningTaskInfo) m.obj, m.arg1);
};
private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
@@ -467,9 +467,9 @@
* Notify listeners that the task has been put in a locked state because one or more of the
* activities inside it belong to a managed profile user that has been locked.
*/
- void notifyTaskProfileLocked(ActivityManager.RunningTaskInfo taskInfo) {
+ void notifyTaskProfileLocked(RunningTaskInfo taskInfo, int userId) {
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG,
- taskInfo);
+ userId, 0, taskInfo);
forAllLocalListeners(mNotifyTaskProfileLocked, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index abc9f8a..0f74aeb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -55,6 +55,7 @@
import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
@@ -65,12 +66,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
@@ -85,6 +88,7 @@
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -185,7 +189,7 @@
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
/** The final animation targets derived from participants after promotion. */
- private ArrayList<ChangeInfo> mTargets;
+ ArrayList<ChangeInfo> mTargets;
/** The displays that this transition is running on. */
private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
@@ -244,6 +248,16 @@
private IContainerFreezer mContainerFreezer = null;
private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+ /**
+ * {@code true} if some other operation may have caused the originally-recorded state (in
+ * mChanges) to be dirty. This is usually due to finishTransition being called mid-collect;
+ * and, the reason that finish can alter the "start" state of other transitions is because
+ * setVisible(false) is deferred until then.
+ * Instead of adding this conditional, we could re-check always; but, this situation isn't
+ * common so it'd be wasted work.
+ */
+ boolean mPriorVisibilityMightBeDirty = false;
+
final TransitionController.Logger mLogger = new TransitionController.Logger();
/** Whether this transition was forced to play early (eg for a SLEEP signal). */
@@ -261,9 +275,14 @@
/** Any 2 transitions of this type can run in parallel with each other. Used for testing. */
static final int PARALLEL_TYPE_MUTUAL = 1;
+ /** This is a recents transition. */
+ static final int PARALLEL_TYPE_RECENTS = 2;
+
+
@IntDef(prefix = { "PARALLEL_TYPE_" }, value = {
PARALLEL_TYPE_NONE,
- PARALLEL_TYPE_MUTUAL
+ PARALLEL_TYPE_MUTUAL,
+ PARALLEL_TYPE_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
@interface ParallelType {}
@@ -318,6 +337,21 @@
mFlags |= flag;
}
+ void calcParallelCollectType(WindowContainerTransaction wct) {
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp hop = wct.getHierarchyOps().get(i);
+ if (hop.getType() != HIERARCHY_OP_TYPE_PENDING_INTENT) continue;
+ final Bundle b = hop.getLaunchOptions();
+ if (b == null || b.isEmpty()) continue;
+ final boolean transientLaunch = b.getBoolean(ActivityOptions.KEY_TRANSIENT_LAUNCH);
+ if (transientLaunch) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Starting a Recents transition which can be parallel.");
+ mParallelCollectType = PARALLEL_TYPE_RECENTS;
+ }
+ }
+ }
+
/** Records an activity as transient-launch. This activity must be already collected. */
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
if (mTransientLaunches == null) {
@@ -370,6 +404,10 @@
return false;
}
+ boolean hasTransientLaunch() {
+ return mTransientLaunches != null && !mTransientLaunches.isEmpty();
+ }
+
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
}
@@ -966,28 +1004,37 @@
mController.mFinishingTransition = this;
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
- // Record all the now-hiding activities so that they are committed after
- // recalculating visibilities. We just use mParticipants because we can and it will
- // ensure proper reporting of `isInFinishTransition`.
- for (int i = 0; i < mTransientHideTasks.size(); ++i) {
- mTransientHideTasks.get(i).forAllActivities(r -> {
- // Only check leaf-tasks that were collected
- if (!mParticipants.contains(r.getTask())) return;
- // Only concern ourselves with anything that can become invisible
- if (!r.isVisible()) return;
- mParticipants.add(r);
- });
- }
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
// step can continue to commit the visibility.
mController.mAtm.mRootWindowContainer.ensureActivitiesVisible(null /* starting */,
0 /* configChanges */, true /* preserveWindows */);
+ // Record all the now-hiding activities so that they are committed. Just use
+ // mParticipants because we can avoid a new list this way.
+ for (int i = 0; i < mTransientHideTasks.size(); ++i) {
+ final Task rootTask = mTransientHideTasks.get(i);
+ rootTask.forAllActivities(r -> {
+ // Only check leaf-tasks that were collected
+ if (!mParticipants.contains(r.getTask())) return;
+ if (rootTask.isVisibleRequested()) {
+ // This transient-hide didn't hide, so don't commit anything (otherwise we
+ // could prematurely commit invisible on unrelated activities). To be safe,
+ // though, notify the controller to prevent degenerate cases.
+ if (!r.isVisibleRequested()) {
+ mController.mValidateCommitVis.add(r);
+ }
+ return;
+ }
+ // This did hide: commit immediately so that other transitions know about it.
+ mParticipants.add(r);
+ });
+ }
}
boolean hasParticipatedDisplay = false;
boolean hasVisibleTransientLaunch = false;
boolean enterAutoPip = false;
+ boolean committedSomeInvisible = false;
// Commit all going-invisible containers
for (int i = 0; i < mParticipants.size(); ++i) {
final WindowContainer<?> participant = mParticipants.valueAt(i);
@@ -1023,6 +1070,7 @@
}
ar.commitVisibility(false /* visible */, false /* performLayout */,
true /* fromTransition */);
+ committedSomeInvisible = true;
} else {
enterAutoPip = true;
}
@@ -1081,6 +1129,9 @@
}
}
}
+ if (committedSomeInvisible) {
+ mController.onCommittedInvisibles();
+ }
if (hasVisibleTransientLaunch) {
// Notify the change about the transient-below task if entering auto-pip.
@@ -1293,6 +1344,9 @@
// leftover order changes.
collectOrderChanges(mController.mWaitingTransitions.isEmpty());
+ if (mPriorVisibilityMightBeDirty) {
+ updatePriorVisibility();
+ }
// Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
// Check whether the participants were animated from back navigation.
@@ -1806,6 +1860,20 @@
}
}
+ private void updatePriorVisibility() {
+ for (int i = 0; i < mChanges.size(); ++i) {
+ final ChangeInfo chg = mChanges.valueAt(i);
+ // For task/activity, recalculate the current "real" visibility.
+ if (chg.mContainer.asActivityRecord() == null && chg.mContainer.asTask() == null) {
+ continue;
+ }
+ // This ONLY works in the visible -> invisible case (and is only needed for this case)
+ // because commitVisible(false) is deferred until finish.
+ if (!chg.mVisible) continue;
+ chg.mVisible = chg.mContainer.isVisible();
+ }
+ }
+
/**
* Under some conditions (eg. all visible targets within a parent container are transitioning
* the same way) the transition can be "promoted" to the parent container. This means an
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 7972637..e1da91a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -133,6 +133,13 @@
final ArrayList<Runnable> mStateValidators = new ArrayList<>();
/**
+ * List of activity-records whose visibility changed outside the main/tracked part of a
+ * transition (eg. in the finish-transaction). These will be checked when idle to recover from
+ * degenerate states.
+ */
+ final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -408,9 +415,9 @@
return false;
}
- /** Returns {@code true} if the `wc` is a participant of the finishing transition. */
+ /** Returns {@code true} if the finishing transition contains `wc`. */
boolean inFinishingTransition(WindowContainer<?> wc) {
- return mFinishingTransition != null && mFinishingTransition.mParticipants.contains(wc);
+ return mFinishingTransition != null && mFinishingTransition.isInTransition(wc);
}
/** @return {@code true} if a transition is running */
@@ -827,6 +834,16 @@
}
}
+ /** Called by {@link Transition#finishTransition} if it committed invisible to any activities */
+ void onCommittedInvisibles() {
+ if (mCollectingTransition != null) {
+ mCollectingTransition.mPriorVisibilityMightBeDirty = true;
+ }
+ for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
+ mWaitingTransitions.get(i).mPriorVisibilityMightBeDirty = true;
+ }
+ }
+
private void validateStates() {
for (int i = 0; i < mStateValidators.size(); ++i) {
mStateValidators.get(i).run();
@@ -838,6 +855,15 @@
}
}
mStateValidators.clear();
+ for (int i = 0; i < mValidateCommitVis.size(); ++i) {
+ final ActivityRecord ar = mValidateCommitVis.get(i);
+ if (!ar.isVisibleRequested() && ar.isVisible()) {
+ Slog.e(TAG, "Uncommitted visibility change: " + ar);
+ ar.commitVisibility(ar.isVisibleRequested(), false /* layout */,
+ false /* fromTransition */);
+ }
+ }
+ mValidateCommitVis.clear();
}
/**
@@ -869,6 +895,8 @@
// If it's a legacy sync, then it needs to wait until there is no collecting transition.
if (queued.mTransition == null) return;
if (!canStartCollectingNow(queued.mTransition)) return;
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from collecting"
+ + " to waiting.", mCollectingTransition.getSyncId());
mWaitingTransitions.add(mCollectingTransition);
mCollectingTransition = null;
} else if (mSyncEngine.hasActiveSync()) {
@@ -920,10 +948,37 @@
* in {@link #getIsIndependent} later.
*/
boolean getCanBeIndependent(Transition collecting, Transition queued) {
+ // For tests
if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
&& collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
return true;
}
+ // For recents
+ if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // Must serialize with itself.
+ return false;
+ }
+ // allow this if `collecting` only has activities
+ for (int i = 0; i < collecting.mParticipants.size(); ++i) {
+ final WindowContainer wc = collecting.mParticipants.valueAt(i);
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
+ // Is task or above, so can't be independent
+ return false;
+ }
+ if (ar != null && ar.isActivityTypeHomeOrRecents()) {
+ // It's a recents or home type, so it conflicts.
+ return false;
+ }
+ }
+ return true;
+ } else if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // We can collect simultaneously with recents if it is populated. This is because
+ // we know that recents will not collect/trampoline any more stuff. If anything in the
+ // queued transition overlaps, it will end up just waiting in sync-queue anyways.
+ return true;
+ }
return false;
}
@@ -932,11 +987,47 @@
* `running` is playing based on its current state.
*/
static boolean getIsIndependent(Transition running, Transition incoming) {
+ // For tests
if (running.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL
&& incoming.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) {
return true;
}
- return false;
+ // For now there's only one mutually-independent pair: an all activity-level transition and
+ // a transient-launch where none of the activities are part of the transient-launch task,
+ // so the following logic is hard-coded specifically for this.
+ // Also, we currently restrict valid transient-launches to just recents.
+ final Transition recents;
+ final Transition other;
+ if (running.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
+ && running.hasTransientLaunch()) {
+ if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) {
+ // Recents can't be independent from itself.
+ return false;
+ }
+ recents = running;
+ other = incoming;
+ } else if (incoming.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS
+ && incoming.hasTransientLaunch()) {
+ recents = incoming;
+ other = running;
+ } else {
+ return false;
+ }
+ // Check against *targets* because that is the post-promotion set of containers that are
+ // actually animating.
+ for (int i = 0; i < other.mTargets.size(); ++i) {
+ final WindowContainer wc = other.mTargets.get(i).mContainer;
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (ar == null && wc.asWindowState() == null && wc.asWindowToken() == null) {
+ // Is task or above, so for now don't let them be independent.
+ return false;
+ }
+ if (ar != null && recents.isTransientLaunch(ar)) {
+ // Change overlaps with recents, so serialize.
+ return false;
+ }
+ }
+ return true;
}
void assignTrack(Transition transition, TransitionInfo info) {
@@ -960,9 +1051,15 @@
if (track < 0) {
// Didn't overlap with anything, so give it its own track
track = mTrackCount;
+ if (track > 0) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Playing #%d in parallel on "
+ + "track #%d", transition.getSyncId(), track);
+ }
}
if (sync) {
info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.",
+ transition.getSyncId());
}
transition.mAnimationTrack = track;
info.setTrack(track);
@@ -1135,6 +1232,8 @@
// Check if we can run in parallel here.
if (canStartCollectingNow(transit)) {
// start running in parallel.
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from"
+ + " collecting to waiting.", mCollectingTransition.getSyncId());
mWaitingTransitions.add(mCollectingTransition);
mCollectingTransition = null;
moveToCollecting(transit);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f4a1765d..a5cdd0b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3594,8 +3594,11 @@
&& !mTransitionController.useShellTransitionsRotation()) {
if (deltaRotation != Surface.ROTATION_0) {
updateSurfaceRotation(t, deltaRotation, null /* positionLeash */);
+ t.setFixedTransformHint(mSurfaceControl,
+ getWindowConfiguration().getDisplayRotation());
} else if (deltaRotation != mLastDeltaRotation) {
t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
+ t.unsetFixedTransformHint(mSurfaceControl);
}
}
mLastDeltaRotation = deltaRotation;
@@ -3839,7 +3842,8 @@
// This can still happen if WMCore starts a new transition when there is ongoing
// sync transaction from Shell. Please file a bug if it happens.
throw new IllegalStateException("Can't sync on 2 groups simultaneously"
- + " currentSyncId=" + mSyncGroup.mSyncId + " newSyncId=" + group.mSyncId);
+ + " currentSyncId=" + mSyncGroup.mSyncId + " newSyncId=" + group.mSyncId
+ + " wc=" + this);
}
mSyncGroup = group;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 40b8274..f9b6fc1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1831,7 +1831,7 @@
boolean needToSendNewConfiguration =
win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
- if (win.providesNonDecorInsets()) {
+ if (win.providesDisplayDecorInsets()) {
needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
}
if (needToSendNewConfiguration) {
@@ -2274,7 +2274,7 @@
& WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
- if (layoutChanged && win.providesNonDecorInsets()) {
+ if (layoutChanged && win.providesDisplayDecorInsets()) {
configChanged = displayPolicy.updateDecorInsetsInfo();
}
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
@@ -2290,14 +2290,24 @@
winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
}
- if (win.mActivityRecord != null
- && !displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
- win.mActivityRecord.info, flagChanges, privateFlagChanges)) {
- mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
- win.mActivityRecord.getTask()));
- Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
- + " can't remain on display " + displayContent.getDisplayId());
- return 0;
+ // See if the DisplayWindowPolicyController wants to keep the activity on the window
+ if (displayContent.mDwpcHelper.hasController()
+ && win.mActivityRecord != null && (!win.mRelayoutCalled || flagChanges != 0
+ || privateFlagChanges != 0)) {
+ int newOrChangedFlags = !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges;
+ int newOrChangedPrivateFlags =
+ !win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges;
+
+ if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
+ win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags,
+ win.mAttrs.flags,
+ win.mAttrs.privateFlags)) {
+ mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
+ win.mActivityRecord.getTask()));
+ Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
+ + " can't remain on display " + displayContent.getDisplayId());
+ return 0;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index cd42528..d5aa520 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -299,6 +299,7 @@
final boolean needsSetReady = t != null;
final Transition nextTransition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
+ nextTransition.calcParallelCollectType(wct);
mTransitionController.startCollectOrQueue(nextTransition,
(deferred) -> {
nextTransition.start();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2920652..032f08a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1834,13 +1834,13 @@
return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
}
- boolean providesNonDecorInsets() {
+ boolean providesDisplayDecorInsets() {
if (mInsetsSourceProviders == null) {
return false;
}
for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) {
final InsetsSource source = mInsetsSourceProviders.valueAt(i).getSource();
- if (source.getType() == WindowInsets.Type.navigationBars()) {
+ if ((source.getType() & DisplayPolicy.DecorInsets.CONFIG_TYPES) != 0) {
return true;
}
}
@@ -1980,19 +1980,16 @@
/**
* Like isOnScreen(), but we don't return true if the window is part
- * of a transition but has not yet started animating.
+ * of a transition that has not yet been started.
*/
boolean isReadyForDisplay() {
- if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
- return false;
- }
- if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()
- && !isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
+ if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
return false;
}
final boolean parentAndClientVisible = !isParentWindowHidden()
&& mViewVisibility == View.VISIBLE && mToken.isVisible();
- return parentAndClientVisible || isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL);
+ return mHasSurface && isVisibleByPolicy() && !mDestroying
+ && (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
boolean isFullyTransparent() {
@@ -2507,13 +2504,13 @@
}
// Check if window provides non decor insets before clearing its provided insets.
- final boolean windowProvidesNonDecorInsets = providesNonDecorInsets();
+ final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
removeImmediately();
// Removing a visible window may affect the display orientation so just update it if
// needed. Also recompute configuration if it provides screen decor insets.
boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
- if (windowProvidesNonDecorInsets) {
+ if (windowProvidesDisplayDecorInsets) {
needToSendNewConfiguration |=
displayContent.getDisplayPolicy().updateDecorInsetsInfo();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index da54b15..4c5efef 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -596,6 +596,7 @@
.build();
t.setPosition(leash, mLastSurfacePosition.x, mLastSurfacePosition.y);
t.reparent(getSurfaceControl(), leash);
+ t.setFixedTransformHint(leash, getWindowConfiguration().getDisplayRotation());
mFixedRotationTransformLeash = leash;
updateSurfaceRotation(t, rotation, mFixedRotationTransformLeash);
return mFixedRotationTransformLeash;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index cf57b33..369c974 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2567,6 +2567,11 @@
im->setStylusPointerIconEnabled(enabled);
}
+static void nativeNotifyKeyGestureTimeoutsChanged(JNIEnv* env, jobject nativeImplObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->getInputManager()->getDispatcher().requestRefreshConfiguration();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -2663,6 +2668,7 @@
(void*)nativeSetStylusButtonMotionEventsEnabled},
{"getMouseCursorPosition", "()[F", (void*)nativeGetMouseCursorPosition},
{"setStylusPointerIconEnabled", "(Z)V", (void*)nativeSetStylusPointerIconEnabled},
+ {"notifyKeyGestureTimeoutsChanged", "()V", (void*)nativeNotifyKeyGestureTimeoutsChanged},
};
#define FIND_CLASS(var, className) \
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index a8d2f4e..a8806b5 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -84,23 +84,26 @@
return result;
}
+static int nativeSetTvMessageEnabled(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId,
+ jint streamId, jint type, jboolean enabled) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ return tvInputHal->setTvMessageEnabled(deviceId, streamId, type, enabled);
+}
+
static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
delete tvInputHal;
}
static const JNINativeMethod gTvInputHalMethods[] = {
- /* name, signature, funcPtr */
- { "nativeOpen", "(Landroid/os/MessageQueue;)J",
- (void*) nativeOpen },
- { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
- (void*) nativeAddOrUpdateStream },
- { "nativeRemoveStream", "(JII)I",
- (void*) nativeRemoveStream },
- { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
- (void*) nativeGetStreamConfigs },
- { "nativeClose", "(J)V",
- (void*) nativeClose },
+ /* name, signature, funcPtr */
+ {"nativeOpen", "(Landroid/os/MessageQueue;)J", (void*)nativeOpen},
+ {"nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I", (void*)nativeAddOrUpdateStream},
+ {"nativeRemoveStream", "(JII)I", (void*)nativeRemoveStream},
+ {"nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
+ (void*)nativeGetStreamConfigs},
+ {"nativeSetTvMessageEnabled", "(JIIIZ)I", (void*)nativeSetTvMessageEnabled},
+ {"nativeClose", "(J)V", (void*)nativeClose},
};
#define FIND_CLASS(var, className) \
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 98e6b19..6bb5217 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -144,6 +144,17 @@
return NO_ERROR;
}
+int JTvInputHal::setTvMessageEnabled(int deviceId, int streamId, int type, bool enabled) {
+ Mutex::Autolock autoLock(&mLock);
+ if (!mTvInput->setTvMessageEnabled(deviceId, streamId,
+ static_cast<AidlTvMessageEventType>(type), enabled)
+ .isOk()) {
+ ALOGE("Error in setTvMessageEnabled. device id:%d stream id:%d", deviceId, streamId);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
const std::vector<AidlTvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
std::vector<AidlTvStreamConfig> list;
::ndk::ScopedAStatus status = mTvInput->getStreamConfigurations(deviceId, &list);
@@ -384,4 +395,15 @@
}
}
+::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setTvMessageEnabled(int32_t deviceId,
+ int32_t streamId,
+ TvMessageEventType in_type,
+ bool enabled) {
+ if (mIsHidl) {
+ return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ } else {
+ return mAidlTvInput->setTvMessageEnabled(deviceId, streamId, in_type, enabled);
+ }
+}
+
} // namespace android
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 984407a..e29da79 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -44,6 +44,8 @@
using ::aidl::android::hardware::tv::input::CableConnectionStatus;
using ::aidl::android::hardware::tv::input::TvInputEventType;
using ::aidl::android::hardware::tv::input::TvInputType;
+using ::aidl::android::hardware::tv::input::TvMessageEvent;
+using ::aidl::android::hardware::tv::input::TvMessageEventType;
using AidlAudioDevice = ::aidl::android::media::audio::common::AudioDevice;
using AidlAudioDeviceAddress = ::aidl::android::media::audio::common::AudioDeviceAddress;
@@ -53,6 +55,7 @@
using AidlTvInputDeviceInfo = ::aidl::android::hardware::tv::input::TvInputDeviceInfo;
using AidlTvInputEvent = ::aidl::android::hardware::tv::input::TvInputEvent;
using AidlTvMessageEvent = ::aidl::android::hardware::tv::input::TvMessageEvent;
+using AidlTvMessageEventType = ::aidl::android::hardware::tv::input::TvMessageEventType;
using AidlTvStreamConfig = ::aidl::android::hardware::tv::input::TvStreamConfig;
extern gTvInputHalClassInfoType gTvInputHalClassInfo;
@@ -69,6 +72,7 @@
static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
+ int setTvMessageEnabled(int deviceId, int streamId, int type, bool enabled);
int removeStream(int deviceId, int streamId);
const std::vector<AidlTvStreamConfig> getStreamConfigs(int deviceId);
@@ -150,6 +154,8 @@
::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId,
AidlNativeHandle* _aidl_return);
::ndk::ScopedAStatus closeStream(int32_t in_deviceId, int32_t in_streamId);
+ ::ndk::ScopedAStatus setTvMessageEnabled(int32_t deviceId, int32_t streamId,
+ TvMessageEventType in_type, bool enabled);
private:
::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index fc7fd1a..b073ff4 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -98,7 +98,9 @@
mRequestId, mClientRequest,
mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
- Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+ Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
+ // TODO(b/279480457): populate
+ /*defaultProviderId=*/new ArrayList<>()),
providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 50e5163..4e82ee7 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -35,6 +35,7 @@
/**
* For all future metric additions, this will contain their names for local usage after importing
* from {@link com.android.internal.util.FrameworkStatsLog}.
+ * TODO(b/271135048) - Emit all atoms, including all V4 atoms (specifically the rest of track 1).
*/
public class MetricUtilities {
private static final boolean LOG_FLAG = true;
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 520b937..40514b2 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -75,7 +75,8 @@
CreateCredentialRequest providerCreateRequest =
createProviderRequest(providerInfo.getCapabilities(),
createRequestSession.mClientRequest,
- createRequestSession.mClientAppInfo);
+ createRequestSession.mClientAppInfo,
+ providerInfo.isSystemProvider());
if (providerCreateRequest != null) {
return new ProviderCreateSession(
context,
@@ -114,9 +115,16 @@
}
@Nullable
- private static CreateCredentialRequest createProviderRequest(List<String> providerCapabilities,
+ private static CreateCredentialRequest createProviderRequest(
+ List<String> providerCapabilities,
android.credentials.CreateCredentialRequest clientRequest,
- CallingAppInfo callingAppInfo) {
+ CallingAppInfo callingAppInfo,
+ boolean isSystemProvider) {
+ if (clientRequest.isSystemProviderRequired() && !isSystemProvider) {
+ // Request requires system provider but this session does not correspond to a
+ // system service
+ return null;
+ }
String capability = clientRequest.getType();
if (providerCapabilities.contains(capability)) {
return new CreateCredentialRequest(callingAppInfo, capability,
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index a62d9e8..0c3d2a4 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -40,7 +40,6 @@
import android.service.credentials.CredentialProviderService;
import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -413,11 +412,9 @@
*/
private boolean onAuthenticationEntrySelected(
@Nullable ProviderPendingIntentResponse providerPendingIntentResponse) {
- Log.i(TAG, "onAuthenticationEntrySelected");
// Authentication entry is expected to have a BeginGetCredentialResponse instance. If it
// does not have it, we remove the authentication entry and do not add any more content.
if (providerPendingIntentResponse == null) {
- Log.i(TAG, "providerPendingIntentResponse is null");
// Nothing received. This is equivalent to no content received.
return false;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index c10f564..ead86ce 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -266,7 +266,7 @@
.collect(Collectors.toList());
updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED,
/*source=*/ CredentialsSource.REGISTRY);
- // TODO(use metric later)
+ // TODO(b/273353677) : metric should be emitted similarly to sibling classes
}
@Nullable
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index d02a8c1..73fdc1c 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -268,12 +268,9 @@
/*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
- } catch (SecurityException e) {
+ } catch (SecurityException | PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e);
return false;
- } catch (PackageManager.NameNotFoundException e) {
- Slog.i(TAG, "Error getting info for " + mComponentName.flattenToString(), e);
- return false;
}
return false;
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
index 721d3d7..b212606 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -30,7 +30,6 @@
* 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 CandidatePhaseMetric {
@@ -56,10 +55,7 @@
private int mProviderQueryStatus = -1;
// Indicates if an exception was thrown by this provider, false by default
private boolean mHasException = false;
- // Indicates the number of total entries available. We can also locally store the entries, but
- // cannot emit them in the current split form. TODO(b/271135048) - possibly readjust candidate
- // entries. Also, it may be okay to remove this and instead aggregate from inner counts.
- // Defaults to -1
+ // Indicates the number of total entries available, defaults to -1
private int mNumEntriesTotal = -1;
// The count of action entries from this provider, defaults to -1
private int mActionEntryCount = -1;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
index c80cc24..8f08bb0 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
@@ -29,11 +29,8 @@
* 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 ChosenProviderFinalPhaseMetric {
-
- // TODO(b/270403549) - applies elsewhere, likely removed or replaced w/ some hashed/count index
private static final String TAG = "ChosenFinalPhaseMetric";
// The session id associated with this API call, used to unite split emits
private int mSessionId = -1;
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 0ecd9cc..5cfb0e7 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -26,7 +26,6 @@
* 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 InitialPhaseMetric {
private static final String TAG = "InitialPhaseMetric";
@@ -47,7 +46,6 @@
private long mCredentialServiceBeginQueryTimeNanoseconds = -1;
// Indicates if the origin was specified when making this API request
- // TODO(b/271135048) - Emit once metrics approved
private boolean mOriginSpecified = false;
// Stores the deduped request information, particularly {"req":5}.
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index 547c09a..4ecdfef 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -337,10 +337,6 @@
*/
public void logApiCalledAtFinish(int apiStatus) {
try {
- // TODO (b/270403549) - this browsing phase object is fine but also have a new emit
- // For the returned types by authentication entries - i.e. a CandidatePhase During
- // Browse
- // Possibly think of adding in more atoms for other APIs as well.
logApiCalledFinalPhase(mChosenProviderFinalPhaseMetric, mCandidateBrowsingPhaseMetric,
apiStatus,
++mSequenceCounter);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 80100a9..c681b88 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -19,13 +19,13 @@
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -55,8 +55,7 @@
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
@GuardedBy("mLock")
- private List<String> mLauncherShortcutOverrides =
- new ArrayList<>();
+ private ArrayMap<String, String> mLauncherShortcutOverrides = new ArrayMap<>();
/** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}. */
@@ -130,18 +129,20 @@
}
@Override
- public List<String> getLauncherShortcutOverrides() {
+ public Map<String, String> getLauncherShortcutOverrides() {
synchronized (mLock) {
- return new ArrayList<>(mLauncherShortcutOverrides);
+ return new ArrayMap<>(mLauncherShortcutOverrides);
}
}
/**
- * Sets a list of packages for which shortcuts should be replaced by their badged version.
+ * Sets a map of packages names to package names, for which all launcher shortcuts which
+ * match a key package name should be modified to launch the corresponding value package
+ * name in the managed profile. The overridden shortcut should be badged accordingly.
*/
- public void setLauncherShortcutOverrides(List<String> launcherShortcutOverrides) {
+ public void setLauncherShortcutOverrides(ArrayMap<String, String> launcherShortcutOverrides) {
synchronized (mLock) {
- mLauncherShortcutOverrides = new ArrayList<>(launcherShortcutOverrides);
+ mLauncherShortcutOverrides = new ArrayMap<>(launcherShortcutOverrides);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 415440b..cf49dcf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -130,12 +130,11 @@
<V> void setLocalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
- @NonNull PolicyValue<V> value,
+ @Nullable PolicyValue<V> value,
int userId,
boolean skipEnforcePolicy) {
Objects.requireNonNull(policyDefinition);
Objects.requireNonNull(enforcingAdmin);
- Objects.requireNonNull(value);
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ebe7e0c..675ebd3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -80,6 +80,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WINDOWS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
+import static android.Manifest.permission.MASTER_CLEAR;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
@@ -520,6 +521,7 @@
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.LocalDate;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -532,6 +534,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -567,7 +570,15 @@
private static final int REQUEST_PROFILE_OFF_DEADLINE = 5572;
+ // Binary XML serializer doesn't support longer strings
+ private static final int MAX_POLICY_STRING_LENGTH = 65535;
+ // FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE, Android packages are used in dir names.
+ private static final int MAX_PACKAGE_NAME_LENGTH = 223;
+
private static final int MAX_PROFILE_NAME_LENGTH = 200;
+ private static final int MAX_LONG_SUPPORT_MESSAGE_LENGTH = 20000;
+ private static final int MAX_SHORT_SUPPORT_MESSAGE_LENGTH = 200;
+ private static final int MAX_ORG_NAME_LENGTH = 200;
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
@@ -863,7 +874,7 @@
// TODO(b/265683382) remove the flag after rollout.
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
- private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
+ private static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = true;
private static final String ENABLE_WORK_PROFILE_TELEPHONY_FLAG =
"enable_work_profile_telephony";
@@ -6040,7 +6051,7 @@
@Override
public void lockNow(int flags, String callerPackageName, boolean parent) {
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(callerPackageName);
} else {
caller = getCallerIdentity();
@@ -6052,7 +6063,7 @@
ActiveAdmin admin;
// Make sure the caller has any active admin with the right policy or
// the required permission.
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
admin = enforcePermissionAndGetEnforcingAdmin(
/* admin= */ null,
/* permission= */ MANAGE_DEVICE_POLICY_LOCK,
@@ -7552,9 +7563,9 @@
boolean calledByProfileOwnerOnOrgOwnedDevice =
isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId());
if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
/*admin=*/ null,
- /*permission= */ MANAGE_DEVICE_POLICY_WIPE_DATA,
+ /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, MASTER_CLEAR},
USES_POLICY_WIPE_DATA,
caller.getPackageName(),
factoryReset ? UserHandle.USER_ALL : getAffectedUser(calledOnParentInstance));
@@ -7576,12 +7587,13 @@
admin = getActiveAdminWithPolicyForUidLocked(/* who= */ null,
DeviceAdminInfo.USES_POLICY_WIPE_DATA, caller.getUid());
}
+ Preconditions.checkCallAuthorization(
+ (admin != null) || hasCallingOrSelfPermission(permission.MASTER_CLEAR),
+ "No active admin for user %d and caller %d does not hold MASTER_CLEAR "
+ + "permission",
+ caller.getUserId(), caller.getUid());
}
- Preconditions.checkCallAuthorization(
- (admin != null) || hasCallingOrSelfPermission(permission.MASTER_CLEAR),
- "No active admin for user %d and caller %d does not hold MASTER_CLEAR permission",
- caller.getUserId(), caller.getUid());
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
if (TextUtils.isEmpty(wipeReasonForUser)) {
@@ -7721,7 +7733,7 @@
}
private void clearLauncherShortcutOverrides() {
- mPolicyCache.setLauncherShortcutOverrides(new ArrayList<>());
+ mPolicyCache.setLauncherShortcutOverrides(new ArrayMap<>());
}
private void updateTelephonyCrossProfileIntentFilters(int parentUserId, int profileUserId,
@@ -7836,15 +7848,14 @@
} else {
// Explicit behaviour
if (factoryReset) {
- // TODO(b/254031494) Replace with new factory reset permission checks
- if (!isPermissionCheckFlagEnabled()) {
- boolean hasPermission = isDeviceOwnerUserId(userId)
- || (isOrganizationOwnedDeviceWithManagedProfile()
- && calledOnParentInstance);
- Preconditions.checkCallAuthorization(hasPermission,
- "Admin %s does not have permission to factory reset the device.",
- userId);
- }
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+ /*admin=*/ null,
+ /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA,
+ MASTER_CLEAR},
+ USES_POLICY_WIPE_DATA,
+ adminPackage,
+ factoryReset ? UserHandle.USER_ALL :
+ getAffectedUser(calledOnParentInstance));
wipeDevice = true;
} else {
Preconditions.checkCallAuthorization(!isSystemUser,
@@ -8906,13 +8917,13 @@
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// The effect of this policy is device-wide.
enforcePermission(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
} else {
@@ -8940,13 +8951,13 @@
return false;
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforceCanQuery(SET_TIME, caller.getPackageName(), UserHandle.USER_ALL);
} else {
Objects.requireNonNull(who, "ComponentName is null");
@@ -8975,7 +8986,7 @@
caller = getCallerIdentity(who);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// The effect of this policy is device-wide.
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
@@ -9015,13 +9026,13 @@
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// The effect of this policy is device-wide.
enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL);
} else {
@@ -9324,7 +9335,7 @@
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
@@ -9334,7 +9345,7 @@
final int userHandle = caller.getUserId();
int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// SUPPORT USES_POLICY_DISABLE_KEYGUARD_FEATURES
EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
who, MANAGE_DEVICE_POLICY_KEYGUARD, caller.getPackageName(),
@@ -9413,7 +9424,7 @@
synchronized (getLockObject()) {
if (who != null) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin admin = getEnforcingAdminForCaller(
who, who.getPackageName());
Integer features = mDevicePolicyEngine.getLocalPolicySetByAdmin(
@@ -9427,7 +9438,7 @@
}
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
Integer features = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.KEYGUARD_DISABLED_FEATURES,
affectedUserId);
@@ -11624,7 +11635,7 @@
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
@@ -11729,7 +11740,15 @@
}
Objects.requireNonNull(agent, "agent is null");
- int userHandle = UserHandle.getCallingUserId();
+
+ enforceMaxPackageNameLength(agent.getPackageName());
+ final String agentAsString = agent.flattenToString();
+ enforceMaxStringLength(agentAsString, "agent name");
+ if (args != null) {
+ enforceMaxStringLength(args, "args");
+ }
+
+ int userHandle = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
ActiveAdmin ap;
if (isPermissionCheckFlagEnabled()) {
@@ -11746,7 +11765,7 @@
checkCanExecuteOrThrowUnsafe(
DevicePolicyManager.OPERATION_SET_TRUST_AGENT_CONFIGURATION);
- ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
+ ap.trustAgentInfos.put(agentAsString, new TrustAgentInfo(args));
saveSettingsLocked(userHandle);
}
}
@@ -12016,6 +12035,10 @@
isDeviceOwner(caller) || isProfileOwner(caller));
if (packageList != null) {
+ for (String pkg : packageList) {
+ enforceMaxPackageNameLength(pkg);
+ }
+
int userId = caller.getUserId();
final List<AccessibilityServiceInfo> enabledServices;
long id = mInjector.binderClearCallingIdentity();
@@ -12173,7 +12196,7 @@
}
CallerIdentity caller;
- if (isPermissionCheckFlagEnabled()) {
+ if (isPolicyEngineForFinanceFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
@@ -12183,7 +12206,7 @@
int userId = getProfileParentUserIfRequested(
caller.getUserId(), calledOnParentInstance);
if (calledOnParentInstance) {
- if (!isPermissionCheckFlagEnabled()) {
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller));
}
@@ -12191,12 +12214,16 @@
"Permitted input methods must allow all input methods or only "
+ "system input methods when called on the parent instance of an "
+ "organization-owned device");
- } else if (!isPermissionCheckFlagEnabled()) {
+ } else if (!isPolicyEngineForFinanceFlagEnabled()) {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwner(caller));
}
if (packageList != null) {
+ for (String pkg : packageList) {
+ enforceMaxPackageNameLength(pkg);
+ }
+
List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
if (enabledImes != null) {
@@ -12214,17 +12241,24 @@
}
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
- admin = enforcePermissionAndGetEnforcingAdmin(
+ if (isPolicyEngineForFinanceFlagEnabled()) {
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
who, MANAGE_DEVICE_POLICY_INPUT_METHODS,
- caller.getPackageName(), userId).getActiveAdmin();
+ caller.getPackageName(), userId);
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS,
+ admin,
+ packageList == null
+ ? null
+ : new StringSetPolicyValue(new HashSet<>(packageList)),
+ userId);
} else {
- admin = getParentOfAdminIfRequired(
- getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), calledOnParentInstance);
+ ActiveAdmin admin = getParentOfAdminIfRequired(
+ getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()),
+ calledOnParentInstance);
+ admin.permittedInputMethods = packageList;
+ saveSettingsLocked(caller.getUserId());
}
- admin.permittedInputMethods = packageList;
- saveSettingsLocked(caller.getUserId());
}
DevicePolicyEventLogger
@@ -12272,19 +12306,18 @@
}
synchronized (getLockObject()) {
- ActiveAdmin admin;
- if (isPermissionCheckFlagEnabled()) {
+ if (isPolicyEngineForFinanceFlagEnabled()) {
int affectedUser = calledOnParentInstance ? getProfileParentId(
caller.getUserId()) : caller.getUserId();
- admin = enforcePermissionAndGetEnforcingAdmin(
- who, MANAGE_DEVICE_POLICY_INPUT_METHODS, caller.getPackageName(),
- affectedUser).getActiveAdmin();
+ Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+ return policy == null ? null : new ArrayList<>(policy);
} else {
- admin = getParentOfAdminIfRequired(
+ ActiveAdmin admin = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(
caller.getUserId()), calledOnParentInstance);
+ return admin.permittedInputMethods;
}
- return admin.permittedInputMethods;
}
}
@@ -12302,37 +12335,45 @@
}
private @Nullable List<String> getPermittedInputMethodsUnchecked(@UserIdInt int userId) {
- synchronized (getLockObject()) {
- List<String> result = null;
- // Only device or profile owners can have permitted lists set.
- List<ActiveAdmin> admins = getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userId);
- for (ActiveAdmin admin: admins) {
- List<String> fromAdmin = admin.permittedInputMethods;
- if (fromAdmin != null) {
- if (result == null) {
- result = new ArrayList<String>(fromAdmin);
- } else {
- result.retainAll(fromAdmin);
- }
- }
- }
-
- // If we have a permitted list add all system input methods.
- if (result != null) {
- List<InputMethodInfo> imes = InputMethodManagerInternal
- .get().getInputMethodListAsUser(userId);
- if (imes != null) {
- for (InputMethodInfo ime : imes) {
- ServiceInfo serviceInfo = ime.getServiceInfo();
- ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
- if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- result.add(serviceInfo.packageName);
+ List<String> result = null;
+ if (isPolicyEngineForFinanceFlagEnabled()) {
+ Set<String> policy = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, userId);
+ result = policy == null ? null : new ArrayList<>(policy);
+ } else {
+ synchronized (getLockObject()) {
+ // Only device or profile owners can have permitted lists set.
+ List<ActiveAdmin> admins =
+ getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
+ userId);
+ for (ActiveAdmin admin : admins) {
+ List<String> fromAdmin = admin.permittedInputMethods;
+ if (fromAdmin != null) {
+ if (result == null) {
+ result = new ArrayList<String>(fromAdmin);
+ } else {
+ result.retainAll(fromAdmin);
}
}
}
}
- return result;
}
+
+ // If we have a permitted list add all system input methods.
+ if (result != null) {
+ List<InputMethodInfo> imes = InputMethodManagerInternal
+ .get().getInputMethodListAsUser(userId);
+ if (imes != null) {
+ for (InputMethodInfo ime : imes) {
+ ServiceInfo serviceInfo = ime.getServiceInfo();
+ ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ result.add(serviceInfo.packageName);
+ }
+ }
+ }
+ }
+ return result;
}
@Override
@@ -12347,17 +12388,38 @@
String.format(NOT_SYSTEM_CALLER_MSG,
"query if an input method is disabled by admin"));
- synchronized (getLockObject()) {
- ActiveAdmin admin = getParentOfAdminIfRequired(
- getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
- if (admin == null) {
- return false;
+ if (isPolicyEngineForFinanceFlagEnabled()) {
+ int affectedUser = calledOnParentInstance ? getProfileParentId(userHandle) : userHandle;
+ Map<EnforcingAdmin, PolicyValue<Set<String>>> policies =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
+ PolicyDefinition.PERMITTED_INPUT_METHODS, affectedUser);
+ EnforcingAdmin admin = null;
+ for (EnforcingAdmin a : policies.keySet()) {
+ if (a.getPackageName().equals(who.getPackageName())) {
+ if (policies.get(a).getValue() == null) {
+ return true;
+ } else {
+ return checkPackagesInPermittedListOrSystem(
+ Collections.singletonList(packageName),
+ new ArrayList<>(policies.get(a).getValue()), affectedUser);
+ }
+ }
}
- if (admin.permittedInputMethods == null) {
- return true;
+ // Admin didn't set a policy
+ return false;
+ } else {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getParentOfAdminIfRequired(
+ getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
+ if (admin == null) {
+ return false;
+ }
+ if (admin.permittedInputMethods == null) {
+ return true;
+ }
+ return checkPackagesInPermittedListOrSystem(Collections.singletonList(packageName),
+ admin.permittedInputMethods, userHandle);
}
- return checkPackagesInPermittedListOrSystem(Collections.singletonList(packageName),
- admin.permittedInputMethods, userHandle);
}
}
@@ -13000,7 +13062,7 @@
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforceCanQueryAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
@@ -13070,7 +13132,7 @@
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
ActiveAdmin admin;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_PACKAGE_STATE,
@@ -13167,7 +13229,7 @@
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforcePermission(
MANAGE_DEVICE_POLICY_PACKAGE_STATE,
caller.getPackageName(),
@@ -13377,6 +13439,13 @@
public void setUserRestrictionGlobally(String callerPackage, String key) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
+ EnforcingAdmin admin = enforcePermissionForUserRestriction(
+ /* who= */ null,
+ key,
+ caller.getPackageName(),
+ UserHandle.USER_ALL
+ );
+
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
if (!isPolicyEngineForFinanceFlagEnabled()) {
@@ -13393,13 +13462,6 @@
throw new IllegalArgumentException("Invalid restriction key: " + key);
}
- EnforcingAdmin admin = enforcePermissionForUserRestriction(
- /* who= */ null,
- key,
- caller.getPackageName(),
- UserHandle.USER_ALL
- );
-
setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
@@ -13773,7 +13835,7 @@
boolean hidden, boolean parent) {
CallerIdentity caller = getCallerIdentity(who, callerPackage);
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// TODO: We need to ensure the delegate with DELEGATION_PACKAGE_ACCESS can do this
enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
} else {
@@ -13792,7 +13854,7 @@
boolean result;
synchronized (getLockObject()) {
if (parent) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
+ if (!isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(
caller.getUserId()) && isManagedProfile(caller.getUserId()));
@@ -13809,7 +13871,7 @@
Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
packageName, hidden, userId);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
mDevicePolicyEngine.setLocalPolicy(
PolicyDefinition.APPLICATION_HIDDEN(packageName),
@@ -13848,7 +13910,7 @@
String packageName, boolean parent) {
CallerIdentity caller = getCallerIdentity(who, callerPackage);
int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
// TODO: Also support DELEGATION_PACKAGE_ACCESS
enforcePermission(MANAGE_DEVICE_POLICY_PACKAGE_STATE, caller.getPackageName(), userId);
} else {
@@ -13860,7 +13922,7 @@
synchronized (getLockObject()) {
if (parent) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
+ if (!isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(
isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())
&& isManagedProfile(caller.getUserId()));
@@ -14048,14 +14110,17 @@
if (!mHasFeature) {
return;
}
+
+ enforceMaxStringLength(accountType, "account type");
+
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
}
synchronized (getLockObject()) {
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
int affectedUser = getAffectedUser(parent);
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
@@ -14118,7 +14183,7 @@
CallerIdentity caller;
Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
final ArraySet<String> resultSet = new ArraySet<>();
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
int affectedUser = parent ? getProfileParentId(userId) : userId;
caller = getCallerIdentity(callerPackageName);
if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
@@ -14738,6 +14803,10 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
+ for (String pkg : packages) {
+ enforceMaxPackageNameLength(pkg);
+ }
+
CallerIdentity caller;
if (isPolicyEngineForFinanceFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
@@ -15485,12 +15554,12 @@
public boolean setStatusBarDisabled(ComponentName who, String callerPackageName,
boolean disabled) {
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
} else {
caller = getCallerIdentity(who);
}
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforcePermission(MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(),
UserHandle.USER_ALL);
} else {
@@ -15501,7 +15570,7 @@
int userId = caller.getUserId();
synchronized (getLockObject()) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
+ if (!isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
"Admin " + who + " is neither the device owner or affiliated "
+ "user's profile owner.");
@@ -15560,7 +15629,7 @@
@Override
public boolean isStatusBarDisabled(String callerPackage) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforceCanQuery(
MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId());
} else {
@@ -15570,7 +15639,7 @@
int userId = caller.getUserId();
synchronized (getLockObject()) {
- if (!isPolicyEngineForFinanceFlagEnabled()) {
+ if (!isPermissionCheckFlagEnabled()) {
Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
"Admin " + callerPackage
+ " is neither the device owner or affiliated user's profile owner.");
@@ -16305,7 +16374,8 @@
// TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
// the admin for the calling user.
Slogf.w(LOG_TAG, "getEnforcingAdminAndUserDetailsInternal(%d, %s): multiple "
- + "sources for restriction %s on user %d", restriction, userId);
+ + "sources for restriction %s on user %d",
+ userId, restriction, restriction, userId);
result = new Bundle();
result.putInt(Intent.EXTRA_USER_ID, userId);
return result;
@@ -16730,7 +16800,7 @@
}
}
EnforcingAdmin enforcingAdmin;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
@@ -16901,7 +16971,7 @@
public int getPermissionGrantState(ComponentName admin, String callerPackage,
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
enforceCanQuery(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, caller.getPackageName(),
caller.getUserId());
} else {
@@ -17340,6 +17410,8 @@
CallerIdentity caller;
ActiveAdmin admin;
+ message = truncateIfLonger(message, MAX_SHORT_SUPPORT_MESSAGE_LENGTH);
+
if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(who, callerPackageName);
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
@@ -17400,6 +17472,9 @@
if (!mHasFeature) {
return;
}
+
+ message = truncateIfLonger(message, MAX_LONG_SUPPORT_MESSAGE_LENGTH);
+
Objects.requireNonNull(who, "ComponentName is null");
final CallerIdentity caller = getCallerIdentity(who);
synchronized (getLockObject()) {
@@ -17564,6 +17639,8 @@
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
}
+ text = truncateIfLonger(text, MAX_ORG_NAME_LENGTH);
+
synchronized (getLockObject()) {
if (!isPermissionCheckFlagEnabled()) {
admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
@@ -17844,9 +17921,8 @@
throw new IllegalArgumentException("ids must not be null");
}
for (String id : ids) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("ids must not contain empty string");
- }
+ Preconditions.checkArgument(!TextUtils.isEmpty(id), "ids must not have empty string");
+ enforceMaxStringLength(id, "affiliation id");
}
final Set<String> affiliationIds = new ArraySet<>(ids);
@@ -19033,14 +19109,14 @@
throw new IllegalArgumentException("token must be at least 32-byte long");
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(admin, callerPackageName);
} else {
caller = getCallerIdentity(admin);
}
final int userId = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19096,7 +19172,7 @@
return false;
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(admin, callerPackageName);
} else {
caller = getCallerIdentity(admin);
@@ -19104,7 +19180,7 @@
final int userId = caller.getUserId();
boolean result = false;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19143,14 +19219,14 @@
return false;
}
CallerIdentity caller;
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
caller = getCallerIdentity(admin, callerPackageName);
} else {
caller = getCallerIdentity(admin);
}
int userId = caller.getUserId();
- if (isPolicyEngineForFinanceFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
@@ -19359,6 +19435,9 @@
"Provided administrator and target are the same object.");
Preconditions.checkArgument(!admin.getPackageName().equals(target.getPackageName()),
"Provided administrator and target have the same package name.");
+ if (bundle != null) {
+ enforceMaxStringLength(bundle, "bundle");
+ }
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(
@@ -22764,6 +22843,7 @@
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
MANAGE_DEVICE_POLICY_FUN,
+ MANAGE_DEVICE_POLICY_INPUT_METHODS,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -22839,9 +22919,11 @@
MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CALLS,
MANAGE_DEVICE_POLICY_CAMERA,
+ MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
+ MANAGE_DEVICE_POLICY_INPUT_METHODS,
MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
MANAGE_DEVICE_POLICY_KEYGUARD,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -22874,7 +22956,6 @@
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
MANAGE_DEVICE_POLICY_DEFAULT_SMS,
MANAGE_DEVICE_POLICY_LOCALE,
@@ -22999,11 +23080,12 @@
//Map of Permission to Delegate Scope.
private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
{
- DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, DELEGATION_APP_RESTRICTIONS);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, DELEGATION_BLOCK_UNINSTALL);
- DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_CERTIFICATES, DELEGATION_CERT_INSTALL);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, DELEGATION_PACKAGE_ACCESS);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING);
}
private static final HashMap<String, String> CROSS_USER_PERMISSIONS =
@@ -23192,6 +23274,28 @@
}
/**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user. Only one permission provided in the list needs to be granted to pass this
+ * check.
+ * The given permissions will be checked along with their associated cross-user permissions if
+ * they exist and the target user is different to the calling user.
+ * Returns an {@link EnforcingAdmin} for the caller.
+ *
+ * @param admin the component name of the admin.
+ * @param callerPackageName The package name of the calling application.
+ * @param permissions The names of the permissions being checked.
+ * @param deviceAdminPolicy The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private EnforcingAdmin enforcePermissionsAndGetEnforcingAdmin(@Nullable ComponentName admin,
+ String[] permissions, int deviceAdminPolicy, String callerPackageName,
+ int targetUserId) {
+ enforcePermissions(permissions, deviceAdminPolicy, callerPackageName, targetUserId);
+ return getEnforcingAdminForCaller(admin, callerPackageName);
+ }
+
+ /**
* Checks whether the calling process has been granted permission to query a device policy on
* a specific user.
* The given permission will be checked along with its associated cross-user permission if it
@@ -23237,12 +23341,13 @@
/**
* Checks if the calling process has been granted permission to apply a device policy on a
- * specific user.
- * The given permission will be checked along with its associated cross-user permission if it
- * exists and the target user is different to the calling user.
+ * specific user. Only one permission provided in the list needs to be granted to pass this
+ * check.
+ * The given permissions will be checked along with their associated cross-user permissions if
+ * they exists and the target user is different to the calling user.
*
* @param callerPackageName The package name of the calling application.
- * @param permission The name of the permission being checked.
+ * @param permissions The names of the permissions being checked.
* @param targetUserId The userId of the user which the caller needs permission to act on.
* @throws SecurityException if the caller has not been granted the given permission,
* the associated cross-user permission if the caller's user is different to the target user.
@@ -23307,6 +23412,27 @@
}
/**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user.
+ * The given permission will be checked along with its associated cross-user permission if it
+ * exists and the target user is different to the calling user.
+ *
+ * @param callerPackageName The package name of the calling application.
+ * @param adminPolicy The admin policy that should grant holders permission.
+ * @param permission The name of the permission being checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private void enforcePermissions(String[] permissions, int adminPolicy,
+ String callerPackageName, int targetUserId) throws SecurityException {
+ if (hasAdminPolicy(adminPolicy, callerPackageName)) {
+ return;
+ }
+ enforcePermissions(permissions, callerPackageName, targetUserId);
+ }
+
+ /**
* Checks whether the calling process has been granted permission to query a device policy on
* a specific user.
* The given permission will be checked along with its associated cross-user permission if it
@@ -23433,7 +23559,8 @@
// Check for non-DPC active admins.
admin = getActiveAdminForCaller(who, caller);
if (admin != null) {
- return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
+ return EnforcingAdmin.createDeviceAdminEnforcingAdmin(admin.info.getComponent(), userId,
+ admin);
}
admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
return EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
@@ -23721,15 +23848,14 @@
private void updateDialerAndSmsManagedShortcutsOverrideCache(
String defaultDialerPackageName, String defaultSmsPackageName) {
-
- List<String> shortcutOverrides = new ArrayList<>();
+ ArrayMap<String, String> shortcutOverrides = new ArrayMap<>();
if (defaultDialerPackageName != null) {
- shortcutOverrides.add(defaultDialerPackageName);
+ shortcutOverrides.put(defaultDialerPackageName, defaultDialerPackageName);
}
if (defaultSmsPackageName != null) {
- shortcutOverrides.add(defaultSmsPackageName);
+ shortcutOverrides.put(defaultSmsPackageName, defaultSmsPackageName);
}
mPolicyCache.setLauncherShortcutOverrides(shortcutOverrides);
}
@@ -23794,6 +23920,7 @@
public DevicePolicyState getDevicePolicyState() {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
return mInjector.binderWithCleanCallingIdentity(mDevicePolicyEngine::getDevicePolicyState);
}
@@ -24058,6 +24185,53 @@
});
}
+ /**
+ * Truncates char sequence to maximum length, nulls are ignored.
+ */
+ private static CharSequence truncateIfLonger(CharSequence input, int maxLength) {
+ return input == null || input.length() <= maxLength
+ ? input
+ : input.subSequence(0, maxLength);
+ }
+
+ /**
+ * Throw if string argument is too long to be serialized.
+ */
+ private static void enforceMaxStringLength(String str, String argName) {
+ Preconditions.checkArgument(
+ str.length() <= MAX_POLICY_STRING_LENGTH, argName + " loo long");
+ }
+
+ private static void enforceMaxPackageNameLength(String pkg) {
+ Preconditions.checkArgument(
+ pkg.length() <= MAX_PACKAGE_NAME_LENGTH, "Package name too long");
+ }
+
+ /**
+ * Throw if persistable bundle contains any string that we can't serialize.
+ */
+ private static void enforceMaxStringLength(PersistableBundle bundle, String argName) {
+ // Persistable bundles can have other persistable bundles as values, traverse with a queue.
+ Queue<PersistableBundle> queue = new ArrayDeque<>();
+ queue.add(bundle);
+ while (!queue.isEmpty()) {
+ PersistableBundle current = queue.remove();
+ for (String key : current.keySet()) {
+ enforceMaxStringLength(key, "key in " + argName);
+ Object value = current.get(key);
+ if (value instanceof String) {
+ enforceMaxStringLength((String) value, "string value in " + argName);
+ } else if (value instanceof String[]) {
+ for (String str : (String[]) value) {
+ enforceMaxStringLength(str, "string value in " + argName);
+ }
+ } else if (value instanceof PersistableBundle) {
+ queue.add((PersistableBundle) value);
+ }
+ }
+ }
+ }
+
private ActiveAdmin getActiveAdminForCaller(@Nullable ComponentName who,
CallerIdentity caller) {
synchronized (getLockObject()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 638596b..43a2c9b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -308,6 +308,13 @@
DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
}
+ static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
+ new MostRecent<>(),
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ (Set<String> value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ new StringSetPolicySerializer());
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -333,6 +340,8 @@
GENERIC_APPLICATION_HIDDEN);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY,
GENERIC_ACCOUNT_MANAGEMENT_DISABLED);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY,
+ PERMITTED_INPUT_METHODS);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
index 741f209..dd4c6af 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyState.java
@@ -67,9 +67,8 @@
/**
* Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
*/
- boolean addPolicy(@NonNull EnforcingAdmin admin, @NonNull PolicyValue<V> policy) {
+ boolean addPolicy(@NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy) {
Objects.requireNonNull(admin);
- Objects.requireNonNull(policy);
//LinkedHashMap doesn't update the insertion order of existing keys, removing the existing
// key will cause it to update.
@@ -89,9 +88,9 @@
* Returns {@code true} if the resolved policy has changed, {@code false} otherwise.
*/
boolean addPolicy(
- @NonNull EnforcingAdmin admin, @NonNull PolicyValue<V> policy,
+ @NonNull EnforcingAdmin admin, @Nullable PolicyValue<V> policy,
LinkedHashMap<EnforcingAdmin, PolicyValue<V>> globalPoliciesSetByAdmins) {
- mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), Objects.requireNonNull(policy));
+ mPoliciesSetByAdmins.put(Objects.requireNonNull(admin), policy);
return resolvePolicy(globalPoliciesSetByAdmins);
}
@@ -210,10 +209,12 @@
for (EnforcingAdmin admin : mPoliciesSetByAdmins.keySet()) {
serializer.startTag(/* namespace= */ null, TAG_ADMIN_POLICY_ENTRY);
- serializer.startTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
- mPolicyDefinition.savePolicyValueToXml(
- serializer, mPoliciesSetByAdmins.get(admin).getValue());
- serializer.endTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
+ if (mPoliciesSetByAdmins.get(admin) != null) {
+ serializer.startTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
+ mPolicyDefinition.savePolicyValueToXml(
+ serializer, mPoliciesSetByAdmins.get(admin).getValue());
+ serializer.endTag(/* namespace= */ null, TAG_POLICY_VALUE_ENTRY);
+ }
serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_ENTRY);
admin.saveToXml(serializer);
@@ -250,7 +251,7 @@
break;
}
}
- if (admin != null && value != null) {
+ if (admin != null) {
policiesSetByAdmins.put(admin, value);
} else {
Log.e(TAG, "Error Parsing TAG_ADMIN_POLICY_ENTRY");
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 3a47b47..2b57c59 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -154,6 +154,7 @@
// Freeze time for testing.
long nowElapsed;
boolean useMotionSensor = true;
+ boolean isLocationPrefetchEnabled = true;
InjectorForTest(Context ctx) {
super(ctx);
@@ -223,6 +224,11 @@
}
@Override
+ boolean isLocationPrefetchEnabled() {
+ return isLocationPrefetchEnabled;
+ }
+
+ @Override
PowerManager getPowerManager() {
return mPowerManager;
}
@@ -991,6 +997,43 @@
}
@Test
+ public void testStepIdleStateLocked_ValidStates_LocationPrefetchDisabled() {
+ mInjector.locationManager = mLocationManager;
+ mInjector.isLocationPrefetchEnabled = false;
+ cleanupDeviceIdleController();
+ setupDeviceIdleController();
+ doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ setAlarmSoon(false);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Prefetch location is off, so SENSING should go straight through to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
// Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
setAlarmSoon(false);
@@ -1024,6 +1067,46 @@
}
@Test
+ public void testStepIdleStateLocked_ValidStates_WithLocationManager_MissingProviders() {
+ mInjector.locationManager = mLocationManager;
+ doReturn(null).when(mLocationManager)
+ .getProvider(eq(LocationManager.FUSED_PROVIDER));
+ doReturn(null).when(mLocationManager)
+ .getProvider(eq(LocationManager.GPS_PROVIDER));
+ doReturn(mock(LocationProvider.class)).when(mLocationManager)
+ .getProvider(eq(LocationManager.NETWORK_PROVIDER));
+ // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+ setAlarmSoon(false);
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyStateConditions(STATE_INACTIVE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_PENDING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_SENSING);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ // Location manager exists, but the required providers don't exist,
+ // so SENSING should go straight through to IDLE.
+ verifyStateConditions(STATE_IDLE);
+
+ // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE_MAINTENANCE);
+ }
+
+ @Test
public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
mInjector.locationManager = mLocationManager;
doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index a5adf3f..f1d4de9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -54,6 +54,7 @@
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_ALLOW_LIST;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
+import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_LISTENER;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION;
@@ -2728,6 +2729,66 @@
}
@Test
+ public void exactListenerBinderCallWithoutPermissionWithoutAllowlist() throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockScheduleExactAlarmState(false);
+ mockUseExactAlarmState(false);
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final IAlarmListener listener = getNewListener(() -> {});
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ 0, null, listener, "test-tag", null, null);
+
+ verify(mService, never()).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE,
+ TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ isNull(), eq(listener), eq("test-tag"), eq(FLAG_STANDALONE), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_LISTENER));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
+ public void exactAllowWhileIdleListenerBinderCallWithoutPermissionWithoutAllowlist()
+ throws RemoteException {
+ mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
+ mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
+
+ mockScheduleExactAlarmState(false);
+ mockUseExactAlarmState(false);
+ when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
+
+ final IAlarmListener listener = getNewListener(() -> {});
+ mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
+ FLAG_ALLOW_WHILE_IDLE, null, listener, "test-tag", null, null);
+
+ verify(mService, never()).hasUseExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID);
+ verify(mService, never()).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE,
+ TEST_CALLING_UID);
+ verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt());
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
+ isNull(), eq(listener), eq("test-tag"),
+ eq(FLAG_STANDALONE | FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), isNull(),
+ eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture(),
+ eq(EXACT_ALLOW_REASON_LISTENER));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
+ }
+
+ @Test
public void inexactAllowWhileIdleBinderCall() throws RemoteException {
// Both permission and power exemption status don't matter for these alarms.
// We only want to test that the flags and idleOptions are correct.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 3fb7fb4..acfea85 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -40,6 +40,7 @@
import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
import static com.android.server.am.BroadcastQueueTest.withPriority;
+import static com.android.server.am.BroadcastRecord.isReceiverEquals;
import static com.google.common.truth.Truth.assertThat;
@@ -49,13 +50,16 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import android.annotation.NonNull;
@@ -116,6 +120,7 @@
TestLooperManager mLooper;
BroadcastConstants mConstants;
+ private BroadcastSkipPolicy mSkipPolicy;
BroadcastQueueModernImpl mImpl;
BroadcastProcessQueue mHead;
@@ -139,29 +144,18 @@
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
- final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
- public boolean shouldSkip(BroadcastRecord r, Object o) {
- // Ignored
- return false;
- }
- public String shouldSkipMessage(BroadcastRecord r, Object o) {
- // Ignored
- return null;
- }
- public boolean disallowBackgroundStart(BroadcastRecord r) {
- // Ignored
- return false;
- }
- };
+ mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+ doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+ doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
+
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
}
};
-
mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
- mConstants, mConstants, emptySkipPolicy, emptyHistory);
+ mConstants, mConstants, mSkipPolicy, emptyHistory);
doReturn(1L).when(mQueue1).getRunnableAt();
doReturn(2L).when(mQueue2).getRunnableAt();
@@ -256,17 +250,12 @@
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex) {
- enqueueOrReplaceBroadcast(queue, record, recordIndex, false, 42_000_000L);
+ enqueueOrReplaceBroadcast(queue, record, recordIndex, 42_000_000L);
}
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex, long enqueueTime) {
- enqueueOrReplaceBroadcast(queue, record, recordIndex, false, enqueueTime);
- }
-
- private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
- BroadcastRecord record, int recordIndex, boolean wouldBeSkipped, long enqueueTime) {
- queue.enqueueOrReplaceBroadcast(record, recordIndex, wouldBeSkipped, (r, i) -> {
+ queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
throw new UnsupportedOperationException();
});
record.enqueueTime = enqueueTime;
@@ -1199,6 +1188,42 @@
assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
}
+ @Test
+ public void testSkipPolicy_atEnqueueTime() throws Exception {
+ final Intent userPresent = new Intent(Intent.ACTION_USER_PRESENT);
+ final Object greenReceiver = makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN);
+ final Object redReceiver = makeManifestReceiver(PACKAGE_RED, CLASS_RED);
+
+ final BroadcastRecord userPresentRecord = makeBroadcastRecord(userPresent,
+ List.of(greenReceiver, redReceiver));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(greenReceiver, redReceiver));
+
+ doAnswer(invocation -> {
+ final BroadcastRecord r = invocation.getArgument(0);
+ final Object o = invocation.getArgument(1);
+ if (userPresent.getAction().equals(r.intent.getAction())
+ && isReceiverEquals(o, greenReceiver)) {
+ return "receiver skipped by test";
+ }
+ return null;
+ }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
+
+ mImpl.enqueueBroadcastLocked(userPresentRecord);
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ // There should be only one broadcast for green process as the other would have
+ // been skipped.
+ verifyPendingRecords(greenQueue, List.of(timeTick));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
+ }
+
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index c4aa0bb..8dc0ac6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -64,6 +64,7 @@
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
@@ -596,17 +597,17 @@
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
- advanceTime(1);
+ advanceTime(1); // Run updatePowerState
verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt());
mHolder = createDisplayPowerController(42, UNIQUE_ID);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
- advanceTime(1);
+ advanceTime(1); // Run updatePowerState
verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
mHolder.dpc.onBootCompleted();
- advanceTime(1);
+ advanceTime(1); // Run updatePowerState
verify(mHolder.displayPowerState).setScreenState(anyInt());
}
@@ -632,8 +633,8 @@
.thenReturn(brightness);
dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
- when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness())
- .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -667,8 +668,8 @@
.thenReturn(brightness);
dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
- when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness())
- .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+ any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -822,6 +823,21 @@
);
}
+ @Test
+ public void testUpdateBrightnessThrottlingDataId() {
+ mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId =
+ "throttling-data-id";
+ clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig());
+
+ mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig())
+ .getThermalBrightnessThrottlingDataMapByThrottlingId();
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -862,8 +878,6 @@
when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
- when(logicalDisplayMock.getThermalBrightnessThrottlingDataIdLocked()).thenReturn(
- DisplayDeviceConfig.DEFAULT_ID);
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 415adbb..5c0810f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -829,6 +829,21 @@
);
}
+ @Test
+ public void testUpdateBrightnessThrottlingDataId() {
+ mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId =
+ "throttling-data-id";
+ clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig());
+
+ mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig())
+ .getThermalBrightnessThrottlingDataMapByThrottlingId();
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -869,8 +884,6 @@
when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
- when(logicalDisplayMock.getThermalBrightnessThrottlingDataIdLocked()).thenReturn(
- DisplayDeviceConfig.DEFAULT_ID);
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
deleted file mode 100644
index 17fba9f..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.display;
-
-import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import android.hardware.display.DisplayManager;
-import android.provider.Settings;
-import android.testing.TestableContext;
-import android.view.Display;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.display.RefreshRateSettingsUtils;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RefreshRateSettingsUtilsTest {
-
- @Rule
- public final TestableContext mContext = new TestableContext(
- InstrumentationRegistry.getInstrumentation().getContext());
-
- @Mock
- private DisplayManager mDisplayManagerMock;
- @Mock
- private Display mDisplayMock;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock);
-
- Display.Mode[] modes = new Display.Mode[] {
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 60),
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 120),
- new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
- /* refreshRate= */ 90)
- };
-
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- when(mDisplayMock.getSupportedModes()).thenReturn(modes);
- }
-
- @Test
- public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
- assertEquals(DEFAULT_REFRESH_RATE,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
-
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
- assertEquals(120,
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* delta= */ 0);
- }
-
- @Test
- public void testGetMinRefreshRate() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
- assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 0);
- assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, 1);
- assertEquals(120, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
- }
-
- @Test
- public void testGetPeakRefreshRate() {
- float defaultPeakRefreshRate = 100;
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
- assertEquals(defaultPeakRefreshRate,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 0);
- assertEquals(DEFAULT_REFRESH_RATE,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
-
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 1);
- assertEquals(120,
- RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
- /* delta= */ 0);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 96eca71..8cfc150 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -281,8 +281,8 @@
@Test
public void testRegisterProxy() throws Exception {
mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
- verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY),
- eq(mTestableContext), anyInt(), any(), eq(mMockSecurityPolicy),
+ verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY), anyInt(),
+ eq(mMockSecurityPolicy),
eq(mA11yms), eq(mA11yms.getTraceManager()),
eq(mMockWindowManagerService));
}
@@ -295,7 +295,7 @@
assertThrows(SecurityException.class,
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
- verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
+ verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
any(), any(), any());
}
@@ -307,7 +307,7 @@
assertThrows(SecurityException.class,
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
- verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
+ verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
any(), any(), any());
}
@@ -316,7 +316,7 @@
public void testRegisterProxyForDefaultDisplay() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
- verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
+ verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
any(), any(), any());
}
@@ -325,7 +325,7 @@
public void testRegisterProxyForInvalidDisplay() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
- verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
+ verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(),
any(), any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
index dd44a79..6ac3658 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyAccessibilityServiceConnectionTest.java
@@ -29,12 +29,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Color;
import android.os.Handler;
import android.view.accessibility.AccessibilityEvent;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -79,12 +81,15 @@
WindowManagerInternal mMockWindowManagerInternal;
ProxyAccessibilityServiceConnection mProxyConnection;
AccessibilityServiceInfo mAccessibilityServiceInfo;
+ private int mFocusStrokeWidthDefaultValue;
+ private int mFocusColorDefaultValue;
@Before
public void setup() {
final Resources resources = getInstrumentation().getContext().getResources();
MockitoAnnotations.initMocks(this);
when(mMockContext.getResources()).thenReturn(resources);
+ when(mMockSecurityPolicy.checkAccessibilityAccess(any())).thenReturn(true);
mAccessibilityServiceInfo = new AccessibilityServiceInfo();
mProxyConnection = new ProxyAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
@@ -92,10 +97,13 @@
getInstrumentation().getContext().getMainLooper()),
mMockLock, mMockSecurityPolicy, mMockSystemSupport, mMockA11yTrace,
mMockWindowManagerInternal, mMockA11yWindowManager, DISPLAY_ID, DEVICE_ID);
+
+ mFocusStrokeWidthDefaultValue = mProxyConnection.getFocusStrokeWidthLocked();
+ mFocusColorDefaultValue = mProxyConnection.getFocusColorLocked();
}
@Test
- public void testSetInstalledAndEnabledServices_clientChanged() {
+ public void testSetInstalledAndEnabledServices_updateInfos_notifiesSystemOfProxyChange() {
final List<AccessibilityServiceInfo> infos = new ArrayList<>();
final AccessibilityServiceInfo info1 = new AccessibilityServiceInfo();
infos.add(info1);
@@ -106,6 +114,17 @@
}
@Test
+ public void testSetFocusAppearance_updateAppearance_notifiesSystemOfProxyChange() {
+ final int updatedWidth = mFocusStrokeWidthDefaultValue + 10;
+ final int updatedColor = mFocusColorDefaultValue
+ == Color.BLUE ? Color.RED : Color.BLUE;
+
+ mProxyConnection.setFocusAppearance(updatedWidth, updatedColor);
+
+ verify(mMockSystemSupport).onProxyChanged(DEVICE_ID);
+ }
+
+ @Test
public void testSetInstalledAndEnabledServices_returnList() {
final List<AccessibilityServiceInfo> infos = new ArrayList<>();
final AccessibilityServiceInfo info1 = new AccessibilityServiceInfo();
@@ -196,7 +215,7 @@
}
@Test
- public void testSetServiceInfo_setIllegalOperationExceptionThrown_() {
+ public void testSetServiceInfo_setIllegalOperationExceptionThrown() {
UnsupportedOperationException thrown =
assertThrows(
UnsupportedOperationException.class,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
new file mode 100644
index 0000000..6c7b995
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -0,0 +1,527 @@
+/*
+ * 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.accessibility;
+
+import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.INTERACTIVE_UI_TIMEOUT_100MS;
+import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.INTERACTIVE_UI_TIMEOUT_200MS;
+import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.NON_INTERACTIVE_UI_TIMEOUT_100MS;
+import static com.android.server.accessibility.ProxyAccessibilityServiceConnectionTest.NON_INTERACTIVE_UI_TIMEOUT_200MS;
+import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_CLASS_NAME;
+import static com.android.server.accessibility.ProxyManager.PROXY_COMPONENT_PACKAGE_NAME;
+
+import static org.junit.Assert.fail;
+
+import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.MagnificationConfig;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.R;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.util.IntPair;
+import com.android.server.LocalServices;
+import com.android.server.accessibility.test.MessageCapturingHandler;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.InstrumentationRegistry;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for ProxyManager.
+ */
+public class ProxyManagerTest {
+ private static final int DISPLAY_ID = 1000;
+ private static final int DISPLAY_2_ID = 1001;
+ private static final int DEVICE_ID = 10;
+ private static final int STREAMED_CALLING_UID = 9876;
+
+ @Mock private Context mMockContext;
+ @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
+ @Mock private AccessibilityWindowManager mMockA11yWindowManager;
+ @Mock private ProxyManager.SystemSupport mMockProxySystemSupport;
+ @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockConnectionSystemSupport;
+ @Mock private AccessibilityTrace mMockA11yTrace;
+ @Mock private WindowManagerInternal mMockWindowManagerInternal;
+ @Mock private IAccessibilityServiceClient mMockAccessibilityServiceClient;
+ @Mock private IBinder mMockServiceAsBinder;
+ @Mock private VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
+ @Mock private IVirtualDeviceManager mMockIVirtualDeviceManager;
+
+ private int mFocusStrokeWidthDefaultValue;
+ private int mFocusColorDefaultValue;
+
+ private MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
+ private ProxyManager mProxyManager;
+
+ @Before
+ public void setup() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ final Resources resources = InstrumentationRegistry.getContext().getResources();
+
+ mFocusStrokeWidthDefaultValue =
+ resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color);
+ when(mMockContext.getResources()).thenReturn(resources);
+
+ when(mMockVirtualDeviceManagerInternal.getDeviceIdsForUid(anyInt())).thenReturn(
+ new ArraySet(Set.of(DEVICE_ID)));
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class,
+ mMockVirtualDeviceManagerInternal);
+
+ when(mMockIVirtualDeviceManager.getDeviceIdForDisplayId(anyInt())).thenReturn(DEVICE_ID);
+ final VirtualDeviceManager virtualDeviceManager =
+ new VirtualDeviceManager(mMockIVirtualDeviceManager, mMockContext);
+ when(mMockContext.getSystemServiceName(VirtualDeviceManager.class)).thenReturn(
+ Context.VIRTUAL_DEVICE_SERVICE);
+ when(mMockContext.getSystemService(VirtualDeviceManager.class))
+ .thenReturn(virtualDeviceManager);
+
+ when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
+
+ final RemoteCallbackList<IAccessibilityManagerClient> userClients =
+ new RemoteCallbackList<>();
+ final RemoteCallbackList<IAccessibilityManagerClient> globalClients =
+ new RemoteCallbackList<>();
+ when(mMockProxySystemSupport.getCurrentUserClientsLocked()).thenReturn(userClients);
+ when(mMockProxySystemSupport.getGlobalClientsLocked()).thenReturn(globalClients);
+
+ when(mMockAccessibilityServiceClient.asBinder()).thenReturn(mMockServiceAsBinder);
+
+ mProxyManager = new ProxyManager(new Object(), mMockA11yWindowManager, mMockContext,
+ mMessageCapturingHandler, new UiAutomationManager(new Object()),
+ mMockProxySystemSupport);
+ }
+
+ @After
+ public void tearDown() {
+ mMessageCapturingHandler.removeAllMessages();
+ }
+
+ /**
+ * Tests that the proxy’s backing AccessibilityServiceClient is initialized when registering a
+ * proxy.
+ */
+ @Test
+ public void registerProxy_always_connectsServiceClient() throws RemoteException {
+ registerProxy(DISPLAY_ID);
+ verify(mMockAccessibilityServiceClient).init(any(), anyInt(), any());
+ }
+
+ /** Tests that unregistering a proxy removes its display from tracking. */
+ @Test
+ public void unregisterProxy_always_stopsTrackingDisplay() {
+ registerProxy(DISPLAY_ID);
+
+ mProxyManager.unregisterProxy(DISPLAY_ID);
+
+ verify(mMockA11yWindowManager).stopTrackingDisplayProxy(DISPLAY_ID);
+ assertThat(mProxyManager.isProxyedDisplay(DISPLAY_ID)).isFalse();
+ }
+ /**
+ * Tests that unregistering a proxied display of a virtual device, where that virtual device
+ * owned only that one proxied display, removes the device from tracking.
+ */
+ @Test
+ public void unregisterProxy_deviceAssociatedWithSingleDisplay_stopsTrackingDevice() {
+ registerProxy(DISPLAY_ID);
+
+ mProxyManager.unregisterProxy(DISPLAY_ID);
+
+ assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isFalse();
+ verify(mMockProxySystemSupport).removeDeviceIdLocked(DEVICE_ID);
+ }
+
+ /**
+ * Tests that unregistering a proxied display of a virtual device, where that virtual device
+ * owns more than one proxied display, does not remove the device from tracking.
+ */
+ @Test
+ public void unregisterProxy_deviceAssociatedWithMultipleDisplays_tracksRemainingProxy() {
+ registerProxy(DISPLAY_ID);
+ registerProxy(DISPLAY_2_ID);
+
+ mProxyManager.unregisterProxy(DISPLAY_ID);
+
+ assertThat(mProxyManager.isProxyedDeviceId(DEVICE_ID)).isTrue();
+ verify(mMockProxySystemSupport, never()).removeDeviceIdLocked(DEVICE_ID);
+ }
+
+ /**
+ * Tests that changing a proxy, e.g. registering/unregistering a proxy or updating its service
+ * info, notifies the apps being streamed and AccessibilityManagerService.
+ */
+ @Test
+ public void testOnProxyChanged_always_propagatesChange() {
+ registerProxy(DISPLAY_ID);
+ mMessageCapturingHandler.sendAllMessages();
+
+ mProxyManager.onProxyChanged(DEVICE_ID);
+
+ // Messages to notify IAccessibilityManagerClients should be posted.
+ assertThat(mMessageCapturingHandler.hasMessages()).isTrue();
+
+ verify(mMockProxySystemSupport).updateWindowsForAccessibilityCallbackLocked();
+ verify(mMockProxySystemSupport).notifyClearAccessibilityCacheLocked();
+ }
+
+ /**
+ * Tests that getting the first device id for an app uid, such as when an app queries for
+ * device-specific state, returns the right device id.
+ */
+ @Test
+ public void testGetFirstDeviceForUid_streamedAppQueriesState_getsHostDeviceId() {
+ registerProxy(DISPLAY_ID);
+ assertThat(mProxyManager.getFirstDeviceIdForUidLocked(STREAMED_CALLING_UID))
+ .isEqualTo(DEVICE_ID);
+ }
+
+ /**
+ * Tests that the app client state retrieved for a device reflects that touch exploration is
+ * enabled since a proxy info has requested touch exploration.
+ */
+ @Test
+ public void testGetClientState_proxyWantsTouchExploration_returnsTouchExplorationEnabled() {
+ registerProxy(DISPLAY_ID);
+
+ final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
+ secondDisplayInfo.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+ AccessibilityServiceClientImpl client = new AccessibilityServiceClientImpl(
+ secondDisplayInfo);
+ registerProxy(DISPLAY_2_ID, client);
+
+ final int deviceClientState = mProxyManager.getStateLocked(DEVICE_ID);
+ assertThat((deviceClientState
+ & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0).isTrue();
+ }
+
+ /**
+ * Tests that the highest interactive and non-interactive timeout is returned if there are
+ * multiple proxied displays belonging to a device.
+ */
+ @Test
+ public void testGetRecommendedTimeout_multipleProxies_returnsHighestTimeout() {
+ final AccessibilityServiceInfo firstDisplayInfo = new AccessibilityServiceInfo();
+ firstDisplayInfo.setInteractiveUiTimeoutMillis(INTERACTIVE_UI_TIMEOUT_100MS);
+ firstDisplayInfo.setNonInteractiveUiTimeoutMillis(NON_INTERACTIVE_UI_TIMEOUT_200MS);
+
+ final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
+ secondDisplayInfo.setInteractiveUiTimeoutMillis(INTERACTIVE_UI_TIMEOUT_200MS);
+ secondDisplayInfo.setNonInteractiveUiTimeoutMillis(NON_INTERACTIVE_UI_TIMEOUT_100MS);
+
+ registerProxy(DISPLAY_ID, new AccessibilityServiceClientImpl(firstDisplayInfo));
+ registerProxy(DISPLAY_2_ID, new AccessibilityServiceClientImpl(secondDisplayInfo));
+
+ final long timeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
+ final int interactiveTimeout = IntPair.first(timeout);
+ final int nonInteractiveTimeout = IntPair.second(timeout);
+
+ assertThat(interactiveTimeout).isEqualTo(INTERACTIVE_UI_TIMEOUT_200MS);
+ assertThat(nonInteractiveTimeout).isEqualTo(NON_INTERACTIVE_UI_TIMEOUT_200MS);
+ }
+ /**
+ * Tests that getting the installed and enabled services returns the info of the registered
+ * proxy. (The component name reflects the display id.)
+ */
+ @Test
+ public void testGetInstalledAndEnabledServices_defaultInfo_returnsInfoForDisplayId() {
+ final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ registerProxy(DISPLAY_ID, new AccessibilityServiceClientImpl(info));
+ final List<AccessibilityServiceInfo> installedAndEnabledServices =
+ mProxyManager.getInstalledAndEnabledServiceInfosLocked(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK, DEVICE_ID);
+ assertThat(installedAndEnabledServices.size()).isEqualTo(1);
+ AccessibilityServiceInfo proxyInfo = installedAndEnabledServices.get(0);
+
+ assertThat(proxyInfo.getComponentName()).isEqualTo(new ComponentName(
+ PROXY_COMPONENT_PACKAGE_NAME, PROXY_COMPONENT_CLASS_NAME + DISPLAY_ID));
+ }
+
+ /**
+ * Tests that the app client state retrieved for a device reflects that accessibility is
+ * enabled.
+ */
+ @Test
+ public void testGetClientState_always_returnsAccessibilityEnabled() {
+ registerProxy(DISPLAY_ID);
+
+ final int deviceClientState = mProxyManager.getStateLocked(DEVICE_ID);
+ assertThat((deviceClientState
+ & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0).isTrue();
+ }
+
+ /**
+ * Tests that the manager can retrieve interactive windows if a proxy sets
+ * AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS.
+ */
+ @Test
+ public void testCanRetrieveInteractiveWindows_atLeastOneProxyWantsWindows_returnsTrue() {
+ registerProxy(DISPLAY_ID);
+
+ final AccessibilityServiceInfo secondDisplayInfo = new AccessibilityServiceInfo();
+ secondDisplayInfo.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ registerProxy(DISPLAY_2_ID, new AccessibilityServiceClientImpl(secondDisplayInfo));
+
+ assertThat(mProxyManager.canRetrieveInteractiveWindowsLocked()).isTrue();
+ }
+
+ /**
+ * Tests that getting service interfaces to interrupt when AccessibilityManager#interrupt
+ * returns the registered proxy interface.
+ */
+ @Test
+ public void testGetServiceInterfacesForInterrupt_defaultProxy_returnsProxyInterface() {
+ registerProxy(DISPLAY_ID);
+ final List<IAccessibilityServiceClient> interfacesToInterrupt = new ArrayList<>();
+ mProxyManager.addServiceInterfacesLocked(interfacesToInterrupt, DEVICE_ID);
+
+ assertThat(interfacesToInterrupt.size()).isEqualTo(1);
+ assertThat(interfacesToInterrupt.get(0).asBinder()).isEqualTo(mMockServiceAsBinder);
+ }
+
+ /** Tests that the default timeout (0) is returned when the proxy is registered. */
+ @Test
+ public void getRecommendedTimeout_defaultProxyInfo_getsDefaultTimeout() {
+ registerProxy(DISPLAY_ID);
+ final long timeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
+ final int interactiveTimeout = IntPair.first(timeout);
+ final int nonInteractiveTimeout = IntPair.second(timeout);
+
+ assertThat(interactiveTimeout).isEqualTo(0);
+ assertThat(nonInteractiveTimeout).isEqualTo(0);
+ }
+
+ /** Tests that the manager returns the updated timeout when the proxy’s timeout is updated. */
+ @Test
+ public void getRecommendedTimeout_updateTimeout_getsUpdatedTimeout() {
+ registerProxy(DISPLAY_ID);
+
+ mProxyManager.updateTimeoutsIfNeeded(NON_INTERACTIVE_UI_TIMEOUT_100MS,
+ INTERACTIVE_UI_TIMEOUT_200MS);
+
+ final long updatedTimeout = mProxyManager.getRecommendedTimeoutMillisLocked(DEVICE_ID);
+ final int updatedInteractiveTimeout = IntPair.first(updatedTimeout);
+ final int updatedNonInteractiveTimeout = IntPair.second(updatedTimeout);
+
+ assertThat(updatedInteractiveTimeout).isEqualTo(INTERACTIVE_UI_TIMEOUT_200MS);
+ assertThat(updatedNonInteractiveTimeout).isEqualTo(NON_INTERACTIVE_UI_TIMEOUT_100MS);
+ }
+
+ /** Tests that the system’s default focus color is returned. */
+ @Test
+ public void testGetFocusColor_defaultProxy_getsDefaultSystemColor() {
+ registerProxy(DISPLAY_ID);
+ final int focusColor = mProxyManager.getFocusColorLocked(DEVICE_ID);
+ assertThat(focusColor).isEqualTo(mFocusColorDefaultValue);
+ }
+
+ /** Tests that the system’s default focus stroke width is returned. */
+ @Test
+ public void testGetFocusStrokeWidth_defaultProxy_getsDefaultSystemWidth() {
+ registerProxy(DISPLAY_ID);
+ final int focusStrokeWidth = mProxyManager.getFocusStrokeWidthLocked(DEVICE_ID);
+ assertThat(focusStrokeWidth).isEqualTo(mFocusStrokeWidthDefaultValue);
+ }
+
+ private void registerProxy(int displayId) {
+ try {
+ mProxyManager.registerProxy(mMockAccessibilityServiceClient, displayId, anyInt(),
+ mMockSecurityPolicy, mMockConnectionSystemSupport,
+ mMockA11yTrace, mMockWindowManagerInternal);
+ } catch (RemoteException e) {
+ fail("Failed to register proxy " + e);
+ }
+ }
+
+ private void registerProxy(int displayId, AccessibilityServiceClientImpl serviceClient) {
+ try {
+ mProxyManager.registerProxy(serviceClient, displayId, anyInt(),
+ mMockSecurityPolicy, mMockConnectionSystemSupport,
+ mMockA11yTrace, mMockWindowManagerInternal);
+ } catch (RemoteException e) {
+ fail("Failed to register proxy " + e);
+ }
+ }
+
+ /**
+ * IAccessibilityServiceClient implementation.
+ * A proxy connection does not populate non-default AccessibilityServiceInfo values until the
+ * proxy is connected in A11yDisplayProxy#onServiceConnected. For tests that check for
+ * non-default values, populate immediately in this testing class, since a real Service is not
+ * being used and connected.
+ */
+ static class AccessibilityServiceClientImpl extends IAccessibilityServiceClient.Stub {
+ List<AccessibilityServiceInfo> mInstalledAndEnabledServices;
+
+ AccessibilityServiceClientImpl(AccessibilityServiceInfo
+ installedAndEnabledService) {
+ mInstalledAndEnabledServices = List.of(installedAndEnabledService);
+ }
+
+ @Override
+ public void init(IAccessibilityServiceConnection connection, int connectionId,
+ IBinder windowToken) throws RemoteException {
+ connection.setInstalledAndEnabledServices(mInstalledAndEnabledServices);
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onInterrupt() throws RemoteException {
+
+ }
+
+ @Override
+ public void onGesture(AccessibilityGestureEvent gestureEvent) throws RemoteException {
+
+ }
+
+ @Override
+ public void clearAccessibilityCache() throws RemoteException {
+
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event, int sequence) throws RemoteException {
+
+ }
+
+ @Override
+ public void onMagnificationChanged(int displayId, Region region, MagnificationConfig config)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event) throws RemoteException {
+
+ }
+
+ @Override
+ public void onTouchStateChanged(int displayId, int state) throws RemoteException {
+
+ }
+
+ @Override
+ public void onSoftKeyboardShowModeChanged(int showMode) throws RemoteException {
+
+ }
+
+ @Override
+ public void onPerformGestureResult(int sequence, boolean completedSuccessfully)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onFingerprintCapturingGesturesChanged(boolean capturing)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onFingerprintGesture(int gesture) throws RemoteException {
+
+ }
+
+ @Override
+ public void onAccessibilityButtonClicked(int displayId) throws RemoteException {
+
+ }
+
+ @Override
+ public void onAccessibilityButtonAvailabilityChanged(boolean available)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void onSystemActionsChanged() throws RemoteException {
+
+ }
+
+ @Override
+ public void createImeSession(IAccessibilityInputMethodSessionCallback callback)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void setImeSessionEnabled(IAccessibilityInputMethodSession session, boolean enabled)
+ throws RemoteException {
+
+ }
+
+ @Override
+ public void bindInput() throws RemoteException {
+
+ }
+
+ @Override
+ public void unbindInput() throws RemoteException {
+
+ }
+
+ @Override
+ public void startInput(IRemoteAccessibilityInputConnection connection,
+ EditorInfo editorInfo, boolean restarting) throws RemoteException {
+
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index 07c6182..fb3a5f6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -80,7 +80,7 @@
@Mock
private WindowManager mWindowManager;
- private OperationContextExt mOpContext = new OperationContextExt();
+ private OperationContextExt mOpContext = new OperationContextExt(true);
private IBiometricContextListener mListener;
private BiometricContextProvider mProvider;
@@ -316,25 +316,6 @@
assertThat(aidlContext.isAod).isEqualTo(false);
assertThat(aidlContext.isCrypto).isEqualTo(false);
- for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
- StatusBarManager.SESSION_KEYGUARD)) {
- final int id = 40 + type;
- final boolean aod = (type & 1) == 0;
-
- mListener.onDisplayStateChanged(aod ? AuthenticateOptions.DISPLAY_STATE_AOD
- : AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN);
- mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
- context = mProvider.updateContext(mOpContext, false /* crypto */);
- aidlContext = context.toAidlContext();
- assertThat(context).isSameInstanceAs(mOpContext);
- assertThat(aidlContext.id).isEqualTo(id);
- assertThat(aidlContext.reason).isEqualTo(reason(type));
- assertThat(aidlContext.isAod).isEqualTo(aod);
- assertThat(aidlContext.isCrypto).isEqualTo(false);
-
- mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
- }
-
context = mProvider.updateContext(mOpContext, false /* crypto */);
aidlContext = context.toAidlContext();
assertThat(context).isSameInstanceAs(mOpContext);
@@ -344,6 +325,33 @@
assertThat(aidlContext.isCrypto).isEqualTo(false);
}
+ @Test
+ public void testUpdateAllSessionTypes() throws RemoteException {
+ OperationContextExt context = mProvider.updateContext(mOpContext, false /* crypto */);
+ OperationContext aidlContext = context.toAidlContext();
+
+ for (int type : List.of(StatusBarManager.SESSION_BIOMETRIC_PROMPT,
+ StatusBarManager.SESSION_KEYGUARD)) {
+ final int id = 40 + type;
+ final boolean aod = (type & 1) == 0;
+
+ OperationContextExt opContext =
+ new OperationContextExt(type == StatusBarManager.SESSION_BIOMETRIC_PROMPT);
+ mListener.onDisplayStateChanged(aod ? AuthenticateOptions.DISPLAY_STATE_AOD
+ : AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN);
+ mSessionListener.onSessionStarted(type, InstanceId.fakeInstanceId(id));
+ context = mProvider.updateContext(opContext, false /* crypto */);
+ aidlContext = context.toAidlContext();
+ assertThat(context).isSameInstanceAs(opContext);
+ assertThat(aidlContext.id).isEqualTo(id);
+ assertThat(aidlContext.reason).isEqualTo(reason(type));
+ assertThat(aidlContext.isAod).isEqualTo(aod);
+ assertThat(aidlContext.isCrypto).isEqualTo(false);
+
+ mSessionListener.onSessionEnded(type, InstanceId.fakeInstanceId(id));
+ }
+ }
+
private static byte reason(int type) {
if (type == StatusBarManager.SESSION_BIOMETRIC_PROMPT) {
return OperationReason.BIOMETRIC_PROMPT;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
index 5adf391..5cff48d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricFrameworkStatsLoggerTest.java
@@ -34,7 +34,7 @@
@Test
public void testConvertsWakeReason_whenEmpty() {
- final OperationContextExt ctx = new OperationContextExt();
+ final OperationContextExt ctx = new OperationContextExt(false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -48,11 +48,11 @@
public void testConvertsWakeReason_whenPowerReason() {
final OperationContext context = new OperationContext();
context.wakeReason = WakeReason.WAKE_MOTION;
- final OperationContextExt ctx = new OperationContextExt(context);
+ final OperationContextExt ctx = new OperationContextExt(context, false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
- .toProtoWakeReasonDetails(new OperationContextExt(context));
+ .toProtoWakeReasonDetails(new OperationContextExt(context, false));
assertThat(reason).isEqualTo(BiometricsProtoEnums.WAKE_REASON_WAKE_MOTION);
assertThat(reasonDetails).isEmpty();
@@ -63,7 +63,7 @@
final OperationContext context = new OperationContext();
context.authenticateReason = AuthenticateReason.faceAuthenticateReason(
AuthenticateReason.Face.ASSISTANT_VISIBLE);
- final OperationContextExt ctx = new OperationContextExt(context);
+ final OperationContextExt ctx = new OperationContextExt(context, false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -79,7 +79,7 @@
final OperationContext context = new OperationContext();
context.authenticateReason = AuthenticateReason.vendorAuthenticateReason(
new AuthenticateReason.Vendor());
- final OperationContextExt ctx = new OperationContextExt(context);
+ final OperationContextExt ctx = new OperationContextExt(context, false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -96,7 +96,7 @@
context.wakeReason = WakeReason.WAKE_KEY;
context.authenticateReason = AuthenticateReason.faceAuthenticateReason(
AuthenticateReason.Face.PRIMARY_BOUNCER_SHOWN);
- final OperationContextExt ctx = new OperationContextExt(context);
+ final OperationContextExt ctx = new OperationContextExt(context, false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
@@ -113,7 +113,7 @@
context.wakeReason = WakeReason.LID;
context.authenticateReason = AuthenticateReason.vendorAuthenticateReason(
new AuthenticateReason.Vendor());
- final OperationContextExt ctx = new OperationContextExt(context);
+ final OperationContextExt ctx = new OperationContextExt(context, false);
final int reason = BiometricFrameworkStatsLogger.toProtoWakeReason(ctx);
final int[] reasonDetails = BiometricFrameworkStatsLogger
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 81dcf48..612f717 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -74,7 +74,7 @@
@Before
public void setUp() {
- mOpContext = new OperationContextExt();
+ mOpContext = new OperationContextExt(false);
mContext.addMockSystemService(SensorManager.class, mSensorManager);
when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
index c652b74..5cf5960 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/OperationContextExtTest.java
@@ -53,12 +53,12 @@
@Test
public void hasAidlContext() {
- OperationContextExt context = new OperationContextExt();
+ OperationContextExt context = new OperationContextExt(false);
assertThat(context.toAidlContext()).isNotNull();
final OperationContext aidlContext = newAidlContext();
- context = new OperationContextExt(aidlContext);
+ context = new OperationContextExt(aidlContext, false);
assertThat(context.toAidlContext()).isSameInstanceAs(aidlContext);
final int id = 5;
@@ -79,7 +79,7 @@
@Test
public void hasNoOrderWithoutSession() {
- OperationContextExt context = new OperationContextExt();
+ OperationContextExt context = new OperationContextExt(false);
assertThat(context.getOrderAndIncrement()).isEqualTo(-1);
assertThat(context.getOrderAndIncrement()).isEqualTo(-1);
}
@@ -96,7 +96,7 @@
);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
- final OperationContextExt context = new OperationContextExt(newAidlContext());
+ final OperationContextExt context = new OperationContextExt(newAidlContext(), true);
when(mBiometricContext.getDisplayState()).thenReturn(entry.getKey());
assertThat(context.update(mBiometricContext).getDisplayState())
.isEqualTo(entry.getValue());
@@ -136,7 +136,8 @@
when(mBiometricContext.isDisplayOn()).thenReturn(true);
when(mBiometricContext.getDisplayState()).thenReturn(displayState);
- final OperationContextExt context = new OperationContextExt(newAidlContext());
+ final OperationContextExt context = new OperationContextExt(newAidlContext(),
+ sessionType == OperationReason.BIOMETRIC_PROMPT);
assertThat(context.update(mBiometricContext)).isSameInstanceAs(context);
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
index c7fb97f..9d42a5b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java
@@ -18,9 +18,11 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
import android.telecom.Call;
import android.telecom.ParcelableCall;
+import android.telecom.PhoneAccountHandle;
import android.testing.AndroidTestingRunner;
import androidx.test.InstrumentationRegistry;
@@ -37,12 +39,14 @@
private static final String CALLER_DISPLAY_NAME = "name";
private static final String CONTACT_DISPLAY_NAME = "contact";
+ private final Call.Details mUninitializedCallDetails = createCallDetails(
+ /* state= */ -1, /* capabilities= */ 0);
@Test
public void updateCallDetails_uninitialized() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
.isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
assertWithMessage("Wrong controls").that(crossDeviceCall.getControls()).isEmpty();
@@ -51,8 +55,8 @@
@Test
public void updateCallDetails_ringing() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
@@ -66,8 +70,8 @@
@Test
public void updateCallDetails_ongoing() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
@@ -81,8 +85,8 @@
@Test
public void updateCallDetails_holding() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_HOLDING,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
@@ -95,8 +99,8 @@
@Test
public void updateCallDetails_cannotHold() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(
createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_MUTE));
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
@@ -109,8 +113,8 @@
@Test
public void updateCallDetails_cannotMute() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(
createCallDetails(Call.STATE_ACTIVE, Call.Details.CAPABILITY_HOLD));
assertWithMessage("Wrong status").that(crossDeviceCall.getStatus())
@@ -123,8 +127,8 @@
@Test
public void updateCallDetails_transitionRingingToOngoing() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
assertWithMessage("Wrong status for ringing state").that(crossDeviceCall.getStatus())
@@ -146,8 +150,8 @@
@Test
public void updateSilencedIfRinging_ringing_silenced() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_RINGING,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
crossDeviceCall.updateSilencedIfRinging();
@@ -161,8 +165,8 @@
@Test
public void updateSilencedIfRinging_notRinging_notSilenced() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE,
Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE));
crossDeviceCall.updateSilencedIfRinging();
@@ -177,8 +181,8 @@
@Test
public void getReadableCallerId_enterpriseCall_adminBlocked_ott() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = true;
crossDeviceCall.mIsOtt = true;
crossDeviceCall.updateCallDetails(
@@ -193,8 +197,8 @@
@Test
public void getReadableCallerId_enterpriseCall_adminUnblocked_ott() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = true;
crossDeviceCall.mIsOtt = true;
crossDeviceCall.updateCallDetails(
@@ -209,8 +213,8 @@
@Test
public void getReadableCallerId_enterpriseCall_adminBlocked_pstn() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = true;
crossDeviceCall.mIsOtt = false;
crossDeviceCall.updateCallDetails(
@@ -225,8 +229,8 @@
@Test
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_ott() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = false;
crossDeviceCall.mIsOtt = true;
crossDeviceCall.updateCallDetails(
@@ -241,8 +245,8 @@
@Test
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_ott() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = false;
crossDeviceCall.mIsOtt = true;
crossDeviceCall.updateCallDetails(
@@ -257,8 +261,8 @@
@Test
public void getReadableCallerId_nonEnterpriseCall_adminBlocked_pstn() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = false;
crossDeviceCall.mIsOtt = false;
crossDeviceCall.updateCallDetails(
@@ -273,8 +277,8 @@
@Test
public void getReadableCallerId_nonEnterpriseCall_adminUnblocked_pstn() {
final CrossDeviceCall crossDeviceCall = new CrossDeviceCall(
- InstrumentationRegistry.getTargetContext().getPackageManager(), /* call= */
- null, /* callAudioState= */ null);
+ InstrumentationRegistry.getTargetContext().getPackageManager(),
+ mUninitializedCallDetails, /* callAudioState= */ null);
crossDeviceCall.mIsEnterprise = false;
crossDeviceCall.mIsOtt = false;
crossDeviceCall.updateCallDetails(
@@ -294,6 +298,8 @@
parcelableCallBuilder.setCapabilities(capabilities);
parcelableCallBuilder.setState(state);
parcelableCallBuilder.setConferenceableCallIds(Collections.emptyList());
+ parcelableCallBuilder.setAccountHandle(new PhoneAccountHandle(
+ new ComponentName("com.google.test", "com.google.test.Activity"), "label"));
return Call.Details.createFromParcelableCall(parcelableCallBuilder.createParcelableCall());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
new file mode 100644
index 0000000..eec026cc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.companion.transport.CompanionTransportManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CrossDeviceSyncControllerTest {
+
+ private CrossDeviceSyncController mCrossDeviceSyncController;
+ @Mock
+ private CompanionTransportManager mMockCompanionTransportManager;
+ @Mock
+ private CrossDeviceCall mMockCrossDeviceCall;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mCrossDeviceSyncController = new CrossDeviceSyncController(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ mMockCompanionTransportManager);
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createCallUpdateMessage_emptyCallsAndRequests() {
+ final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage(new HashSet<>(),
+ InstrumentationRegistry.getInstrumentation().getContext().getUserId());
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Unexpectedly found a call").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ assertWithMessage("Unexpectedly found a request").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createEmptyMessage_emptyCallsAndRequests() {
+ final byte[] data = CrossDeviceSyncController.createEmptyMessage();
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Unexpectedly found a call").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ assertWithMessage("Unexpectedly found a request").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromSync_createCallUpdateMessage_hasCalls() {
+ when(mMockCrossDeviceCall.getId()).thenReturn(5L);
+ final String callerId = "Firstname Lastname";
+ when(mMockCrossDeviceCall.getReadableCallerId(anyBoolean())).thenReturn(callerId);
+ final String appName = "AppName";
+ when(mMockCrossDeviceCall.getCallingAppName()).thenReturn(appName);
+ final String appIcon = "ABCD";
+ when(mMockCrossDeviceCall.getCallingAppIcon()).thenReturn(appIcon.getBytes());
+ when(mMockCrossDeviceCall.getStatus()).thenReturn(android.companion.Telecom.Call.RINGING);
+ final Set<Integer> controls = Set.of(
+ android.companion.Telecom.Call.ACCEPT,
+ android.companion.Telecom.Call.REJECT,
+ android.companion.Telecom.Call.SILENCE);
+ when(mMockCrossDeviceCall.getControls()).thenReturn(controls);
+ final byte[] data = mCrossDeviceSyncController.createCallUpdateMessage(
+ new HashSet<>(List.of(mMockCrossDeviceCall)),
+ InstrumentationRegistry.getInstrumentation().getContext().getUserId());
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Wrong number of active calls").that(
+ callMetadataSyncData.getCalls()).hasSize(1);
+ final CallMetadataSyncData.Call call =
+ callMetadataSyncData.getCalls().stream().findAny().orElseThrow();
+ assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L);
+ assertWithMessage("Wrong app icon").that(new String(call.getAppIcon())).isEqualTo(appIcon);
+ assertWithMessage("Wrong app name").that(call.getAppName()).isEqualTo(appName);
+ assertWithMessage("Wrong caller id").that(call.getCallerId()).isEqualTo(callerId);
+ assertWithMessage("Wrong status").that(call.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.RINGING);
+ assertWithMessage("Wrong controls").that(call.getControls()).isEqualTo(controls);
+ assertWithMessage("Unexpectedly has requests").that(
+ callMetadataSyncData.getRequests()).isEmpty();
+ }
+
+ @Test
+ public void processTelecomDataFromMessage_createCallControlMessage_hasCallControlRequest() {
+ final byte[] data = CrossDeviceSyncController.createCallControlMessage(
+ /* callId= */ 5L, /* status= */ android.companion.Telecom.Call.ACCEPT);
+ final CallMetadataSyncData callMetadataSyncData =
+ mCrossDeviceSyncController.processTelecomDataFromSync(data);
+ assertWithMessage("Wrong number of requests").that(
+ callMetadataSyncData.getRequests()).hasSize(1);
+ final CallMetadataSyncData.Call call =
+ callMetadataSyncData.getRequests().stream().findAny().orElseThrow();
+ assertWithMessage("Wrong id").that(call.getId()).isEqualTo(5L);
+ assertWithMessage("Wrong app icon").that(call.getAppIcon()).isNull();
+ assertWithMessage("Wrong app name").that(call.getAppName()).isNull();
+ assertWithMessage("Wrong caller id").that(call.getCallerId()).isNull();
+ assertWithMessage("Wrong status").that(call.getStatus())
+ .isEqualTo(android.companion.Telecom.Call.UNKNOWN_STATUS);
+ assertWithMessage("Wrong controls").that(call.getControls())
+ .isEqualTo(Set.of(android.companion.Telecom.Call.ACCEPT));
+ assertWithMessage("Unexpectedly has active calls").that(
+ callMetadataSyncData.getCalls()).isEmpty();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 34b88b0..1e342f5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -130,6 +130,7 @@
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.IpcDataCache;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -1511,7 +1512,6 @@
* Validates that when the device owner is removed, the reset password token is cleared
*/
@Test
- @Ignore("b/277916462")
public void testClearDeviceOwner_clearResetPasswordToken() throws Exception {
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -2602,7 +2602,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetApplicationHiddenWithDO() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -2628,7 +2627,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception {
final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE;
final int MANAGED_PROFILE_ADMIN_UID =
@@ -4375,7 +4373,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -4387,7 +4384,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception {
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setupProfileOwnerOnUser0();
@@ -4399,7 +4395,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception {
setupProfileOwner();
assertExpectException(SecurityException.class, null,
@@ -4409,7 +4404,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
@@ -5383,7 +5377,6 @@
}
@Test
- @Ignore("b/277916462")
public void testResetPasswordWithToken() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -5418,7 +5411,6 @@
}
@Test
- @Ignore("b/277916462")
public void resetPasswordWithToken_NumericPin() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -5439,7 +5431,6 @@
}
@Test
- @Ignore("b/277916462")
public void resetPasswordWithToken_EmptyPassword() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -7260,7 +7251,6 @@
}
@Test
- @Ignore("b/277916462")
public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception {
setDeviceEncryptionPerUser();
setupProfileOwner();
@@ -7324,7 +7314,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception {
setupProfileOwner();
@@ -7344,7 +7333,6 @@
}
@Test
- @Ignore("b/277916462")
public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile()
throws Exception {
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
@@ -8539,6 +8527,46 @@
eq(FUSED_PROVIDER), any(), eq(getServices().executor), any());
}
+ /**
+ * Verifies that bundles with tons of moderately long strings are persisted correctly.
+ *
+ * Policy is serialized into binary XML and there is a limit on the max string length: 65535.
+ * This test ensures that as long as each string in the trust agent configuration is below this
+ * limit, the policy can be serialized and deserialized correctly, even when the total length
+ * of the configuration is above that limit. This should be the case because PersistableBundle
+ * contents are stored as XML subtrees rather than as strings.
+ */
+ @Test
+ public void testSetTrustAgentConfiguration_largeBundlePersisted() {
+ setAsProfileOwner(admin1);
+
+ ComponentName agent = new ComponentName("some.trust.agent", "some.trust.agent.Agent");
+ PersistableBundle configIn = new PersistableBundle();
+ String kilobyteString = new String(new char[1024]).replace('\0', 'A');
+ for (int i = 0; i < 1024; i++) {
+ configIn.putString("key-" + i, kilobyteString);
+ }
+
+ runAsCaller(mAdmin1Context, dpms, dpm -> {
+ dpm.setTrustAgentConfiguration(admin1, agent, configIn);
+ });
+
+ // Re-read policies to see if they were serialized/deserialized correctly.
+ initializeDpms();
+
+ List<PersistableBundle> configsOut = new ArrayList<>();
+ runAsCaller(mAdmin1Context, dpms, dpm -> {
+ configsOut.addAll(dpm.getTrustAgentConfiguration(admin1, agent));
+ });
+
+ assertThat(configsOut.size()).isEqualTo(1);
+ PersistableBundle configOut = configsOut.get(0);
+ assertThat(configOut.size()).isEqualTo(1024);
+ for (int i = 0; i < 1024; i++) {
+ assertThat(configOut.getString("key-" + i, null)).isEqualTo(kilobyteString);
+ }
+ }
+
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
userVpnUid, List.of(new AppOpsManager.OpEntry(
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 7536c79..1eec70d 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -649,9 +649,9 @@
assertEquals(0, mLogicalDisplayMapper.getDisplayLocked(device2)
.getLeadDisplayIdLocked());
assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device1)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
mLogicalDisplayMapper.setDeviceStateLocked(1, false);
advanceTime(1000);
@@ -661,10 +661,10 @@
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
mLogicalDisplayMapper.getDisplayLocked(device1)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
mLogicalDisplayMapper.getDisplayLocked(device2)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
mLogicalDisplayMapper.setDeviceStateLocked(2, false);
advanceTime(1000);
@@ -674,10 +674,10 @@
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
mLogicalDisplayMapper.getDisplayLocked(device1)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
assertEquals(DisplayDeviceConfig.DEFAULT_ID,
mLogicalDisplayMapper.getDisplayLocked(device2)
- .getThermalBrightnessThrottlingDataIdLocked());
+ .getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index 5ea3029..f6cf571 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -205,4 +205,19 @@
assertNotEquals(info3, info2);
assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling));
}
+
+ @Test
+ public void testSetThermalBrightnessThrottlingDataId() {
+ String brightnessThrottlingDataId = "throttling_data_id";
+ DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
+ mLogicalDisplay.setThermalBrightnessThrottlingDataIdLocked(brightnessThrottlingDataId);
+ DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
+ // Display info should only be updated when updateLocked is called
+ assertEquals(info2, info1);
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
+ assertNotEquals(info3, info2);
+ assertEquals(brightnessThrottlingDataId, info3.thermalBrightnessThrottlingDataId);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index d7b12e0..e6d3bbc 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -19,10 +19,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -66,25 +69,27 @@
@Mock
private HandlerExecutor mBrightnessChangeExecutor;
+ private final DisplayBrightnessController.Injector mInjector = new
+ DisplayBrightnessController.Injector() {
+ @Override
+ DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(
+ Context context, int displayId) {
+ return mDisplayBrightnessStrategySelector;
+ }
+ };
+
private DisplayBrightnessController mDisplayBrightnessController;
@Before
public void before() {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
- DisplayBrightnessController.Injector injector = new DisplayBrightnessController.Injector() {
- @Override
- DisplayBrightnessStrategySelector getDisplayBrightnessStrategySelector(
- Context context, int displayId) {
- return mDisplayBrightnessStrategySelector;
- }
- };
when(mBrightnessSetting.getBrightness()).thenReturn(Float.NaN);
when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(-1f);
when(mResources.getBoolean(
com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
.thenReturn(true);
- mDisplayBrightnessController = new DisplayBrightnessController(mContext, injector,
+ mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
DISPLAY_ID, DEFAULT_BRIGHTNESS, mBrightnessSetting, mOnBrightnessChangeRunnable,
mBrightnessChangeExecutor);
}
@@ -257,27 +262,6 @@
}
@Test
- public void testBrightnessNitsForDefaultDisplay() {
- float brightness = 0.3f;
- float nits = 500;
- AutomaticBrightnessController automaticBrightnessController =
- mock(AutomaticBrightnessController.class);
- when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness);
- when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
-
- mDisplayBrightnessController.setAutomaticBrightnessController(
- automaticBrightnessController);
- assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(),
- /* delta= */ 0);
-
- float newBrightness = 0.5f;
- float newNits = 700;
- when(automaticBrightnessController.convertToNits(newBrightness)).thenReturn(newNits);
- mDisplayBrightnessController.setBrightness(newBrightness);
- verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(newNits);
- }
-
- @Test
public void testConvertToNits() {
final float brightness = 0.5f;
final float nits = 300;
@@ -330,4 +314,48 @@
mDisplayBrightnessController.stop();
verify(mBrightnessSetting).unregisterListener(brightnessSettingListener);
}
+
+ @Test
+ public void testLoadNitBasedBrightnessSetting() {
+ // When the nits value is valid, the brightness is set from the old default display nits
+ // value
+ float nits = 200f;
+ float brightness = 0.3f;
+ AutomaticBrightnessController automaticBrightnessController =
+ mock(AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness);
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
+ mDisplayBrightnessController.setAutomaticBrightnessController(
+ automaticBrightnessController);
+ verify(mBrightnessSetting).setBrightness(brightness);
+ assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
+ clearInvocations(automaticBrightnessController, mBrightnessSetting);
+
+ // When the nits value is invalid, the brightness is resumed from where it was last set
+ nits = -1;
+ brightness = 0.4f;
+ when(automaticBrightnessController.convertToFloatScale(nits)).thenReturn(brightness);
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
+ when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
+ mDisplayBrightnessController.setAutomaticBrightnessController(
+ automaticBrightnessController);
+ verify(mBrightnessSetting, never()).setBrightness(brightness);
+ assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
+ clearInvocations(automaticBrightnessController, mBrightnessSetting);
+
+ // When the display is a non-default display, the brightness is resumed from where it was
+ // last set
+ int nonDefaultDisplayId = 1;
+ mDisplayBrightnessController = new DisplayBrightnessController(mContext, mInjector,
+ nonDefaultDisplayId, DEFAULT_BRIGHTNESS, mBrightnessSetting,
+ mOnBrightnessChangeRunnable, mBrightnessChangeExecutor);
+ brightness = 0.5f;
+ when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
+ mDisplayBrightnessController.setAutomaticBrightnessController(
+ automaticBrightnessController);
+ assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
+ verifyZeroInteractions(automaticBrightnessController);
+ verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
+ verify(mBrightnessSetting, never()).setBrightness(brightness);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index eb208d2..d9cf15b 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -40,6 +41,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.AutomaticBrightnessController;
+import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.BrightnessReason;
import org.junit.After;
@@ -262,12 +264,13 @@
float automaticScreenBrightness = 0.3f;
AutomaticBrightnessController automaticBrightnessController = mock(
AutomaticBrightnessController.class);
- when(automaticBrightnessController.getAutomaticScreenBrightness()).thenReturn(
- automaticScreenBrightness);
+ when(automaticBrightnessController.getAutomaticScreenBrightness(any(BrightnessEvent.class)))
+ .thenReturn(automaticScreenBrightness);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(automaticScreenBrightness,
- mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(), 0.0f);
+ mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
+ new BrightnessEvent(DISPLAY_ID)), 0.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index e492252..b2a3a57 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -84,7 +84,6 @@
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
-import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -159,9 +158,6 @@
LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-
- clearSmoothDisplaySetting();
- clearForcePeakRefreshRateSetting();
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
@@ -922,6 +918,7 @@
public void testLockFpsForLowZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -929,7 +926,6 @@
config.setRefreshRateInLowZone(90);
config.setLowDisplayBrightnessThresholds(new int[] { 10 });
config.setLowAmbientBrightnessThresholds(new int[] { 20 });
- config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -980,6 +976,7 @@
public void testLockFpsForHighZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -987,7 +984,6 @@
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -1035,123 +1031,16 @@
}
@Test
- public void testSmoothDisplay() {
- float defaultRefreshRate = 60;
- int defaultPeakRefreshRate = 100;
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- final FakeDeviceConfig config = mInjector.getDeviceConfig();
- config.setDefaultPeakRefreshRate(defaultPeakRefreshRate);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- // Default value of the setting
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultPeakRefreshRate);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setSmoothDisplayEnabled(false);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setSmoothDisplayEnabled(true);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
- }
-
- @Test
- public void testForcePeakRefreshRate() {
- float defaultRefreshRate = 60;
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
- director.start(sensorManager);
-
- setForcePeakRefreshRateEnabled(false);
- setSmoothDisplayEnabled(false);
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
-
- setForcePeakRefreshRateEnabled(true);
-
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */
- RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
- /* frameRateHigh= */ Float.POSITIVE_INFINITY);
- vote = director.getVote(Display.DEFAULT_DISPLAY,
- Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
- assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
- defaultRefreshRate);
- }
-
- @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2527,10 +2416,10 @@
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
- config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2828,30 +2717,10 @@
listener.onDisplayChanged(DISPLAY_ID);
}
- private void setSmoothDisplayEnabled(boolean enabled) {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY,
- enabled ? 1 : 0);
- mInjector.notifySmoothDisplaySettingChanged();
- waitForIdleSync();
- }
-
- private void clearSmoothDisplaySetting() {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
- mInjector.notifySmoothDisplaySettingChanged();
- waitForIdleSync();
- }
-
- private void setForcePeakRefreshRateEnabled(boolean enabled) {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, enabled ? 1 : 0);
- mInjector.notifyForcePeakRefreshRateSettingChanged();
- waitForIdleSync();
- }
-
- private void clearForcePeakRefreshRateSetting() {
- Settings.System.putInt(mContext.getContentResolver(),
- Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
- mInjector.notifySmoothDisplaySettingChanged();
+ private void setPeakRefreshRate(float fps) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+ fps);
+ mInjector.notifyPeakRefreshRateChanged();
waitForIdleSync();
}
@@ -2900,8 +2769,7 @@
private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private ContentObserver mBrightnessObserver;
- private ContentObserver mSmoothDisplaySettingObserver;
- private ContentObserver mForcePeakRefreshRateSettingObserver;
+ private ContentObserver mPeakRefreshRateObserver;
FakesInjector() {
mDeviceConfig = new FakeDeviceConfig();
@@ -2918,15 +2786,9 @@
}
@Override
- public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- mSmoothDisplaySettingObserver = observer;
- }
-
- @Override
- public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- mForcePeakRefreshRateSettingObserver = observer;
+ mPeakRefreshRateObserver = observer;
}
@Override
@@ -2976,17 +2838,10 @@
ApplicationProvider.getApplicationContext().getResources());
}
- void notifySmoothDisplaySettingChanged() {
- if (mSmoothDisplaySettingObserver != null) {
- mSmoothDisplaySettingObserver.dispatchChange(false /*selfChange*/,
- SMOOTH_DISPLAY_URI);
- }
- }
-
- void notifyForcePeakRefreshRateSettingChanged() {
- if (mForcePeakRefreshRateSettingObserver != null) {
- mForcePeakRefreshRateSettingObserver.dispatchChange(false /*selfChange*/,
- FORCE_PEAK_REFRESH_RATE_URI);
+ void notifyPeakRefreshRateChanged() {
+ if (mPeakRefreshRateObserver != null) {
+ mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+ PEAK_REFRESH_RATE_URI);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index a446e10..c53a7a7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -18,6 +18,7 @@
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_LONG;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
@@ -145,11 +146,12 @@
@Test
public void isValid_systemAudioModeStatus() {
assertMessageValidity("40:7E:00").isEqualTo(OK);
- assertMessageValidity("40:7E:01:01").isEqualTo(OK);
+ assertMessageValidity("40:7E:01").isEqualTo(OK);
assertMessageValidity("0F:7E:00").isEqualTo(ERROR_DESTINATION);
assertMessageValidity("F0:7E").isEqualTo(ERROR_SOURCE);
assertMessageValidity("40:7E").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:7E:01:1F:28").isEqualTo(ERROR_PARAMETER_LONG);
assertMessageValidity("40:7E:02").isEqualTo(ERROR_PARAMETER);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 4dd5e94..fd6eb92 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -761,6 +761,13 @@
assertThat(mHdmiControlServiceSpy.handleCecCommand(message))
.isEqualTo(Constants.ABORT_INVALID_OPERAND);
+
+ // Validating ERROR_PARAMETER_LONG will generate ABORT_INVALID_OPERAND.
+ // Taken from HdmiCecMessageValidatorTest#isValid_systemAudioModeStatus
+ HdmiCecMessage systemAudioModeStatus = HdmiUtils.buildMessage("40:7E:01:1F:28");
+
+ assertThat(mHdmiControlServiceSpy.handleCecCommand(systemAudioModeStatus))
+ .isEqualTo(Constants.ABORT_INVALID_OPERAND);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index d07831d..fd65807 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -20,16 +20,25 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodManagerServiceTests {
@@ -87,4 +96,25 @@
InputMethodManagerService.computeImeDisplayIdForTarget(
SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
}
+
+ @Test
+ public void testSoftInputShowHideHistoryDump_withNulls_doesntThrow() {
+ var writer = new StringWriter();
+ var history = new InputMethodManagerService.SoftInputShowHideHistory();
+ history.addEntry(new InputMethodManagerService.SoftInputShowHideHistory.Entry(
+ null,
+ null,
+ null,
+ SOFT_INPUT_STATE_UNSPECIFIED,
+ SoftInputShowHideReason.SHOW_SOFT_INPUT,
+ false,
+ null,
+ null,
+ null,
+ null));
+
+ history.dump(new PrintWriter(writer), "" /* prefix */);
+
+ // Asserts that dump doesn't throw an NPE.
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index d9cd77d..af144cf 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -37,7 +37,6 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.NonNull;
-import android.content.AttributionSource;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
@@ -50,7 +49,7 @@
import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteException;
-import android.permission.PermissionCheckerManager;
+import android.os.test.FakePermissionEnforcer;
import android.platform.test.annotations.Presubmit;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
@@ -90,7 +89,6 @@
private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
private final MockDependencies mDeps = new MockDependencies();
- private final MockPermissionEnforcer mPermissionEnforcer = new MockPermissionEnforcer();
private final class MockDependencies extends NetworkManagementService.Dependencies {
@Override
@@ -118,24 +116,6 @@
}
}
- private static final class MockPermissionEnforcer extends PermissionEnforcer {
- @Override
- protected int checkPermission(@NonNull String permission,
- @NonNull AttributionSource source) {
- String[] granted = new String [] {
- android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.OBSERVE_NETWORK_POLICY,
- android.Manifest.permission.SHUTDOWN
- };
- for (String p : granted) {
- if (p.equals(permission)) {
- return PermissionCheckerManager.PERMISSION_GRANTED;
- }
- }
- return PermissionCheckerManager.PERMISSION_HARD_DENIED;
- }
- }
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -145,12 +125,15 @@
eq(ConnectivityManager.class));
doReturn(mCm).when(mContext).getSystemService(eq(Context.CONNECTIVITY_SERVICE));
// The AIDL stub will use PermissionEnforcer to check permission from the caller.
- // Mock the service. See MockPermissionEnforcer above.
+ // Mock the service and grant the expected permissions.
+ FakePermissionEnforcer permissionEnforcer = new FakePermissionEnforcer();
+ permissionEnforcer.grant(android.Manifest.permission.NETWORK_SETTINGS);
+ permissionEnforcer.grant(android.Manifest.permission.OBSERVE_NETWORK_POLICY);
+ permissionEnforcer.grant(android.Manifest.permission.SHUTDOWN);
doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName(
eq(PermissionEnforcer.class));
- doReturn(mPermissionEnforcer).when(mContext).getSystemService(
+ doReturn(permissionEnforcer).when(mContext).getSystemService(
eq(Context.PERMISSION_ENFORCER_SERVICE));
-
// Start the service and wait until it connects to our socket.
mNMService = NetworkManagementService.create(mContext, mDeps);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 21a11bc..52bf244 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -40,6 +40,7 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -1611,7 +1612,8 @@
startSystem();
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
- verify(mNotifierMock, never()).onWakefulnessChangeStarted(anyInt(), anyInt(), anyLong());
+ verify(mNotifierMock, never()).onGlobalWakefulnessChangeStarted(anyInt(), anyInt(),
+ anyLong());
}
@Test
@@ -1630,7 +1632,7 @@
startSystem();
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
- verify(mNotifierMock).onWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP), anyInt(),
+ verify(mNotifierMock).onGlobalWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP), anyInt(),
anyLong());
}
@@ -2112,7 +2114,7 @@
}
@Test
- public void testMultiDisplay_defaultDozing_addNewDisplayDefaultGoesBackToDoze() {
+ public void testMultiDisplay_addNewDisplay_becomeGloballyAwakeButDefaultRemainsDozing() {
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
@@ -2142,6 +2144,7 @@
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
WAKEFULNESS_DOZING);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+ verify(mDreamManagerInternalMock).stopDream(anyBoolean(), anyString());
verify(mDreamManagerInternalMock).startDream(eq(true), anyString());
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
@@ -2152,7 +2155,10 @@
WAKEFULNESS_AWAKE);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
WAKEFULNESS_DOZING);
- verify(mDreamManagerInternalMock, times(2)).startDream(eq(true), anyString());
+
+ // Make sure there were no additional calls to stopDream or startDream
+ verify(mDreamManagerInternalMock, atMost(1)).stopDream(anyBoolean(), anyString());
+ verify(mDreamManagerInternalMock, atMost(1)).startDream(eq(true), anyString());
}
@Test
@@ -2169,7 +2175,7 @@
}
@Test
- public void testMultiDisplay_onlyOneDisplaySleeps_onWakefulnessChangedEventFires() {
+ public void testMultiDisplay_onlyOneDisplaySleeps_onWakefulnessChangedEventsFire() {
createService();
startSystem();
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -2177,12 +2183,14 @@
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
WAKEFULNESS_ASLEEP);
- verify(mNotifierMock).onPowerGroupWakefulnessChanged(eq(Display.DEFAULT_DISPLAY_GROUP),
- eq(WAKEFULNESS_ASLEEP), anyInt(), eq(WAKEFULNESS_ASLEEP));
+ verify(mNotifierMock).onGroupWakefulnessChangeStarted(eq(Display.DEFAULT_DISPLAY_GROUP),
+ eq(WAKEFULNESS_ASLEEP), eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
+ verify(mNotifierMock).onGlobalWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP),
+ eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
}
@Test
- public void testMultiDisplay_bothDisplaysSleep_onWakefulnessChangedEventFiresCorrectly() {
+ public void testMultiDisplay_bothDisplaysSleep_onWakefulnessChangedEventsFireCorrectly() {
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
@@ -2201,10 +2209,10 @@
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
- mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
- null, null);
- mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0,
- null, null);
+ mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0,
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
+ mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0,
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
@@ -2212,14 +2220,16 @@
assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
WAKEFULNESS_ASLEEP);
- verify(mNotifierMock).onPowerGroupWakefulnessChanged(eq(nonDefaultDisplayGroupId),
- eq(WAKEFULNESS_ASLEEP), anyInt(), eq(WAKEFULNESS_AWAKE));
- verify(mNotifierMock).onPowerGroupWakefulnessChanged(eq(Display.DEFAULT_DISPLAY_GROUP),
- eq(WAKEFULNESS_ASLEEP), anyInt(), eq(WAKEFULNESS_ASLEEP));
+ verify(mNotifierMock).onGroupWakefulnessChangeStarted(eq(nonDefaultDisplayGroupId),
+ eq(WAKEFULNESS_ASLEEP), eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
+ verify(mNotifierMock).onGroupWakefulnessChangeStarted(eq(Display.DEFAULT_DISPLAY_GROUP),
+ eq(WAKEFULNESS_ASLEEP), eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
+ verify(mNotifierMock).onGlobalWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP),
+ eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
}
@Test
- public void testMultiDisplay_separateWakeStates_onWakefulnessChangedEventFiresCorrectly() {
+ public void testMultiDisplay_separateWakeStates_onWakefulnessChangedEventsFireCorrectly() {
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
@@ -2255,10 +2265,53 @@
WAKEFULNESS_ASLEEP);
assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
WAKEFULNESS_AWAKE);
- verify(mNotifierMock).onPowerGroupWakefulnessChanged(eq(Display.DEFAULT_DISPLAY_GROUP),
- eq(WAKEFULNESS_ASLEEP), anyInt(), eq(WAKEFULNESS_AWAKE));
- verify(mNotifierMock, never()).onPowerGroupWakefulnessChanged(
- eq(nonDefaultDisplayGroupId), anyInt(), anyInt(), anyInt());
+ verify(mNotifierMock).onGroupWakefulnessChangeStarted(eq(nonDefaultDisplayGroupId),
+ eq(WAKEFULNESS_AWAKE), eq(PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED), anyLong());
+ verify(mNotifierMock).onGroupWakefulnessChangeStarted(eq(Display.DEFAULT_DISPLAY_GROUP),
+ eq(WAKEFULNESS_ASLEEP), eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), anyLong());
+ verify(mNotifierMock, never()).onGlobalWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP),
+ anyInt(), anyLong());
+ }
+
+ @Test
+ public void testMultiDisplay_oneDisplayGroupChanges_globalDoesNotChange() {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = nonDefaultDisplayGroupId;
+ when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(info);
+
+ createService();
+ startSystem();
+
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ long eventTime = mClock.now();
+ mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, eventTime, 0,
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0, null, null);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_ASLEEP);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ verify(mNotifierMock, never()).onGlobalWakefulnessChangeStarted(anyInt(), anyInt(),
+ anyLong());
+ verify(mNotifierMock, atMost(1)).onGroupWakefulnessChangeStarted(
+ eq(nonDefaultDisplayGroupId), eq(WAKEFULNESS_ASLEEP),
+ eq(PowerManager.GO_TO_SLEEP_REASON_APPLICATION), eq(eventTime));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 2665e19..3513557 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -202,9 +202,11 @@
.canonicalToCurrentPackageNames(any());
} catch (PackageManager.NameNotFoundException ignored) { }
- doReturn(mTelecomManager).when(mContext).getSystemService(eq(Context.TELECOM_SERVICE));
- doReturn(mNotificationManager).when(mContext)
- .getSystemService(eq(NotificationManager.class));
+ doReturn(false).when(mTelecomManager).isInCall();
+ doReturn(false).when(mTelecomManager).isRinging();
+ doReturn(mTelecomManager).when(mPhoneWindowManager).getTelecommService();
+ doNothing().when(mNotificationManager).silenceNotificationSound();
+ doReturn(mNotificationManager).when(mPhoneWindowManager).getNotificationService();
doReturn(mVibrator).when(mContext).getSystemService(eq(Context.VIBRATOR_SERVICE));
final PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
@@ -233,8 +235,9 @@
doNothing().when(mPhoneWindowManager).updateSettings();
doNothing().when(mPhoneWindowManager).screenTurningOn(anyInt(), any());
doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt());
- doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt());
- doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt());
+ doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt(), anyInt());
+ doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt());
+ doNothing().when(mPhoneWindowManager).lockNow(any());
mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl));
mPhoneWindowManager.systemReady();
@@ -249,6 +252,7 @@
void tearDown() {
mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
+ Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
}
@@ -322,6 +326,7 @@
void overrideDisplayState(int state) {
doReturn(state).when(mDisplay).getState();
+ doReturn(state == STATE_ON).when(mDisplayPolicy).isAwake();
Mockito.reset(mPowerManager);
}
@@ -388,6 +393,7 @@
}
void assertDreamRequest() {
+ waitForIdle();
verify(mDreamManagerInternal).requestDream();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 47f32fe..cb984f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3124,7 +3124,7 @@
.setSystemDecorations(true).build();
// Add a decor insets provider window.
final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
- assertTrue(navbar.providesNonDecorInsets()
+ assertTrue(navbar.providesDisplayDecorInsets()
&& squareDisplay.getDisplayPolicy().updateDecorInsetsInfo());
squareDisplay.sendNewConfiguration();
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 06b4ad9..7b4392b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -50,7 +50,6 @@
import android.util.Log;
import android.view.IWindowManager;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.server.am.AssistDataRequester;
@@ -155,7 +154,6 @@
}
@Test
- @FlakyTest(bugId = 130388718)
public void testRequestData() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
CALLER_ASSIST_SCREENSHOT_ALLOWED);
@@ -263,7 +261,6 @@
}
@Test
- @FlakyTest(bugId = 130388718)
public void testNoFetchScreenshots_expectNoScreenshotCallbacks() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
CALLER_ASSIST_SCREENSHOT_ALLOWED);
@@ -275,7 +272,6 @@
}
@Test
- @FlakyTest(bugId = 130388718)
public void testDisallowAssistScreenshot_expectNullScreenshotCallback() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
!CALLER_ASSIST_SCREENSHOT_ALLOWED);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index c8fdee0..353a8ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -45,6 +45,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -298,20 +300,27 @@
@Test
public void testUpdateDisplayConfigurationByDecor() {
+ doReturn(NO_CUTOUT).when(mDisplayContent).calculateDisplayCutoutForRotation(anyInt());
final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent);
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
final DisplayInfo di = mDisplayContent.getDisplayInfo();
final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
- assertTrue(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
+ assertTrue(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
mDisplayContent.sendNewConfiguration();
assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
- assertFalse(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
+ assertFalse(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
navbar.removeIfPossible();
assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
di.logicalHeight).mNonDecorInsets.bottom);
+
+ final WindowState statusBar = createStatusBarWithProvidedInsets(mDisplayContent);
+ assertTrue(statusBar.providesDisplayDecorInsets()
+ && displayPolicy.updateDecorInsetsInfo());
+ assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
+ di.logicalWidth, di.logicalHeight).mConfigInsets.top);
}
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 027f903..8cf2776 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -27,7 +27,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.After;
@@ -110,7 +109,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testProcessOneItem_Flush() throws Exception {
mFactory.setExpectedProcessedItemNumber(1);
mListener.setExpectedOnPreProcessItemCallbackTimes(1);
@@ -162,7 +160,6 @@
}
@Test
- @FlakyTest(bugId = 128526085)
public void testProcessTwoItems_OneAfterAnother() throws Exception {
// First item
mFactory.setExpectedProcessedItemNumber(1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 0db983c..c4d03be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -37,7 +37,6 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
@@ -53,7 +52,6 @@
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
-@FlakyTest
public class RefreshRatePolicyTest extends WindowTestsBase {
private static final int HI_MODE_ID = 1;
private static final float HI_REFRESH_RATE = 90;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index dfc453f..d173ce9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -1134,7 +1134,7 @@
TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController();
spyOn(controller);
mWm.mRoot.lockAllProfileTasks(profileUserId);
- verify(controller).notifyTaskProfileLocked(any());
+ verify(controller).notifyTaskProfileLocked(any(), eq(profileUserId));
// Create the work lock activity on top of the task
final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1144,7 +1144,7 @@
// Make sure the listener won't be notified again.
clearInvocations(controller);
mWm.mRoot.lockAllProfileTasks(profileUserId);
- verify(controller, never()).notifyTaskProfileLocked(any());
+ verify(controller, never()).notifyTaskProfileLocked(any(), anyInt());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index ff753f2..339162a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -98,7 +98,6 @@
mFinishCallbackLatch.countDown();
}
- @FlakyTest(bugId = 144611135)
@Test
public void testAnimation() throws Exception {
mSurfaceAnimationRunner
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
index 342ab83..4f45d5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
@@ -125,9 +125,10 @@
public void testSurfaceChangedOnRotation() {
final Instrumentation instrumentation = getInstrumentation();
final Context context = instrumentation.getContext();
- final Activity activity = instrumentation.startActivitySync(new Intent().setComponent(
+ final Intent intent = new Intent().setComponent(
new ComponentName(context, ActivityOptionsTest.MainActivity.class))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ final Activity activity = instrumentation.startActivitySync(intent);
final SurfaceView sv = new SurfaceView(activity);
final AtomicInteger surfaceChangedCount = new AtomicInteger();
instrumentation.runOnMainSync(() -> activity.setContentView(sv));
@@ -157,12 +158,27 @@
instrumentation.waitForIdleSync();
final int newRotation = activity.getResources().getConfiguration()
.windowConfiguration.getRotation();
+ if (rotation == newRotation) {
+ // The device might not support requested orientation.
+ activity.finishAndRemoveTask();
+ return;
+ }
final int count = surfaceChangedCount.get();
+ activity.moveTaskToBack(true /* nonRoot */);
+ instrumentation.getUiAutomation().syncInputTransactions();
+ context.startActivity(intent);
+ instrumentation.getUiAutomation().syncInputTransactions();
+ final int countAfterToFront = count - surfaceChangedCount.get();
activity.finishAndRemoveTask();
+
// The first count is triggered from creation, so the target number is 2.
- if (rotation != newRotation && count > 2) {
+ if (count > 2) {
fail("More than once surfaceChanged for rotation change: " + count);
}
+ if (countAfterToFront > 1) {
+ fail("More than once surfaceChanged for app transition with rotation change: "
+ + countAfterToFront);
+ }
}
private SurfaceControl buildTestSurface() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d43805a..c53addc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -63,6 +63,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Tests for the {@link TaskDisplayArea} container.
*
@@ -349,23 +351,24 @@
@Test
public void testReuseTaskAsRootTask() {
final Task candidateTask = createTask(mDisplayContent);
- final int type = ACTIVITY_TYPE_STANDARD;
- assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
- true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
- true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
- true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
- true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
- true /* reuseCandidate */);
+ List<Integer> activityTypesWithReusableRootTask = List.of(ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_RECENTS);
+ for (Integer type : activityTypesWithReusableRootTask) {
+ assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
+ true /* reuseCandidate */);
+ assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
+ true /* reuseCandidate */);
+ assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
+ true /* reuseCandidate */);
+ assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
+ true /* reuseCandidate */);
+ assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
+ true /* reuseCandidate */);
+ }
final int windowingMode = WINDOWING_MODE_FULLSCREEN;
assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_HOME, candidateTask,
false /* reuseCandidate */);
- assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask,
- false /* reuseCandidate */);
assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask,
false /* reuseCandidate */);
assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 790b154..453e468 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -52,7 +52,6 @@
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.After;
@@ -319,7 +318,6 @@
};
@Presubmit
- @FlakyTest(bugId = 150409355)
@Test
public void testNotifyTaskRequestedOrientationChanged() throws Exception {
final ArrayBlockingQueue<int[]> taskIdAndOrientationQueue = new ArrayBlockingQueue<>(10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 32033fb..adf3f39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -114,24 +114,35 @@
}
@Override
- public void startedWakingUp(@WakeReason int wakeReason) {
+ public void startedWakingUpGlobal(@WakeReason int reason) {
}
@Override
- public void finishedWakingUp(@WakeReason int wakeReason) {
+ public void finishedWakingUpGlobal(@WakeReason int reason) {
}
@Override
- public void startedGoingToSleep(@GoToSleepReason int sleepReason) {
+ public void startedGoingToSleepGlobal(@GoToSleepReason int reason) {
}
@Override
- public void finishedGoingToSleep(@GoToSleepReason int sleepReason) {
+ public void finishedGoingToSleepGlobal(@GoToSleepReason int reason) {
}
@Override
- public void onPowerGroupWakefulnessChanged(int groupId, int wakefulness,
- @GoToSleepReason int pmSleepReason, int globalWakefulness) {
+ public void startedWakingUp(int displayGroupId, @WakeReason int wakeReason) {
+ }
+
+ @Override
+ public void finishedWakingUp(int displayGroupId, @WakeReason int wakeReason) {
+ }
+
+ @Override
+ public void startedGoingToSleep(int displayGroupId, @GoToSleepReason int sleepReason) {
+ }
+
+ @Override
+ public void finishedGoingToSleep(int displayGroupId, @GoToSleepReason int sleepReason) {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java
index 0b1b877..2ae1172 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerThumbnailTest.java
@@ -26,7 +26,6 @@
import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -55,7 +54,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testDestroy_nullsSurface() {
final WindowContainerThumbnail t = buildThumbnail();
assertNotNull(t.getSurfaceControl());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index ba6b3b6..58bf184 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -27,6 +27,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -65,6 +66,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
@@ -89,6 +91,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.window.ClientWindowFrames;
import android.window.ScreenCapture;
import android.window.WindowContainerToken;
@@ -101,6 +104,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
/**
* Build/Install/Run:
@@ -272,6 +276,124 @@
}
@Test
+ public void testRelayout_firstLayout_dwpcHelperCalledWithCorrectFlags() {
+ // When doing the first layout, the initial flags should be reported as changed to
+ // keepActivityOnWindowFlagsChanged.
+ testRelayoutFlagChanges(
+ /*firstRelayout=*/ true,
+ /*startFlags=*/ FLAG_SECURE,
+ /*startPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*newFlags=*/ FLAG_SECURE,
+ /*newPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*expectedChangedFlags=*/ FLAG_SECURE,
+ /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*expectedFlagsValue=*/ FLAG_SECURE,
+ /*expectedPrivateFlagsValue=*/ PRIVATE_FLAG_TRUSTED_OVERLAY);
+ }
+
+ @Test
+ public void testRelayout_secondLayoutFlagAdded_dwpcHelperCalledWithCorrectFlags() {
+ testRelayoutFlagChanges(
+ /*firstRelayout=*/ false,
+ /*startFlags=*/ 0,
+ /*startPrivateFlags=*/ 0,
+ /*newFlags=*/ FLAG_SECURE,
+ /*newPrivateFlags=*/ 0,
+ /*expectedChangedFlags=*/ FLAG_SECURE,
+ /*expectedChangedPrivateFlags=*/ 0,
+ /*expectedFlagsValue=*/ FLAG_SECURE,
+ /*expectedPrivateFlagsValue=*/ 0);
+ }
+
+ @Test
+ public void testRelayout_secondLayoutMultipleFlagsAddOne_dwpcHelperCalledWithCorrectFlags() {
+ testRelayoutFlagChanges(
+ /*firstRelayout=*/ false,
+ /*startFlags=*/ FLAG_NOT_FOCUSABLE,
+ /*startPrivateFlags=*/ 0,
+ /*newFlags=*/ FLAG_SECURE | FLAG_NOT_FOCUSABLE,
+ /*newPrivateFlags=*/ 0,
+ /*expectedChangedFlags=*/ FLAG_SECURE,
+ /*expectedChangedPrivateFlags=*/ 0,
+ /*expectedFlagsValue=*/ FLAG_SECURE | FLAG_NOT_FOCUSABLE,
+ /*expectedPrivateFlagsValue=*/ 0);
+ }
+
+ @Test
+ public void testRelayout_secondLayoutPrivateFlagAdded_dwpcHelperCalledWithCorrectFlags() {
+ testRelayoutFlagChanges(
+ /*firstRelayout=*/ false,
+ /*startFlags=*/ 0,
+ /*startPrivateFlags=*/ 0,
+ /*newFlags=*/ 0,
+ /*newPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*expectedChangedFlags=*/ 0,
+ /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*expectedFlagsValue=*/ 0,
+ /*expectedPrivateFlagsValue=*/ PRIVATE_FLAG_TRUSTED_OVERLAY);
+ }
+
+ @Test
+ public void testRelayout_secondLayoutFlagsRemoved_dwpcHelperCalledWithCorrectFlags() {
+ testRelayoutFlagChanges(
+ /*firstRelayout=*/ false,
+ /*startFlags=*/ FLAG_SECURE,
+ /*startPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*newFlags=*/ 0,
+ /*newPrivateFlags=*/ 0,
+ /*expectedChangedFlags=*/ FLAG_SECURE,
+ /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ /*expectedFlagsValue=*/ 0,
+ /*expectedPrivateFlagsValue=*/ 0);
+ }
+
+ // Helper method to test relayout of a window, either for the initial layout, or a subsequent
+ // one, and makes sure that the flags and private flags changes and final values are properly
+ // reported to mDwpcHelper.keepActivityOnWindowFlagsChanged.
+ private void testRelayoutFlagChanges(boolean firstRelayout, int startFlags,
+ int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags,
+ int expectedChangedPrivateFlags, int expectedFlagsValue,
+ int expectedPrivateFlagsValue) {
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
+ win.mRelayoutCalled = !firstRelayout;
+ mWm.mWindowMap.put(win.mClient.asBinder(), win);
+ spyOn(mDisplayContent.mDwpcHelper);
+ when(mDisplayContent.mDwpcHelper.hasController()).thenReturn(true);
+
+ win.mAttrs.flags = startFlags;
+ win.mAttrs.privateFlags = startPrivateFlags;
+
+ LayoutParams newParams = new LayoutParams();
+ newParams.copyFrom(win.mAttrs);
+ newParams.flags = newFlags;
+ newParams.privateFlags = newPrivateFlags;
+
+ int seq = 1;
+ if (!firstRelayout) {
+ win.mRelayoutSeq = 1;
+ seq = 2;
+ }
+ mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
+ 0, new ClientWindowFrames(), new MergedConfiguration(),
+ new SurfaceControl(), new InsetsState(), new InsetsSourceControl.Array(),
+ new Bundle());
+
+ ArgumentCaptor<Integer> changedFlags = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> changedPrivateFlags = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> flagsValue = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> privateFlagsValue = ArgumentCaptor.forClass(Integer.class);
+
+ verify(mDisplayContent.mDwpcHelper).keepActivityOnWindowFlagsChanged(
+ any(ActivityInfo.class), changedFlags.capture(), changedPrivateFlags.capture(),
+ flagsValue.capture(), privateFlagsValue.capture());
+
+ assertThat(changedFlags.getValue()).isEqualTo(expectedChangedFlags);
+ assertThat(changedPrivateFlags.getValue()).isEqualTo(expectedChangedPrivateFlags);
+ assertThat(flagsValue.getValue()).isEqualTo(expectedFlagsValue);
+ assertThat(privateFlagsValue.getValue()).isEqualTo(expectedPrivateFlagsValue);
+ }
+
+ @Test
public void testMoveWindowTokenToDisplay_NullToken_DoNothing() {
mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 07244a4..a63807d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -471,6 +471,17 @@
return navbar;
}
+ WindowState createStatusBarWithProvidedInsets(DisplayContent dc) {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, dc, "statusBar");
+ final Binder owner = new Binder();
+ statusBar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars())
+ .setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
+ };
+ dc.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ return statusBar;
+ }
+
WindowState createAppWindow(Task task, int type, String name) {
final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
task.addChild(activity, 0);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 10b7952..ba17884 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -584,15 +584,34 @@
}
/**
+ * Returns true only if there is a terminal whose subtype and terminal type are the same as
+ * the given values.
* @hide
*/
- public boolean hasAudioTerminal(int subType) {
+ public boolean hasAudioTerminal(int subType, int terminalType) {
for (UsbDescriptor descriptor : mDescriptors) {
- if (descriptor instanceof UsbACInterface) {
- if (((UsbACInterface) descriptor).getSubclass()
- == UsbDescriptor.AUDIO_AUDIOCONTROL
- && ((UsbACInterface) descriptor).getSubtype()
- == subType) {
+ if (descriptor instanceof UsbACTerminal) {
+ if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL
+ && ((UsbACTerminal) descriptor).getSubtype() == subType
+ && ((UsbACTerminal) descriptor).getTerminalType() == terminalType) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true only if there is an interface whose subtype is the same as the given one and
+ * terminal type is different from the given one.
+ * @hide
+ */
+ public boolean hasAudioTerminalExcludeType(int subType, int excludedTerminalType) {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbACTerminal) {
+ if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL
+ && ((UsbACTerminal) descriptor).getSubtype() == subType
+ && ((UsbACTerminal) descriptor).getTerminalType() != excludedTerminalType) {
return true;
}
}
@@ -604,14 +623,21 @@
* @hide
*/
public boolean hasAudioPlayback() {
- return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL);
+ return hasAudioTerminalExcludeType(
+ UsbACInterface.ACI_OUTPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING)
+ && hasAudioTerminal(
+ UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING);
}
/**
* @hide
*/
public boolean hasAudioCapture() {
- return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL);
+ return hasAudioTerminalExcludeType(
+ UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING)
+ && hasAudioTerminal(
+ UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbTerminalTypes.TERMINAL_USB_STREAMING);
}
/**
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index bee75df..255db1e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -1043,7 +1043,13 @@
internalClearGlobalStateLocked();
if (mModule != null) {
mModule.detach();
- mModule = mModuleProvider.apply(this);
+ try {
+ // This is best effort
+ // TODO (b/279507851)
+ mModule = mModuleProvider.apply(this);
+ } catch (Exception e) {
+ mModule = null;
+ }
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 77e5317..913535e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -57,6 +57,7 @@
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.hardware.soundtrigger.SoundTriggerModule;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
@@ -134,6 +135,8 @@
private Object mLock;
private final SoundTriggerServiceStub mServiceStub;
private final LocalSoundTriggerService mLocalSoundTriggerService;
+
+ private ISoundTriggerMiddlewareService mMiddlewareService;
private SoundTriggerDbHelper mDbHelper;
private final EventLogger mServiceEventLogger = new EventLogger(256, "Service");
@@ -241,17 +244,18 @@
if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
mDbHelper = new SoundTriggerDbHelper(mContext);
}
+ mMiddlewareService = ISoundTriggerMiddlewareService.Stub.asInterface(
+ ServiceManager.waitForService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE));
+
}
// Must be called with cleared binder context.
- private static List<ModuleProperties> listUnderlyingModuleProperties(
+ private List<ModuleProperties> listUnderlyingModuleProperties(
Identity originatorIdentity) {
Identity middlemanIdentity = new Identity();
middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
- var service = ISoundTriggerMiddlewareService.Stub.asInterface(
- ServiceManager.waitForService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE));
try {
- return Arrays.stream(service.listModulesAsMiddleman(middlemanIdentity,
+ return Arrays.stream(mMiddlewareService.listModulesAsMiddleman(middlemanIdentity,
originatorIdentity))
.map(desc -> ConversionUtil.aidl2apiModuleDescriptor(desc))
.collect(Collectors.toList());
@@ -282,10 +286,9 @@
return new SoundTriggerHelper(
mContext,
eventLogger,
- (SoundTrigger.StatusListener statusListener) ->
- SoundTrigger.attachModuleAsMiddleman(
- moduleId, statusListener, null /* handler */,
- middlemanIdentity, originatorIdentity),
+ (SoundTrigger.StatusListener statusListener) -> new SoundTriggerModule(
+ mMiddlewareService, moduleId, statusListener,
+ Looper.getMainLooper(), middlemanIdentity, originatorIdentity),
moduleId,
() -> listUnderlyingModuleProperties(originatorIdentity)
);
@@ -293,7 +296,10 @@
// Helper to add session logger to the capacity limited detached list.
// If we are at capacity, remove the oldest, and retry
- private void addDetachedSessionLogger(EventLogger logger) {
+ private void detachSessionLogger(EventLogger logger) {
+ if (!mSessionEventLoggers.remove(logger)) {
+ return;
+ }
// Attempt to push to the top of the queue
while (!mDetachedSessionEventLoggers.offerFirst(logger)) {
// Remove the oldest element, if one still exists
@@ -869,8 +875,7 @@
private void detach() {
mSoundTriggerHelper.detach();
- mSessionEventLoggers.remove(mEventLogger);
- addDetachedSessionLogger(mEventLogger);
+ detachSessionLogger(mEventLogger);
}
private void enforceCallingPermission(String permission) {
@@ -1656,8 +1661,7 @@
private void detachInternal() {
mEventLogger.enqueue(new SessionEvent(Type.DETACH, null));
- mSessionEventLoggers.remove(mEventLogger);
- addDetachedSessionLogger(mEventLogger);
+ detachSessionLogger(mEventLogger);
mSoundTriggerHelper.detach();
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 13fe14c..00cedd7 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -78,7 +78,7 @@
public @NonNull
SoundTriggerModuleDescriptor[] listModules() {
Identity identity = getIdentity();
- enforcePermissionsForPreflight(identity);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
return mDelegate.listModules();
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 486945d..edaaf3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -913,8 +913,10 @@
}
// Handle case where all hotword detector sessions are destroyed with only the visual
// detector session left
- if (mDetectorSessions.size() == 1
- && mDetectorSessions.get(0) instanceof VisualQueryDetectorSession) {
+ boolean allHotwordDetectionServiceSessionsRemoved = mDetectorSessions.size() == 0
+ || (mDetectorSessions.size() == 1 && mDetectorSessions.get(0)
+ instanceof VisualQueryDetectorSession);
+ if (allHotwordDetectionServiceSessionsRemoved) {
unbindHotwordDetectionService();
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9dd2a61..26590c4 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -744,10 +744,6 @@
* state of calls in the self-managed {@link ConnectionService}. An example use-case is
* exposing these calls to an automotive device via its companion app.
* <p>
- * This meta-data can only be set for an {@link InCallService} which also sets
- * {@link #METADATA_IN_CALL_SERVICE_UI}. Only the default phone/dialer app, or a car-mode
- * {@link InCallService} can see self-managed calls.
- * <p>
* See also {@link Connection#PROPERTY_SELF_MANAGED}.
*/
public static final String METADATA_INCLUDE_SELF_MANAGED_CALLS =
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f27c23b..1888943 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5773,14 +5773,15 @@
* </ul>
* @hide
*/
- public static final String KEY_SA_DISABLE_POLICY_INT = KEY_PREFIX + "sa_disable_policy_int";
+ public static final String KEY_NR_SA_DISABLE_POLICY_INT =
+ KEY_PREFIX + "sa_disable_policy_int";
/** @hide */
@IntDef({
- SA_DISABLE_POLICY_NONE,
- SA_DISABLE_POLICY_WFC_ESTABLISHED,
- SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED,
- SA_DISABLE_POLICY_VOWIFI_REGISTERED
+ NR_SA_DISABLE_POLICY_NONE,
+ NR_SA_DISABLE_POLICY_WFC_ESTABLISHED,
+ NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED,
+ NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED
})
public @interface NrSaDisablePolicy {}
@@ -5788,14 +5789,14 @@
* Do not disables NR SA mode.
* @hide
*/
- public static final int SA_DISABLE_POLICY_NONE = 0;
+ public static final int NR_SA_DISABLE_POLICY_NONE = 0;
/**
* Disables NR SA mode when VoWiFi call is established in order to improve the delay or
* voice mute when the handover from ePDG to NR is not supported in UE or network.
* @hide
*/
- public static final int SA_DISABLE_POLICY_WFC_ESTABLISHED = 1;
+ public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED = 1;
/**
* Disables NR SA mode when VoWiFi call is established when VoNR is disabled in order to
@@ -5803,14 +5804,14 @@
* in UE or network.
* @hide
*/
- public static final int SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED = 2;
+ public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED = 2;
/**
* Disables NR SA mode when IMS is registered over WiFi in order to improve the delay or
* voice mute when the handover from ePDG to NR is not supported in UE or network.
* @hide
*/
- public static final int SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3;
+ public static final int NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3;
private Ims() {}
@@ -5883,7 +5884,7 @@
defaults.putInt(KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT, 30000);
defaults.putInt(KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT, 1800000);
defaults.putInt(KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT, 600000);
- defaults.putInt(KEY_SA_DISABLE_POLICY_INT, SA_DISABLE_POLICY_NONE);
+ defaults.putInt(KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE);
defaults.putIntArray(
KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8be074d..28ea5a6 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1186,6 +1186,7 @@
ApnSetting other = (ApnSetting) o;
return mEntryName.equals(other.mEntryName)
+ && Objects.equals(mId, other.mId)
&& Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
&& Objects.equals(mApnName, other.mApnName)
&& Objects.equals(mProxyAddress, other.mProxyAddress)
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 8ed60c1..b273ba2 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -571,6 +571,7 @@
int RIL_REQUEST_UPDATE_IMS_CALL_STATUS = 240;
int RIL_REQUEST_SET_N1_MODE_ENABLED = 241;
int RIL_REQUEST_IS_N1_MODE_ENABLED = 242;
+ int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 259;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 4e3ae0c..efd9b00 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -18,7 +18,7 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index f389e13..1f120d4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -19,9 +19,9 @@
package com.android.server.wm.flicker
import android.tools.common.PlatformConsts
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.common.datatypes.component.IComponentNameMatcher
import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.IComponentNameMatcher
import android.tools.common.traces.wm.WindowManagerTrace
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.helpers.WindowUtils
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
index c3cbb84..8638288 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.activityembedding
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 5dc2dd7..10b71ff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -40,20 +40,27 @@
* Launch an app [testApp] and wait animation to complete
* Press back button
* ```
+ *
* To run only the presubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
* ```
+ *
* To run only the postsubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
* ```
+ *
* To run only the flaky assertions add: `--
+ *
* ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index b042a14..e5bd350 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -40,20 +40,27 @@
* Launch an app [testApp] and wait animation to complete
* Press home button
* ```
+ *
* To run only the presubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
* ```
+ *
* To run only the postsubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
* ```
+ *
* To run only the flaky assertions add: `--
+ *
* ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index c4628aa..4570fa2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index e531bc0..6053f1d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.wm.WindowManagerState.Companion.STATE_RESUMED
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
index afb9fbf..94ac1a6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
class AppPairsHelper(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index 47dd4e9..c6fa1bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index d4f48fe..747cf37 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 73effbd..d172252 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.WindowManagerStateHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index a6e57d5..7a8d780 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
index d61a500..83a41ab 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -18,10 +18,10 @@
import android.app.Instrumentation
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.common.datatypes.component.IComponentMatcher
import android.tools.common.traces.Condition
import android.tools.common.traces.DeviceStateDump
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.helpers.IME_PACKAGE
import android.tools.device.traces.parsers.WindowManagerStateHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index fb5e1d2..b2aeb14 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
index 1ccac13..a670d68 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -17,15 +17,15 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.testapp.ActivityOptions
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.device.traces.parsers.toFlickerComponent
-import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.helpers.SYSTEMUI_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
class LetterboxAppHelper
@JvmOverloads
@@ -37,13 +37,21 @@
) : StandardAppHelper(instr, launcherName, component) {
fun clickRestart(wmHelper: WindowManagerStateHelper) {
- val restartButton = uiDevice.wait(Until.findObject(By.res(
- SYSTEMUI_PACKAGE, "size_compat_restart_button")), FIND_TIMEOUT)
+ val restartButton =
+ uiDevice.wait(
+ Until.findObject(By.res(SYSTEMUI_PACKAGE, "size_compat_restart_button")),
+ FIND_TIMEOUT
+ )
restartButton?.run { restartButton.click() } ?: error("Restart button not found")
// size compat mode restart confirmation dialog button
- val restartDialogButton = uiDevice.wait(Until.findObject(By.res(
- SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")), FIND_TIMEOUT)
+ val restartDialogButton =
+ uiDevice.wait(
+ Until.findObject(
+ By.res(SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")
+ ),
+ FIND_TIMEOUT
+ )
restartDialogButton?.run { restartDialogButton.click() }
?: error("Restart dialog button not found")
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
index ab91685..c98f1c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.toFlickerComponent
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
index e93e9c8..65175ef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
@@ -19,7 +19,7 @@
import android.app.Instrumentation
import android.content.Context
import android.provider.Settings
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.util.Log
import com.android.compatibility.common.util.SystemUtil
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index c547ad0..5b3d308 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.WindowManagerStateHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index 20ee3b9..ee65004 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index 78f8bcf..7665690 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.WindowManagerStateHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index c5a21a8..24e231c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -21,8 +21,8 @@
import android.media.session.MediaSessionManager
import android.tools.common.datatypes.Rect
import android.tools.common.datatypes.Region
-import android.tools.common.datatypes.component.IComponentMatcher
import android.tools.common.traces.ConditionsFactory
+import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.helpers.SYSTEMUI_PACKAGE
@@ -250,7 +250,8 @@
waitConditions = arrayOf(ConditionsFactory.hasPipWindow())
)
- wmHelper.StateSyncBuilder()
+ wmHelper
+ .StateSyncBuilder()
.withWindowSurfaceAppeared(this)
.withPipShown()
.waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 06e668e..cac3530 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index 94c90da..8366a7a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index 64af811..89c6c35 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.traces.parsers.toFlickerComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 316766a..895725c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
import android.tools.device.traces.parsers.WindowManagerStateHelper
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 34fa921..7e0632d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -18,8 +18,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 823328a..7f496d8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index df9d33b..cbe03dc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index 7954dd1..82c390b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 10ce5ea..c693ca7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 579c10f..8e33719 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,7 +18,7 @@
package com.android.server.wm.flicker.ime
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerTest
fun FlickerTest.imeLayerBecomesVisible() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 3f87aef..19bbf0c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -18,8 +18,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 6179fc2..f64ad53 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 690ed53..11bc7b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 866e858..46967db 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 6f22589..277b9155 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 231d0d7..9275d6a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
import android.tools.common.traces.ConditionsFactory
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 3c577ac..e6594c9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
index 3289bc6..ac05c76 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
@@ -41,4 +41,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index d0dc42f..26f88d2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -23,9 +23,9 @@
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -42,6 +42,7 @@
* Make sure no apps are running on the device
* Launch an app [testApp] and wait animation to complete
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
index f75d9ee..d9a99da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
@@ -49,4 +49,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
index 1a1d403..3d5a8e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.replacesLayer
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index b1a267a..b21777b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -78,9 +78,7 @@
override fun taskBarLayerIsVisibleAtStartAndEnd() {}
/** {@inheritDoc} */
- @Test
- @Ignore("Display is off at the start")
- override fun taskBarWindowIsAlwaysVisible() {}
+ @Test @Ignore("Display is off at the start") override fun taskBarWindowIsAlwaysVisible() {}
/** {@inheritDoc} */
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index e414325..ec92ca6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index 0b09e24..009d617 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
@@ -113,9 +113,7 @@
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @Presubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
@FlakyTest(bugId = 278227468)
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
index 730f78f..eae9ca1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
index 9c16b79..7bcb910 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
index 4aa78d4..8b4a613 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
@@ -44,4 +44,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 23cb1d4..425e674d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 00d7544..8e1b059 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -44,6 +44,7 @@
* Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
* complete (only this action is traced)
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 17f5638..1383ae3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerTest
@@ -47,6 +47,7 @@
* Lock the device.
* Launch an app on top of the lock screen [testApp] and wait animation to complete
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
@@ -161,9 +162,7 @@
super.appWindowBecomesFirstAndOnlyTopWindow()
/** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+ @Presubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
/** Checks the [ComponentNameMatcher.NAV_BAR] is visible at the end of the transition */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index e0db96f..87a14c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index cdd2d45..3385830 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -42,6 +42,7 @@
* Press home
* Relaunch an app [testApp] and wait animation to complete (only this action is traced)
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
index 9679059..d8b38b3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
@@ -43,4 +43,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
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 b848e63..b48611e 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
@@ -20,20 +20,20 @@
import android.os.Bundle
import android.os.Handler
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
import android.tools.common.traces.ConditionsFactory
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.R
-import com.android.server.wm.flicker.helpers.setRotation
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
-import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index be73547..d0fd732 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,11 +21,11 @@
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
-import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
-import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
-import android.tools.common.datatypes.component.ComponentSplashScreenMatcher
-import android.tools.common.datatypes.component.IComponentMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
+import android.tools.common.traces.component.ComponentSplashScreenMatcher
+import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index eadeef5..a8b80ad 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 1360495..96cd8ff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index d49f035..7e935f0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -21,7 +21,7 @@
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.common.datatypes.Rect
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index fe789a7..855ea3e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 3c0bbd6..fe9da33 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 4d010f3..0cbbb83 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.tools.common.ScenarioBuilder
-import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 129baab..07f6bca 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -2,7 +2,6 @@
android {
compileSdkVersion 33
- buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.prefabulated.touchlatency"
diff --git a/tests/TouchLatency/app/src/main/AndroidManifest.xml b/tests/TouchLatency/app/src/main/AndroidManifest.xml
index 25bb5d9..5743b25 100644
--- a/tests/TouchLatency/app/src/main/AndroidManifest.xml
+++ b/tests/TouchLatency/app/src/main/AndroidManifest.xml
@@ -30,12 +30,6 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
-
- <activity android:name=".TouchLatencyActivityPresentation"
- android:label="@string/app_name"
- android:parentActivityName=".TouchLatencyActivity"
- android:exported="true">
- </activity>
</application>
</manifest>
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index 3a520bc..7678633 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -16,8 +16,6 @@
package com.prefabulated.touchlatency;
-import android.app.ActivityOptions;
-import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
@@ -105,7 +103,6 @@
updateDisplayMode(menuItem, currentMode);
}
updateRefreshRateMenu(mMenu.findItem(R.id.frame_rate));
- updateMultiDisplayMenu(mMenu.findItem(R.id.multi_display));
}
@Override
@@ -186,20 +183,6 @@
mCurrentModeIndex = modeIndex;
}
- private void changeMultipleDisplays() {
- Intent intent = new Intent(this, TouchLatencyActivityPresentation.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
- ActivityOptions options = ActivityOptions.makeBasic();
- for (int i = 1; i < mDisplayManager.getDisplays().length; ++i) {
- // We assume the first display is already displaying the TouchLatencyActivity
- int displayId = mDisplayManager.getDisplays()[i].getDisplayId();
- options.setLaunchDisplayId(displayId);
- intent.putExtra(TouchLatencyActivityPresentation.DISPLAY_ID, displayId);
- startActivity(intent, options.toBundle());
- }
- }
-
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Trace.beginSection("TouchLatencyActivity onOptionsItemSelected");
@@ -218,10 +201,6 @@
changeDisplayMode(item);
break;
}
- case R.id.multi_display: {
- changeMultipleDisplays();
- break;
- }
}
Trace.endSection();
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
deleted file mode 100644
index 2602e6b..0000000
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.prefabulated.touchlatency;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Trace;
-import android.view.Display;
-import android.view.Display.Mode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.WindowManager;
-
-public class TouchLatencyActivityPresentation extends Activity {
- public static final String DISPLAY_ID = "DISPLAY_ID";
- private Mode[] mDisplayModes;
- private int mCurrentModeIndex;
- private int mDisplayId;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (getIntent().hasExtra(DISPLAY_ID)) {
- mDisplayId = (int) getIntent().getExtras().get(DISPLAY_ID);
- }
- Trace.beginSection(
- "TouchLatencyActivityPresentation::DisplayId::" + mDisplayId + " onCreate");
- setContentView(R.layout.activity_touch_latency);
-
- mTouchView = findViewById(R.id.canvasView);
-
- WindowManager wm = getWindowManager();
- Display display = wm.getDefaultDisplay();
- mDisplayModes = display.getSupportedModes();
- Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
-
- for (int i = 0; i < mDisplayModes.length; i++) {
- if (currentMode.getModeId() == mDisplayModes[i].getModeId()) {
- mCurrentModeIndex = i;
- break;
- }
- }
- Trace.endSection();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- Trace.beginSection(
- "TouchLatencyActivityPresentation::DisplayId:: "
- + mDisplayId + " onCreateOptionsMenu");
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
- if (mDisplayModes.length > 1) {
- MenuItem menuItem = menu.findItem(R.id.display_mode);
- Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
- updateDisplayMode(menuItem, currentMode);
- }
- Trace.endSection();
- return true;
- }
-
- private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
- int fps = (int) displayMode.getRefreshRate();
- menuItem.setTitle(fps + "hz");
- menuItem.setVisible(true);
- }
-
- public void changeDisplayMode(MenuItem item) {
- Window w = getWindow();
- WindowManager.LayoutParams params = w.getAttributes();
-
- int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length;
- params.preferredDisplayModeId = mDisplayModes[modeIndex].getModeId();
- w.setAttributes(params);
-
- updateDisplayMode(item, mDisplayModes[modeIndex]);
- mCurrentModeIndex = modeIndex;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- Trace.beginSection(
- "TouchLatencyActivityPresentation::DisplayId::"
- + mDisplayId + " onOptionsItemSelected");
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
- //noinspection SimplifiableIfStatement
- switch (id) {
- case R.id.action_settings: {
- mTouchView.changeMode(item);
- break;
- }
- case R.id.display_mode: {
- changeDisplayMode(item);
- break;
- }
- }
-
- Trace.endSection();
- return super.onOptionsItemSelected(item);
- }
-
- private TouchLatencyView mTouchView;
-}
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index 7169021..f637f71 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -30,9 +30,4 @@
android:title="@string/display_mode"
android:visible="false"
app:showAsAction="always" />
- <item
- android:id="@+id/multi_display"
- android:title="@string/multi_display"
- android:visible="false"
- app:showAsAction="ifRoom" />
</menu>
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index 381e55e..f52935b 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -6,7 +6,7 @@
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.4.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
index 758de96..7454180 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..8049c68 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/tests/TouchLatency/gradlew b/tests/TouchLatency/gradlew
index cccdd3d..1b6c787 100755
--- a/tests/TouchLatency/gradlew
+++ b/tests/TouchLatency/gradlew
@@ -1,78 +1,129 @@
-#!/usr/bin/env sh
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+# https://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.
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,7 +132,7 @@
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -89,84 +140,95 @@
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
fi
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
exec "$JAVACMD" "$@"
diff --git a/tests/TouchLatency/gradlew.bat b/tests/TouchLatency/gradlew.bat
index e95643d..ac1b06f 100644
--- a/tests/TouchLatency/gradlew.bat
+++ b/tests/TouchLatency/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -13,15 +29,18 @@
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,28 +64,14 @@
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index c6e675a..e207b01 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -185,6 +185,7 @@
*/
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
+ Objects.requireNonNull(extras);
mExtras = extras;
return this;
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java
deleted file mode 100644
index b18ab50..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.os.Parcel;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-/**
- * Unit tests for {@link HotspotNetworkConnectionStatus}.
- */
-@SmallTest
-public class HotspotNetworkConnectionStatusTest {
- private static final long DEVICE_ID = 11L;
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
- private static final String NETWORK_NAME = "TEST_NETWORK";
- private static final String HOTSPOT_SSID = "TEST_SSID";
- private static final String HOTSPOT_BSSID = "TEST _BSSID";
- private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
- private static final long DEVICE_ID_1 = 111L;
- private static final String BUNDLE_KEY = "INT-KEY";
- private static final int BUNDLE_VALUE = 1;
-
- /**
- * Verifies parcel serialization/deserialization.
- */
- @Test
- public void testParcelOperation() {
- HotspotNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
-
- Parcel parcelW = Parcel.obtain();
- status.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- HotspotNetworkConnectionStatus fromParcel =
- HotspotNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
-
- assertThat(fromParcel).isEqualTo(status);
- assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode());
- }
-
- /**
- * Verifies the Equals operation
- */
- @Test
- public void testEqualsOperation() {
- HotspotNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- HotspotNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
- assertThat(status1).isEqualTo(status2);
-
- HotspotNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
- .setStatus(CONNECTION_STATUS_TETHERING_TIMEOUT);
- assertThat(builder.build()).isNotEqualTo(status1);
-
- builder = buildConnectionStatusBuilder()
- .setHotspotNetwork(buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1).build());
- assertThat(builder.build()).isNotEqualTo(status1);
- }
-
- /**
- * Verifies the get methods return the expected data.
- */
- @Test
- public void testGetMethods() {
- HotspotNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
- assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_ENABLING_HOTSPOT);
- assertThat(status.getHotspotNetwork()).isEqualTo(buildHotspotNetworkBuilder().build());
- assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
- }
-
- @Test
- public void testHashCode() {
- HotspotNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- HotspotNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
-
- assertThat(status1.hashCode()).isEqualTo(status2.hashCode());
- }
-
- private HotspotNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
- return new HotspotNetworkConnectionStatus.Builder()
- .setStatus(CONNECTION_STATUS_ENABLING_HOTSPOT)
- .setHotspotNetwork(buildHotspotNetworkBuilder().build())
- .setExtras(buildBundle());
- }
-
- private Bundle buildBundle() {
- Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
- return bundle;
- }
-
- private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
- HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
- .setDeviceId(DEVICE_ID)
- .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
- .setHostNetworkType(NETWORK_TYPE)
- .setNetworkName(NETWORK_NAME)
- .setHotspotSsid(HOTSPOT_SSID)
- .setHotspotBssid(HOTSPOT_BSSID);
- Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
- return builder;
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
deleted file mode 100644
index 0827ffa..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_WIFI;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.util.ArraySet;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-/**
- * Unit tests for {@link HotspotNetwork}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class HotspotNetworkTest {
- private static final long DEVICE_ID = 11L;
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
- private static final String NETWORK_NAME = "TEST_NETWORK";
- private static final String HOTSPOT_SSID = "TEST_SSID";
- private static final String HOTSPOT_BSSID = "TEST _BSSID";
- private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
- private static final String BUNDLE_KEY = "INT-KEY";
- private static final int BUNDLE_VALUE = 1;
-
- private static final long DEVICE_ID_1 = 111L;
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_PHONE).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final int NETWORK_TYPE_1 = NETWORK_TYPE_WIFI;
- private static final String NETWORK_NAME_1 = "TEST_NETWORK1";
- private static final String HOTSPOT_SSID_1 = "TEST_SSID1";
- private static final String HOTSPOT_BSSID_1 = "TEST _BSSID1";
- private static final int[] HOTSPOT_SECURITY_TYPES_1 = {SECURITY_TYPE_PSK, SECURITY_TYPE_EAP};
-
- /**
- * Verifies parcel serialization/deserialization.
- */
- @Test
- public void testParcelOperation() {
- HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
-
- Parcel parcelW = Parcel.obtain();
- network.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- HotspotNetwork fromParcel = HotspotNetwork.CREATOR.createFromParcel(parcelR);
-
- assertThat(fromParcel).isEqualTo(network);
- assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode());
- }
-
- /**
- * Verifies the Equals operation
- */
- @Test
- public void testEqualsOperation() {
- HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
- HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
- assertThat(network1).isEqualTo(network2);
-
- HotspotNetwork.Builder builder = buildHotspotNetworkBuilder(true).setDeviceId(DEVICE_ID_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true).setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true).setHostNetworkType(NETWORK_TYPE_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true).setNetworkName(NETWORK_NAME_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true).setHotspotSsid(HOTSPOT_SSID_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true).setHotspotBssid(HOTSPOT_BSSID_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildHotspotNetworkBuilder(true);
- HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder(true);
- Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
-
- assertThat(builder1.build()).isNotEqualTo(builder.build());
- }
-
- /**
- * Verifies the get methods return the expected data.
- */
- @Test
- public void testGetMethods() {
- HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
- ArraySet<Integer> securityTypes = new ArraySet<>();
- Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
-
- assertThat(network.getDeviceId()).isEqualTo(DEVICE_ID);
- assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO);
- assertThat(network.getHostNetworkType()).isEqualTo(NETWORK_TYPE);
- assertThat(network.getNetworkName()).isEqualTo(NETWORK_NAME);
- assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID);
- assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID);
- assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes);
- assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
- }
-
- @Test
- public void testHashCode() {
- HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
- HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
-
- assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
- }
-
- @Test
- public void networkProviderInfoNotSet_shouldThrowException() {
- Exception e = assertThrows(IllegalArgumentException.class,
- () -> buildHotspotNetworkBuilder(false).build());
- assertThat(e.getMessage()).contains("NetworkProviderInfo");
- }
-
- private HotspotNetwork.Builder buildHotspotNetworkBuilder(boolean withNetworkProviderInfo) {
- HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
- .setDeviceId(DEVICE_ID)
- .setHostNetworkType(NETWORK_TYPE)
- .setNetworkName(NETWORK_NAME)
- .setHotspotSsid(HOTSPOT_SSID)
- .setHotspotBssid(HOTSPOT_BSSID)
- .setExtras(buildBundle());
- Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
- if (withNetworkProviderInfo) {
- builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
- }
- return builder;
- }
-
- private Bundle buildBundle() {
- Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
- return bundle;
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
deleted file mode 100644
index f98a0fc..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
-import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED;
-import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVE_FAILED;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.os.Parcel;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-/**
- * Unit tests for {@link KnownNetworkConnectionStatus}.
- */
-@SmallTest
-public class KnownNetworkConnectionStatusTest {
- private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
- private static final String SSID = "TEST_SSID";
- private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final String SSID_1 = "TEST_SSID1";
- private static final String BUNDLE_KEY = "INT-KEY";
- private static final int BUNDLE_VALUE = 1;
-
- /**
- * Verifies parcel serialization/deserialization.
- */
- @Test
- public void testParcelOperation() {
- KnownNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
-
- Parcel parcelW = Parcel.obtain();
- status.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- KnownNetworkConnectionStatus fromParcel =
- KnownNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
-
- assertThat(fromParcel).isEqualTo(status);
- assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode());
- }
-
- /**
- * Verifies the Equals operation
- */
- @Test
- public void testEqualsOperation() {
- KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
- assertThat(status1).isEqualTo(status2);
-
- KnownNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
- .setStatus(CONNECTION_STATUS_SAVE_FAILED);
- assertThat(builder.build()).isNotEqualTo(status1);
-
- builder = buildConnectionStatusBuilder()
- .setKnownNetwork(buildKnownNetworkBuilder().setSsid(SSID_1).build());
- assertThat(builder.build()).isNotEqualTo(status1);
- }
-
- /**
- * Verifies the get methods return the expected data.
- */
- @Test
- public void testGetMethods() {
- KnownNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
- assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_SAVED);
- assertThat(status.getKnownNetwork()).isEqualTo(buildKnownNetworkBuilder().build());
- assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
- }
-
- @Test
- public void testHashCode() {
- KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
-
- assertThat(status1.hashCode()).isEqualTo(status2.hashCode());
- }
-
- private KnownNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
- return new KnownNetworkConnectionStatus.Builder()
- .setStatus(CONNECTION_STATUS_SAVED)
- .setKnownNetwork(buildKnownNetworkBuilder().build())
- .setExtras(buildBundle());
- }
-
- private Bundle buildBundle() {
- Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
- return bundle;
- }
-
- private KnownNetwork.Builder buildKnownNetworkBuilder() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
- Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
- return builder;
- }
-
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
deleted file mode 100644
index 81d7b44..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF;
-import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.util.ArraySet;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-/**
- * Unit tests for {@link KnownNetwork}.
- */
-@SmallTest
-public class KnownNetworkTest {
-
- private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
- private static final String SSID = "TEST_SSID";
- private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final String BUNDLE_KEY = "INT-KEY";
- private static final int BUNDLE_VALUE = 1;
-
- private static final int NETWORK_SOURCE_1 = NETWORK_SOURCE_CLOUD_SELF;
- private static final String SSID_1 = "TEST_SSID1";
- private static final int[] SECURITY_TYPES_1 = {SECURITY_TYPE_PSK};
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 =
- new NetworkProviderInfo.Builder("TEST_NAME_1", "TEST_MODEL_1")
- .setDeviceType(DEVICE_TYPE_PHONE).setConnectionStrength(3)
- .setBatteryPercentage(33).build();
-
- /**
- * Verifies parcel serialization/deserialization.
- */
- @Test
- public void testParcelOperation() {
- KnownNetwork network = buildKnownNetworkBuilder().build();
-
- Parcel parcelW = Parcel.obtain();
- network.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- KnownNetwork fromParcel = KnownNetwork.CREATOR.createFromParcel(parcelR);
-
- assertThat(fromParcel).isEqualTo(network);
- assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode());
- }
-
- /**
- * Verifies the Equals operation
- */
- @Test
- public void testEqualsOperation() {
- KnownNetwork network1 = buildKnownNetworkBuilder().build();
- KnownNetwork network2 = buildKnownNetworkBuilder().build();
- assertThat(network1).isEqualTo(network2);
-
- KnownNetwork.Builder builder = buildKnownNetworkBuilder()
- .setNetworkSource(NETWORK_SOURCE_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildKnownNetworkBuilder().setSsid(SSID_1);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildKnownNetworkBuilder();
- Arrays.stream(SECURITY_TYPES_1).forEach(builder::addSecurityType);
- assertThat(builder.build()).isNotEqualTo(network1);
-
- builder = buildKnownNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
- assertThat(builder.build()).isNotEqualTo(network1);
- }
-
- /**
- * Verifies the get methods return the expected data.
- */
- @Test
- public void testGetMethods() {
- KnownNetwork network = buildKnownNetworkBuilder().build();
- ArraySet<Integer> securityTypes = new ArraySet<>();
- Arrays.stream(SECURITY_TYPES).forEach(securityTypes::add);
-
- assertThat(network.getNetworkSource()).isEqualTo(NETWORK_SOURCE);
- assertThat(network.getSsid()).isEqualTo(SSID);
- assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes);
- assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO);
- assertThat(network.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
- }
-
- @Test
- public void testHashCode() {
- KnownNetwork network1 = buildKnownNetworkBuilder().build();
- KnownNetwork network2 = buildKnownNetworkBuilder().build();
-
- assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
- }
-
- private KnownNetwork.Builder buildKnownNetworkBuilder() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
- .setExtras(buildBundle());
- Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
- return builder;
- }
-
- private Bundle buildBundle() {
- Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
- return bundle;
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
deleted file mode 100644
index 4aa9ca6..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_LAPTOP;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.os.Parcel;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Unit tests for {@link NetworkProviderInfo}.
- */
-@SmallTest
-public class NetworkProviderInfoTest {
-
- private static final int DEVICE_TYPE = DEVICE_TYPE_PHONE;
- private static final String DEVICE_NAME = "TEST_NAME";
- private static final String DEVICE_MODEL = "TEST_MODEL";
- private static final int BATTERY_PERCENTAGE = 50;
- private static final int CONNECTION_STRENGTH = 2;
- private static final String BUNDLE_KEY = "INT-KEY";
- private static final int BUNDLE_VALUE = 1;
-
- private static final int DEVICE_TYPE_1 = DEVICE_TYPE_LAPTOP;
- private static final String DEVICE_NAME_1 = "TEST_NAME1";
- private static final String DEVICE_MODEL_1 = "TEST_MODEL1";
- private static final int BATTERY_PERCENTAGE_1 = 30;
- private static final int CONNECTION_STRENGTH_1 = 1;
-
- /**
- * Verifies parcel serialization/deserialization.
- */
- @Test
- public void testParcelOperation() {
- NetworkProviderInfo info = buildNetworkProviderInfoBuilder().build();
-
- Parcel parcelW = Parcel.obtain();
- info.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- NetworkProviderInfo fromParcel = NetworkProviderInfo.CREATOR.createFromParcel(parcelR);
-
- assertThat(fromParcel).isEqualTo(info);
- assertThat(fromParcel.hashCode()).isEqualTo(info.hashCode());
- }
-
- /**
- * Verifies the Equals operation
- */
- @Test
- public void testEqualsOperation() {
- NetworkProviderInfo info1 = buildNetworkProviderInfoBuilder().build();
- NetworkProviderInfo info2 = buildNetworkProviderInfoBuilder().build();
- assertThat(info1).isEqualTo(info2);
-
- NetworkProviderInfo.Builder builder = buildNetworkProviderInfoBuilder().setDeviceType(
- DEVICE_TYPE_1);
- assertThat(builder.build()).isNotEqualTo(info1);
-
- builder = buildNetworkProviderInfoBuilder().setDeviceName(DEVICE_NAME_1);
- assertThat(builder.build()).isNotEqualTo(info1);
-
- builder = buildNetworkProviderInfoBuilder().setModelName(DEVICE_MODEL_1);
- assertThat(builder.build()).isNotEqualTo(info1);
-
- builder = buildNetworkProviderInfoBuilder()
- .setBatteryPercentage(BATTERY_PERCENTAGE_1);
- assertThat(builder.build()).isNotEqualTo(info1);
-
- builder = buildNetworkProviderInfoBuilder()
- .setConnectionStrength(CONNECTION_STRENGTH_1);
- assertThat(builder.build()).isNotEqualTo(info1);
- }
-
- /**
- * Verifies the get methods return the expected data.
- */
- @Test
- public void testGetMethods() {
- NetworkProviderInfo info = buildNetworkProviderInfoBuilder().build();
- assertThat(info.getDeviceType()).isEqualTo(DEVICE_TYPE);
- assertThat(info.getDeviceName()).isEqualTo(DEVICE_NAME);
- assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL);
- assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE);
- assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH);
- assertThat(info.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
- }
-
- @Test
- public void testHashCode() {
- NetworkProviderInfo info1 = buildNetworkProviderInfoBuilder().build();
- NetworkProviderInfo info2 = buildNetworkProviderInfoBuilder().build();
-
- assertThat(info1.hashCode()).isEqualTo(info2.hashCode());
- }
-
- private NetworkProviderInfo.Builder buildNetworkProviderInfoBuilder() {
- return new NetworkProviderInfo.Builder(DEVICE_NAME, DEVICE_MODEL).setDeviceType(DEVICE_TYPE)
- .setBatteryPercentage(BATTERY_PERCENTAGE)
- .setConnectionStrength(CONNECTION_STRENGTH)
- .setExtras(buildBundle());
- }
-
- private Bundle buildBundle() {
- Bundle bundle = new Bundle();
- bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE);
- return bundle;
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
deleted file mode 100644
index a03a6c2..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
-import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
-import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.res.Resources;
-import android.net.wifi.sharedconnectivity.service.ISharedConnectivityService;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Unit tests for {@link SharedConnectivityManager}.
- */
-@SmallTest
-public class SharedConnectivityManagerTest {
- private static final long DEVICE_ID = 11L;
- private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
- new NetworkProviderInfo.Builder("TEST_NAME", "TEST_MODEL")
- .setDeviceType(DEVICE_TYPE_TABLET).setConnectionStrength(2)
- .setBatteryPercentage(50).build();
- private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
- private static final String NETWORK_NAME = "TEST_NETWORK";
- private static final String HOTSPOT_SSID = "TEST_SSID";
- private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
-
- private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
- private static final String SSID = "TEST_SSID";
- private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
-
- private static final String SERVICE_PACKAGE_NAME = "TEST_PACKAGE";
- private static final String SERVICE_INTENT_ACTION = "TEST_INTENT_ACTION";
-
-
- @Mock
- Context mContext;
- @Mock
- ISharedConnectivityService mService;
- @Mock
- Executor mExecutor;
- @Mock
- SharedConnectivityClientCallback mClientCallback, mClientCallback2;
- @Mock
- Resources mResources;
- @Mock
- ISharedConnectivityService.Stub mIBinder;
-
- private static final ComponentName COMPONENT_NAME =
- new ComponentName("dummypkg", "dummycls");
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- setResources(mContext);
- }
-
- @Test
- public void resourcesNotDefined_createShouldReturnNull() {
- when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException());
-
- assertThat(SharedConnectivityManager.create(mContext)).isNull();
- }
-
- @Test
- public void resourceStringsAreEmpty_createShouldReturnNull() {
- when(mResources.getString(anyInt())).thenReturn("");
-
- assertThat(SharedConnectivityManager.create(mContext)).isNull();
- }
-
- @Test
- public void bindingToServiceOnFirstCallbackRegistration() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.registerCallback(mExecutor, mClientCallback);
-
- verify(mContext).bindService(any(Intent.class), any(ServiceConnection.class), anyInt());
- }
-
- @Test
- public void bindIsCalledOnceOnMultipleCallbackRegistrations() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-
- manager.registerCallback(mExecutor, mClientCallback);
- verify(mContext, times(1)).bindService(any(Intent.class), any(ServiceConnection.class),
- anyInt());
-
- manager.registerCallback(mExecutor, mClientCallback2);
- verify(mContext, times(1)).bindService(any(Intent.class), any(ServiceConnection.class),
- anyInt());
- }
-
- @Test
- public void unbindIsCalledOnLastCallbackUnregistrations() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.registerCallback(mExecutor, mClientCallback2);
- manager.unregisterCallback(mClientCallback);
- verify(mContext, never()).unbindService(
- any(ServiceConnection.class));
-
- manager.unregisterCallback(mClientCallback2);
- verify(mContext, times(1)).unbindService(
- any(ServiceConnection.class));
- }
-
- @Test
- public void registerCallback_serviceNotConnected_canUnregisterAndReregister() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.unregisterCallback(mClientCallback);
- manager.registerCallback(mExecutor, mClientCallback);
-
- verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
- }
-
- @Test
- public void registerCallback_serviceConnected() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.registerCallback(mExecutor, mClientCallback);
-
- verify(mService).registerCallback(any());
- verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
- }
-
- @Test
- public void registerCallback_doubleRegistration_shouldFail() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.registerCallback(mExecutor, mClientCallback);
-
- verify(mClientCallback).onRegisterCallbackFailed(any(IllegalStateException.class));
- }
-
- @Test
- public void registerCallback_remoteException_shouldFail() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).registerCallback(any());
-
- manager.registerCallback(mExecutor, mClientCallback);
-
- verify(mClientCallback).onRegisterCallbackFailed(any(RemoteException.class));
- }
-
- @Test
- public void unregisterCallback_withoutRegisteringFirst_serviceNotConnected_shouldFail() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
- }
-
- @Test
- public void unregisterCallback_withoutRegisteringFirst_serviceConnected_shouldFail() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
- }
-
- @Test
- public void unregisterCallback() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.registerCallback(mExecutor, mClientCallback);
-
- assertThat(manager.unregisterCallback(mClientCallback)).isTrue();
- verify(mService).unregisterCallback(any());
- }
-
- @Test
- public void unregisterCallback_doubleUnregistration_serviceConnected_shouldFail() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.unregisterCallback(mClientCallback);
-
- assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
- }
-
- @Test
- public void unregisterCallback_doubleUnregistration_serviceNotConnected_shouldFail() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.unregisterCallback(mClientCallback);
-
- assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
- }
-
- @Test
- public void unregisterCallback_remoteException_shouldFail() throws Exception {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- doThrow(new RemoteException()).when(mService).unregisterCallback(any());
-
- assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
- }
-
- @Test
- public void onServiceConnected() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
-
- verify(mClientCallback).onServiceConnected();
- }
-
- @Test
- public void onServiceDisconnected() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-
- manager.registerCallback(mExecutor, mClientCallback);
- manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
- manager.getServiceConnection().onServiceDisconnected(COMPONENT_NAME);
-
- verify(mClientCallback).onServiceDisconnected();
- }
-
-
- @Test
- public void connectHotspotNetwork_serviceNotConnected_shouldFail() {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.connectHotspotNetwork(network)).isFalse();
- }
-
- @Test
- public void connectHotspotNetwork() throws RemoteException {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.connectHotspotNetwork(network);
-
- verify(mService).connectHotspotNetwork(network);
- }
-
- @Test
- public void connectHotspotNetwork_remoteException_shouldFail() throws RemoteException {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).connectHotspotNetwork(network);
-
- assertThat(manager.connectHotspotNetwork(network)).isFalse();
- }
-
- @Test
- public void disconnectHotspotNetwork_serviceNotConnected_shouldFail() {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.disconnectHotspotNetwork(network)).isFalse();
- }
-
- @Test
- public void disconnectHotspotNetwork() throws RemoteException {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.disconnectHotspotNetwork(network);
-
- verify(mService).disconnectHotspotNetwork(network);
- }
-
- @Test
- public void disconnectHotspotNetwork_remoteException_shouldFail() throws RemoteException {
- HotspotNetwork network = buildHotspotNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).disconnectHotspotNetwork(any());
-
- assertThat(manager.disconnectHotspotNetwork(network)).isFalse();
- }
-
- @Test
- public void connectKnownNetwork_serviceNotConnected_shouldFail() throws RemoteException {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.connectKnownNetwork(network)).isFalse();
- }
-
- @Test
- public void connectKnownNetwork() throws RemoteException {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.connectKnownNetwork(network);
-
- verify(mService).connectKnownNetwork(network);
- }
-
- @Test
- public void connectKnownNetwork_remoteException_shouldFail() throws RemoteException {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).connectKnownNetwork(network);
-
- assertThat(manager.connectKnownNetwork(network)).isFalse();
- }
-
- @Test
- public void forgetKnownNetwork_serviceNotConnected_shouldFail() {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.forgetKnownNetwork(network)).isFalse();
- }
-
- @Test
- public void forgetKnownNetwork_serviceConnected() throws RemoteException {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
-
- manager.forgetKnownNetwork(network);
-
- verify(mService).forgetKnownNetwork(network);
- }
-
- @Test
- public void forgetKnownNetwork_remoteException_shouldFail() throws RemoteException {
- KnownNetwork network = buildKnownNetwork();
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network);
-
- assertThat(manager.forgetKnownNetwork(network)).isFalse();
- }
-
- @Test
- public void getHotspotNetworks_serviceNotConnected_shouldReturnNull() {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.getHotspotNetworks()).isNull();
- }
-
- @Test
- public void getHotspotNetworks_remoteException_shouldReturnNull() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getHotspotNetworks();
-
- assertThat(manager.getHotspotNetworks()).isNull();
- }
-
- @Test
- public void getHotspotNetworks_shouldReturnNetworksList() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- List<HotspotNetwork> networks = List.of(buildHotspotNetwork());
- manager.setService(mService);
- when(mService.getHotspotNetworks()).thenReturn(networks);
-
- assertThat(manager.getHotspotNetworks()).containsExactly(buildHotspotNetwork());
- }
-
- @Test
- public void getKnownNetworks_serviceNotConnected_shouldReturnNull()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.getKnownNetworks()).isNull();
- }
-
- @Test
- public void getKnownNetworks_remoteException_shouldReturnNull() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getKnownNetworks();
-
- assertThat(manager.getKnownNetworks()).isNull();
- }
-
- @Test
- public void getKnownNetworks_shouldReturnNetworksList() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- List<KnownNetwork> networks = List.of(buildKnownNetwork());
- manager.setService(mService);
- when(mService.getKnownNetworks()).thenReturn(networks);
-
- assertThat(manager.getKnownNetworks()).containsExactly(buildKnownNetwork());
- }
-
- @Test
- public void getSettingsState_serviceNotConnected_shouldReturnNull() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.getSettingsState()).isNull();
- }
-
- @Test
- public void getSettingsState_remoteException_shouldReturnNull() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getSettingsState();
-
- assertThat(manager.getSettingsState()).isNull();
- }
-
- @Test
- public void getSettingsState_serviceConnected_shouldReturnState() throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- SharedConnectivitySettingsState state =
- new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true)
- .setExtras(new Bundle()).build();
- manager.setService(mService);
- when(mService.getSettingsState()).thenReturn(state);
-
- assertThat(manager.getSettingsState()).isEqualTo(state);
- }
-
- @Test
- public void getHotspotNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.getHotspotNetworkConnectionStatus()).isNull();
- }
-
- @Test
- public void getHotspotNetworkConnectionStatus_remoteException_shouldReturnNull()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getHotspotNetworkConnectionStatus();
-
- assertThat(manager.getHotspotNetworkConnectionStatus()).isNull();
- }
-
- @Test
- public void getHotspotNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- HotspotNetworkConnectionStatus status = new HotspotNetworkConnectionStatus.Builder()
- .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT)
- .setExtras(new Bundle()).build();
- manager.setService(mService);
- when(mService.getHotspotNetworkConnectionStatus()).thenReturn(status);
-
- assertThat(manager.getHotspotNetworkConnectionStatus()).isEqualTo(status);
- }
-
- @Test
- public void getKnownNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(null);
-
- assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
- }
-
- @Test
- public void getKnownNetworkConnectionStatus_remoteException_shouldReturnNull()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getKnownNetworkConnectionStatus();
-
- assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
- }
-
- @Test
- public void getKnownNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
- throws RemoteException {
- SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- KnownNetworkConnectionStatus status = new KnownNetworkConnectionStatus.Builder()
- .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED)
- .setExtras(new Bundle()).build();
- manager.setService(mService);
- when(mService.getKnownNetworkConnectionStatus()).thenReturn(status);
-
- assertThat(manager.getKnownNetworkConnectionStatus()).isEqualTo(status);
- }
-
- private void setResources(@Mock Context context) {
- when(context.getResources()).thenReturn(mResources);
- when(mResources.getString(anyInt()))
- .thenReturn(SERVICE_PACKAGE_NAME, SERVICE_INTENT_ACTION);
- }
-
- private HotspotNetwork buildHotspotNetwork() {
- HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
- .setDeviceId(DEVICE_ID)
- .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
- .setHostNetworkType(NETWORK_TYPE)
- .setNetworkName(NETWORK_NAME)
- .setHotspotSsid(HOTSPOT_SSID);
- Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
- return builder.build();
- }
-
- private KnownNetwork buildKnownNetwork() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
- Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
- return builder.build();
- }
-}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
deleted file mode 100644
index d6e7138..0000000
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.sharedconnectivity.app;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertThrows;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Parcel;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Unit tests for {@link SharedConnectivitySettingsState}.
- */
-@SmallTest
-public class SharedConnectivitySettingsStateTest {
- private static final boolean INSTANT_TETHER_STATE = true;
- private static final String INTENT_ACTION = "instant.tether.settings";
-
- private static final boolean INSTANT_TETHER_STATE_1 = false;
- private static final String INTENT_ACTION_1 = "instant.tether.settings1";
-
- @Test
- public void pendingIntentMutable_buildShouldThrow() {
- SharedConnectivitySettingsState.Builder builder =
- new SharedConnectivitySettingsState.Builder()
- .setInstantTetherEnabled(INSTANT_TETHER_STATE)
- .setInstantTetherSettingsPendingIntent(PendingIntent.getActivity(
- ApplicationProvider.getApplicationContext(), 0,
- new Intent(INTENT_ACTION).setComponent(new ComponentName(
- "com.test.package", "TestClass")),
- PendingIntent.FLAG_MUTABLE));
-
- Exception e = assertThrows(IllegalArgumentException.class, builder::build);
- assertThat(e.getMessage()).contains("Pending intent must be immutable");
- }
-
- @Test
- public void parcelOperation() {
- SharedConnectivitySettingsState state = buildSettingsStateBuilder(INTENT_ACTION).build();
-
- Parcel parcel = Parcel.obtain();
- state.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- SharedConnectivitySettingsState fromParcel =
- SharedConnectivitySettingsState.CREATOR.createFromParcel(parcel);
-
- assertThat(fromParcel).isEqualTo(state);
- assertThat(fromParcel.hashCode()).isEqualTo(state.hashCode());
- }
-
- @Test
- public void equalsOperation() {
- SharedConnectivitySettingsState state1 = buildSettingsStateBuilder(INTENT_ACTION).build();
- SharedConnectivitySettingsState state2 = buildSettingsStateBuilder(INTENT_ACTION).build();
- assertThat(state1).isEqualTo(state2);
-
- SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder(INTENT_ACTION)
- .setInstantTetherEnabled(INSTANT_TETHER_STATE_1);
- assertThat(builder.build()).isNotEqualTo(state1);
-
- builder = buildSettingsStateBuilder(INTENT_ACTION_1);
- assertThat(builder.build()).isNotEqualTo(state1);
- }
-
- @Test
- public void getMethods() {
- SharedConnectivitySettingsState state = buildSettingsStateBuilder(INTENT_ACTION).build();
-
- assertThat(state.isInstantTetherEnabled()).isEqualTo(INSTANT_TETHER_STATE);
- assertThat(state.getInstantTetherSettingsPendingIntent())
- .isEqualTo(buildPendingIntent(INTENT_ACTION));
- }
-
- @Test
- public void hashCodeCalculation() {
- SharedConnectivitySettingsState state1 = buildSettingsStateBuilder(INTENT_ACTION).build();
- SharedConnectivitySettingsState state2 = buildSettingsStateBuilder(INTENT_ACTION).build();
-
- assertThat(state1.hashCode()).isEqualTo(state2.hashCode());
- }
-
- private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder(String intentAction) {
- return new SharedConnectivitySettingsState.Builder()
- .setInstantTetherEnabled(INSTANT_TETHER_STATE)
- .setInstantTetherSettingsPendingIntent(buildPendingIntent(intentAction));
- }
-
- private PendingIntent buildPendingIntent(String intentAction) {
- return PendingIntent.getActivity(
- ApplicationProvider.getApplicationContext(), 0,
- new Intent(intentAction), PendingIntent.FLAG_IMMUTABLE);
- }
-}