Merge "Remove outdated comment in ElementContentPicker" into main
diff --git a/Android.bp b/Android.bp
index 7f4871f..af205d8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -261,7 +261,6 @@
"devicepolicyprotosnano",
"ImmutabilityAnnotation",
- "com.android.sysprop.init",
"com.android.sysprop.localization",
"PlatformProperties",
],
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8dc9652..0db91f5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2579,9 +2579,10 @@
@FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect {
method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
+ method public float getAdaptiveScale();
method public long getDuration();
method public int getEffectStrength();
- method public float getLinearScale();
+ method public float getScale();
method @NonNull public android.os.PersistableBundle getVendorData();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR;
diff --git a/core/java/android/app/appfunctions/ServiceCallHelper.java b/core/java/android/app/appfunctions/ServiceCallHelper.java
new file mode 100644
index 0000000..cc882bd
--- /dev/null
+++ b/core/java/android/app/appfunctions/ServiceCallHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Defines a contract for establishing temporary connections to services and executing operations
+ * within a specified timeout. Implementations of this interface provide mechanisms to ensure that
+ * services are properly unbound after the operation completes or a timeout occurs.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public interface ServiceCallHelper<T> {
+
+ /**
+ * Initiates service binding and executes a provided method when the service connects. Unbinds
+ * the service after execution or upon timeout. Returns the result of the bindService API.
+ *
+ * <p>When the service connection was made successfully, it's the caller responsibility to
+ * report the usage is completed and can be unbound by calling {@link
+ * ServiceUsageCompleteListener#onCompleted()}.
+ *
+ * <p>This method includes a timeout mechanism to prevent the system from being stuck in a state
+ * where a service is bound indefinitely (for example, if the binder method never returns). This
+ * helps ensure that the calling app does not remain alive unnecessarily.
+ *
+ * @param intent An Intent object that describes the service that should be bound.
+ * @param bindFlags Flags used to control the binding process See {@link
+ * android.content.Context#bindService}.
+ * @param timeoutInMillis The maximum time in milliseconds to wait for the service connection.
+ * @param userHandle The UserHandle of the user for which the service should be bound.
+ * @param callback A callback to be invoked for various events. See {@link
+ * RunServiceCallCallback}.
+ */
+ boolean runServiceCall(
+ @NonNull Intent intent,
+ int bindFlags,
+ long timeoutInMillis,
+ @NonNull UserHandle userHandle,
+ @NonNull RunServiceCallCallback<T> callback);
+
+ /** An interface for clients to signal that they have finished using a bound service. */
+ interface ServiceUsageCompleteListener {
+ /**
+ * Called when a client has finished using a bound service. This indicates that the service
+ * can be safely unbound.
+ */
+ void onCompleted();
+ }
+
+ interface RunServiceCallCallback<T> {
+ /**
+ * Called when the service connection has been established. Uses {@code
+ * serviceUsageCompleteListener} to report finish using the connected service.
+ */
+ void onServiceConnected(
+ @NonNull T service,
+ @NonNull ServiceUsageCompleteListener serviceUsageCompleteListener);
+
+ /** Called when the service connection was failed to establish. */
+ void onFailedToConnect();
+
+ /**
+ * Called when the whole operation(i.e. binding and the service call) takes longer than
+ * allowed.
+ */
+ void onTimedOut();
+ }
+}
diff --git a/core/java/android/app/appfunctions/ServiceCallHelperImpl.java b/core/java/android/app/appfunctions/ServiceCallHelperImpl.java
new file mode 100644
index 0000000..2e58546
--- /dev/null
+++ b/core/java/android/app/appfunctions/ServiceCallHelperImpl.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+/**
+ * An implementation of {@link android.app.appfunctions.ServiceCallHelper} that that is based on
+ * {@link Context#bindService}.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public class ServiceCallHelperImpl<T> implements ServiceCallHelper<T> {
+ private static final String TAG = "AppFunctionsServiceCall";
+
+ @NonNull private final Context mContext;
+ @NonNull private final Function<IBinder, T> mInterfaceConverter;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Executor mExecutor;
+
+ /**
+ * @param interfaceConverter A function responsible for converting an IBinder object into the
+ * desired service interface.
+ * @param executor An Executor instance to dispatch callback.
+ * @param context The system context.
+ */
+ public ServiceCallHelperImpl(
+ @NonNull Context context,
+ @NonNull Function<IBinder, T> interfaceConverter,
+ @NonNull Executor executor) {
+ mContext = context;
+ mInterfaceConverter = interfaceConverter;
+ mExecutor = executor;
+ }
+
+ @Override
+ public boolean runServiceCall(
+ @NonNull Intent intent,
+ int bindFlags,
+ long timeoutInMillis,
+ @NonNull UserHandle userHandle,
+ @NonNull RunServiceCallCallback<T> callback) {
+ OneOffServiceConnection serviceConnection =
+ new OneOffServiceConnection(
+ intent, bindFlags, timeoutInMillis, userHandle, callback);
+
+ return serviceConnection.bindAndRun();
+ }
+
+ private class OneOffServiceConnection
+ implements ServiceConnection, ServiceUsageCompleteListener {
+ private final Intent mIntent;
+ private final int mFlags;
+ private final long mTimeoutMillis;
+ private final UserHandle mUserHandle;
+ private final RunServiceCallCallback<T> mCallback;
+ private final Runnable mTimeoutCallback;
+
+ OneOffServiceConnection(
+ @NonNull Intent intent,
+ int flags,
+ long timeoutMillis,
+ @NonNull UserHandle userHandle,
+ @NonNull RunServiceCallCallback<T> callback) {
+ mIntent = intent;
+ mFlags = flags;
+ mTimeoutMillis = timeoutMillis;
+ mCallback = callback;
+ mTimeoutCallback =
+ () ->
+ mExecutor.execute(
+ () -> {
+ safeUnbind();
+ mCallback.onTimedOut();
+ });
+ mUserHandle = userHandle;
+ }
+
+ public boolean bindAndRun() {
+ boolean bindServiceResult =
+ mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle);
+
+ if (bindServiceResult) {
+ mHandler.postDelayed(mTimeoutCallback, mTimeoutMillis);
+ } else {
+ safeUnbind();
+ }
+
+ return bindServiceResult;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ T serviceInterface = mInterfaceConverter.apply(service);
+
+ mExecutor.execute(() -> mCallback.onServiceConnected(serviceInterface, this));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ safeUnbind();
+ mExecutor.execute(mCallback::onFailedToConnect);
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ safeUnbind();
+ mExecutor.execute(mCallback::onFailedToConnect);
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ safeUnbind();
+ mExecutor.execute(mCallback::onFailedToConnect);
+ }
+
+ private void safeUnbind() {
+ try {
+ mHandler.removeCallbacks(mTimeoutCallback);
+ mContext.unbindService(this);
+ } catch (Exception ex) {
+ Log.w(TAG, "Failed to unbind", ex);
+ }
+ }
+
+ @Override
+ public void onCompleted() {
+ safeUnbind();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 1fab3cf..c7716e5 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -173,14 +173,47 @@
]
},
{
- "name":"CtsPackageInstallerCUJTestCases",
+ "name": "CtsPackageInstallerCUJInstallationTestCases",
"options":[
- {
- "exclude-annotation":"androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation":"org.junit.Ignore"
- }
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUninstallationTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
]
}
]
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index da863e5..5896227 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -151,31 +151,6 @@
*/
public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
- /** @hide */
- @IntDef(prefix = { "CATEGORY_" }, value = {
- CATEGORY_UNKNOWN,
- CATEGORY_KEYBOARD,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Category {}
-
- /**
- * Category value when the vibration category is unknown.
- *
- * @hide
- */
- public static final int CATEGORY_UNKNOWN = 0x0;
-
- /**
- * Category value for keyboard vibrations.
- *
- * <p>Most typical keyboard vibrations are haptic feedback for virtual keyboard key
- * press/release, for example.
- *
- * @hide
- */
- public static final int CATEGORY_KEYBOARD = 1;
-
/**
* @hide
*/
@@ -252,14 +227,12 @@
private final int mUsage;
private final int mFlags;
private final int mOriginalAudioUsage;
- private final int mCategory;
private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
- @Flag int flags, @Category int category) {
+ @Flag int flags) {
mUsage = usage;
mOriginalAudioUsage = audioUsage;
mFlags = flags & FLAG_ALL_SUPPORTED;
- mCategory = category;
}
/**
@@ -297,20 +270,6 @@
}
/**
- * Return the vibration category.
- *
- * <p>Vibration categories describe the source of the vibration, and it can be combined with
- * the vibration usage to best match to a user setting, e.g. a vibration with usage touch and
- * category keyboard can be used to control keyboard haptic feedback independently.
- *
- * @hide
- */
- @Category
- public int getCategory() {
- return mCategory;
- }
-
- /**
* Check whether a flag is set
* @return true if a flag is set and false otherwise
*/
@@ -362,14 +321,12 @@
dest.writeInt(mUsage);
dest.writeInt(mOriginalAudioUsage);
dest.writeInt(mFlags);
- dest.writeInt(mCategory);
}
private VibrationAttributes(Parcel src) {
mUsage = src.readInt();
mOriginalAudioUsage = src.readInt();
mFlags = src.readInt();
- mCategory = src.readInt();
}
public static final @NonNull Parcelable.Creator<VibrationAttributes>
@@ -392,12 +349,12 @@
}
VibrationAttributes rhs = (VibrationAttributes) o;
return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
- && mFlags == rhs.mFlags && mCategory == rhs.mCategory;
+ && mFlags == rhs.mFlags;
}
@Override
public int hashCode() {
- return Objects.hash(mUsage, mOriginalAudioUsage, mFlags, mCategory);
+ return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
}
@Override
@@ -405,7 +362,6 @@
return "VibrationAttributes{"
+ "mUsage=" + usageToString()
+ ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
- + ", mCategory=" + categoryToString()
+ ", mFlags=" + mFlags
+ '}';
}
@@ -445,23 +401,6 @@
}
}
- /** @hide */
- public String categoryToString() {
- return categoryToString(mCategory);
- }
-
- /** @hide */
- public static String categoryToString(@Category int category) {
- switch (category) {
- case CATEGORY_UNKNOWN:
- return "UNKNOWN";
- case CATEGORY_KEYBOARD:
- return "KEYBOARD";
- default:
- return "unknown category " + category;
- }
- }
-
/**
* Builder class for {@link VibrationAttributes} objects.
* By default, all information is set to UNKNOWN.
@@ -471,7 +410,6 @@
private int mUsage = USAGE_UNKNOWN;
private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
private int mFlags = 0x0;
- private int mCategory = CATEGORY_UNKNOWN;
/**
* Constructs a new Builder with the defaults.
@@ -487,7 +425,6 @@
mUsage = vib.mUsage;
mOriginalAudioUsage = vib.mOriginalAudioUsage;
mFlags = vib.mFlags;
- mCategory = vib.mCategory;
}
}
@@ -554,7 +491,7 @@
*/
public @NonNull VibrationAttributes build() {
VibrationAttributes ans = new VibrationAttributes(
- mUsage, mOriginalAudioUsage, mFlags, mCategory);
+ mUsage, mOriginalAudioUsage, mFlags);
return ans;
}
@@ -570,19 +507,6 @@
}
/**
- * Sets the attribute describing the category of the corresponding vibration.
- *
- * @param category The category for the vibration
- * @return the same Builder instance.
- *
- * @hide
- */
- public @NonNull Builder setCategory(@Category int category) {
- mCategory = category;
- return this;
- }
-
- /**
* Sets only the flags specified in the bitmask, leaving the other supported flag values
* unchanged in the builder.
*
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e68b746..f02d4a9 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -346,7 +346,7 @@
@RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
- VendorEffect.DEFAULT_SCALE);
+ VendorEffect.DEFAULT_SCALE, VendorEffect.DEFAULT_SCALE);
vendorEffect.validate();
return vendorEffect;
}
@@ -623,7 +623,7 @@
* @hide
*/
@NonNull
- public abstract VibrationEffect scaleLinearly(float scaleFactor);
+ public abstract VibrationEffect applyAdaptiveScale(float scaleFactor);
/**
* Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
@@ -948,7 +948,7 @@
/** @hide */
@NonNull
@Override
- public Composed scaleLinearly(float scaleFactor) {
+ public Composed applyAdaptiveScale(float scaleFactor) {
return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
}
@@ -1100,21 +1100,23 @@
private final PersistableBundle mVendorData;
private final int mEffectStrength;
- private final float mLinearScale;
+ private final float mScale;
+ private final float mAdaptiveScale;
/** @hide */
VendorEffect(@NonNull Parcel in) {
this(Objects.requireNonNull(
in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
- in.readInt(), in.readFloat());
+ in.readInt(), in.readFloat(), in.readFloat());
}
/** @hide */
public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
- float linearScale) {
+ float scale, float adaptiveScale) {
mVendorData = vendorData;
mEffectStrength = effectStrength;
- mLinearScale = linearScale;
+ mScale = scale;
+ mAdaptiveScale = adaptiveScale;
}
@NonNull
@@ -1126,8 +1128,12 @@
return mEffectStrength;
}
- public float getLinearScale() {
- return mLinearScale;
+ public float getScale() {
+ return mScale;
+ }
+
+ public float getAdaptiveScale() {
+ return mAdaptiveScale;
}
/** @hide */
@@ -1175,7 +1181,8 @@
if (mEffectStrength == effectStrength) {
return this;
}
- VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale);
+ VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mScale,
+ mAdaptiveScale);
updated.validate();
return updated;
}
@@ -1184,18 +1191,24 @@
@NonNull
@Override
public VendorEffect scale(float scaleFactor) {
- // Vendor effect strength cannot be scaled with this method.
- return this;
+ if (Float.compare(mScale, scaleFactor) == 0) {
+ return this;
+ }
+ VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor,
+ mAdaptiveScale);
+ updated.validate();
+ return updated;
}
/** @hide */
@NonNull
@Override
- public VibrationEffect scaleLinearly(float scaleFactor) {
- if (Float.compare(mLinearScale, scaleFactor) == 0) {
+ public VibrationEffect applyAdaptiveScale(float scaleFactor) {
+ if (Float.compare(mAdaptiveScale, scaleFactor) == 0) {
return this;
}
- VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor);
+ VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, mScale,
+ scaleFactor);
updated.validate();
return updated;
}
@@ -1216,29 +1229,31 @@
return false;
}
return mEffectStrength == other.mEffectStrength
- && (Float.compare(mLinearScale, other.mLinearScale) == 0)
+ && (Float.compare(mScale, other.mScale) == 0)
+ && (Float.compare(mAdaptiveScale, other.mAdaptiveScale) == 0)
&& isPersistableBundleEquals(mVendorData, other.mVendorData);
}
@Override
public int hashCode() {
// PersistableBundle does not implement hashCode, so use its size as a shortcut.
- return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale);
+ return Objects.hash(mVendorData.size(), mEffectStrength, mScale, mAdaptiveScale);
}
@Override
public String toString() {
return String.format(Locale.ROOT,
- "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}",
- mVendorData, effectStrengthToString(mEffectStrength), mLinearScale);
+ "VendorEffect{vendorData=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f}",
+ mVendorData, effectStrengthToString(mEffectStrength), mScale, mAdaptiveScale);
}
/** @hide */
@Override
public String toDebugString() {
- return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f",
+ return String.format(Locale.ROOT,
+ "vendorEffect=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f",
mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
- mLinearScale);
+ mScale, mAdaptiveScale);
}
@Override
@@ -1251,7 +1266,8 @@
out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
out.writePersistableBundle(mVendorData);
out.writeInt(mEffectStrength);
- out.writeFloat(mLinearScale);
+ out.writeFloat(mScale);
+ out.writeFloat(mAdaptiveScale);
}
/**
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 6a54d23..711578c 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -350,7 +350,7 @@
private static final char PARAGRAPH_SEPARATOR = '\n';
/**
- * Move the cusrot to the closest paragraph start offset.
+ * Move the cursor to the closest paragraph start offset.
*
* @param text the spannable text
* @param layout layout to be used for drawing.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 42d66ce..f7745d1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -325,17 +325,62 @@
private String mTag = TAG;
- private final ISurfaceControlViewHostParent mSurfaceControlViewHostParent =
- new ISurfaceControlViewHostParent.Stub() {
+ private static class SurfaceControlViewHostParent extends ISurfaceControlViewHostParent.Stub {
+
+ /**
+ * mSurfaceView is set in {@link #attach} and cleared in {@link #detach} to prevent
+ * temporary memory leaks. The remote process's ISurfaceControlViewHostParent binder
+ * reference extends this object's lifetime. If mSurfaceView is not cleared in
+ * {@link #detach}, then the SurfaceView and anything it references will not be promptly
+ * garbage collected.
+ */
+ @Nullable
+ private SurfaceView mSurfaceView;
+
+ void attach(SurfaceView sv) {
+ synchronized (this) {
+ try {
+ sv.mSurfacePackage.getRemoteInterface().attachParentInterface(this);
+ mSurfaceView = sv;
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is alraedy "
+ + "dead.");
+ }
+ }
+ }
+
+ void detach() {
+ synchronized (this) {
+ if (mSurfaceView == null) {
+ return;
+ }
+ try {
+ mSurfaceView.mSurfacePackage.getRemoteInterface().attachParentInterface(null);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to remove parent interface from SCVH. Likely SCVH is "
+ + "already dead");
+ }
+ mSurfaceView = null;
+ }
+ }
+
@Override
public void updateParams(WindowManager.LayoutParams[] childAttrs) {
- mEmbeddedWindowParams.clear();
- mEmbeddedWindowParams.addAll(Arrays.asList(childAttrs));
+ SurfaceView sv;
+ synchronized (this) {
+ sv = mSurfaceView;
+ }
+ if (sv == null) {
+ return;
+ }
- if (isAttachedToWindow()) {
- runOnUiThread(() -> {
- if (mParent != null) {
- mParent.recomputeViewAttributes(SurfaceView.this);
+ sv.mEmbeddedWindowParams.clear();
+ sv.mEmbeddedWindowParams.addAll(Arrays.asList(childAttrs));
+
+ if (sv.isAttachedToWindow()) {
+ sv.runOnUiThread(() -> {
+ if (sv.mParent != null) {
+ sv.mParent.recomputeViewAttributes(sv);
}
});
}
@@ -343,34 +388,45 @@
@Override
public void forwardBackKeyToParent(@NonNull KeyEvent keyEvent) {
- runOnUiThread(() -> {
- if (!isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) {
- return;
- }
- final ViewRootImpl vri = getViewRootImpl();
- if (vri == null) {
- return;
- }
- final InputManager inputManager = mContext.getSystemService(InputManager.class);
- if (inputManager == null) {
- return;
- }
- // Check that the event was created recently.
- final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime();
- if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) {
- Log.e(TAG, "Ignore the input event that exceed the tolerance time, "
- + "exceed " + timeDiff + "ms");
- return;
- }
- if (inputManager.verifyInputEvent(keyEvent) == null) {
- Log.e(TAG, "Received invalid input event");
- return;
- }
- vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
- true /* processImmediately */);
- });
+ SurfaceView sv;
+ synchronized (this) {
+ sv = mSurfaceView;
+ }
+ if (sv == null) {
+ return;
+ }
+
+ sv.runOnUiThread(() -> {
+ if (!sv.isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) {
+ return;
+ }
+ final ViewRootImpl vri = sv.getViewRootImpl();
+ if (vri == null) {
+ return;
+ }
+ final InputManager inputManager = sv.mContext.getSystemService(InputManager.class);
+ if (inputManager == null) {
+ return;
+ }
+ // Check that the event was created recently.
+ final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime();
+ if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) {
+ Log.e(TAG, "Ignore the input event that exceed the tolerance time, "
+ + "exceed " + timeDiff + "ms");
+ return;
+ }
+ if (inputManager.verifyInputEvent(keyEvent) == null) {
+ Log.e(TAG, "Received invalid input event");
+ return;
+ }
+ vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
+ true /* processImmediately */);
+ });
}
- };
+ }
+
+ private final SurfaceControlViewHostParent mSurfaceControlViewHostParent =
+ new SurfaceControlViewHostParent();
private final boolean mRtDrivenClipping = Flags.clipSurfaceviews();
@@ -930,13 +986,8 @@
}
if (mSurfacePackage != null) {
- try {
- mSurfacePackage.getRemoteInterface().attachParentInterface(null);
- mEmbeddedWindowParams.clear();
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to remove parent interface from SCVH. Likely SCVH is "
- + "already dead");
- }
+ mSurfaceControlViewHostParent.detach();
+ mEmbeddedWindowParams.clear();
if (releaseSurfacePackage) {
mSurfacePackage.release();
mSurfacePackage = null;
@@ -2067,12 +2118,7 @@
applyTransactionOnVriDraw(transaction);
}
mSurfacePackage = p;
- try {
- mSurfacePackage.getRemoteInterface().attachParentInterface(
- mSurfaceControlViewHostParent);
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is already dead.");
- }
+ mSurfaceControlViewHostParent.attach(this);
if (isFocused()) {
requestEmbeddedFocus(true);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 33610a0..c7e1fba 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -396,6 +396,9 @@
int cujType = conf.mCujType;
if (!shouldMonitor()) {
return false;
+ } else if (!conf.hasValidView()) {
+ Log.w(TAG, "The view has since become invalid, aborting the CUJ.");
+ return false;
}
RunningTracker tracker = putTrackerIfNoCurrent(cujType, () ->
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 14786e1..cbcbf2db 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -162,8 +162,10 @@
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@Nullable ProtoLogViewerConfigReader viewerConfigReader,
@NonNull Runnable cacheUpdater) {
- assert (viewerConfigFilePath == null || viewerConfigInputStreamProvider == null) :
- "Only one of viewerConfigFilePath and viewerConfigInputStreamProvider can be set";
+ if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
+ throw new RuntimeException("Only one of viewerConfigFilePath and "
+ + "viewerConfigInputStreamProvider can be set");
+ }
Producer.init(InitArguments.DEFAULTS);
DataSourceParams params =
@@ -734,7 +736,7 @@
return UUID.nameUUIDFromBytes(fullStringIdentifier.getBytes()).getMostSignificantBits();
}
- private static final int STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL = 12;
+ private static final int STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL = 6;
private String collectStackTrace() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
diff --git a/core/java/com/android/internal/util/RateLimitingCache.java b/core/java/com/android/internal/util/RateLimitingCache.java
new file mode 100644
index 0000000..9916076
--- /dev/null
+++ b/core/java/com/android/internal/util/RateLimitingCache.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.os.SystemClock;
+
+/**
+ * A speed/rate limiting cache that's used to cache a value to be returned as long as period hasn't
+ * elapsed and then fetches a new value after period has elapsed. Use this when AIDL calls are
+ * expensive but the value returned by those APIs don't change often enough (or the recency doesn't
+ * matter as much), to incur the cost every time. This class maintains the last fetch time and
+ * fetches a new value when period has passed. Do not use this for API calls that have side-effects.
+ * <p>
+ * By passing in an optional <code>count</code> during creation, this can be used as a rate
+ * limiter that allows up to <code>count</code> calls per period to be passed on to the query
+ * and then the cached value is returned for the remainder of the period. It uses a simple fixed
+ * window method to track rate. Use a window and count appropriate for bursts of calls and for
+ * high latency/cost of the AIDL call.
+ *
+ * @param <Value> The type of the return value
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class RateLimitingCache<Value> {
+
+ private volatile Value mCurrentValue;
+ private volatile long mLastTimestamp; // Can be last fetch time or window start of fetch time
+ private final long mPeriodMillis; // window size
+ private final int mLimit; // max per window
+ private int mCount = 0; // current count within window
+ private long mRandomOffset; // random offset to avoid batching of AIDL calls at window boundary
+
+ /**
+ * The interface to fetch the actual value, if the cache is null or expired.
+ * @hide
+ * @param <V> The return value type
+ */
+ public interface ValueFetcher<V> {
+ /** Called when the cache needs to be updated.
+ * @return the latest value fetched from the source
+ */
+ V fetchValue();
+ }
+
+ /**
+ * Create a speed limiting cache that returns the same value until periodMillis has passed
+ * and then fetches a new value via the {@link ValueFetcher}.
+ *
+ * @param periodMillis time to wait before fetching a new value. Use a negative period to
+ * indicate the value never changes and is fetched only once and
+ * cached. A value of 0 will mean always fetch a new value.
+ */
+ public RateLimitingCache(long periodMillis) {
+ this(periodMillis, 1);
+ }
+
+ /**
+ * Create a rate-limiting cache that allows up to <code>count</code> number of AIDL calls per
+ * period before it starts returning a cached value. The count resets when the next period
+ * begins.
+ *
+ * @param periodMillis the window of time in which <code>count</code> calls will fetch the
+ * newest value from the AIDL call.
+ * @param count how many times during the period it's ok to forward the request to the fetcher
+ * in the {@link #get(ValueFetcher)} method.
+ */
+ public RateLimitingCache(long periodMillis, int count) {
+ mPeriodMillis = periodMillis;
+ mLimit = count;
+ if (mLimit > 1 && periodMillis > 1) {
+ mRandomOffset = (long) (Math.random() * (periodMillis / 2));
+ }
+ }
+
+ /**
+ * Returns the current time in <code>elapsedRealtime</code>. Can be overridden to use
+ * a different timebase that is monotonically increasing; for example, uptimeMillis()
+ * @return a monotonically increasing time in milliseconds
+ */
+ protected long getTime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Returns either the cached value, if called more frequently than the specific rate, or
+ * a new value is fetched and cached. Warning: if the caller is likely to mutate the returned
+ * object, override this method and make a clone before returning it.
+ * @return the cached or latest value
+ */
+ public Value get(ValueFetcher<Value> query) {
+ // If the value never changes
+ if (mPeriodMillis < 0 && mLastTimestamp != 0) {
+ return mCurrentValue;
+ }
+
+ synchronized (this) {
+ // Get the current time and add a random offset to avoid colliding with other
+ // caches with similar harmonic window boundaries
+ final long now = getTime() + mRandomOffset;
+ final boolean newWindow = now - mLastTimestamp >= mPeriodMillis;
+ if (newWindow || mCount < mLimit) {
+ // Fetch a new value
+ mCurrentValue = query.fetchValue();
+
+ // If rate limiting, set timestamp to start of this window
+ if (mLimit > 1) {
+ mLastTimestamp = now - (now % mPeriodMillis);
+ } else {
+ mLastTimestamp = now;
+ }
+
+ if (newWindow) {
+ mCount = 1;
+ } else {
+ mCount++;
+ }
+ }
+ return mCurrentValue;
+ }
+ }
+}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9112d37..284c299 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -19,14 +19,6 @@
#include "com_android_internal_os_Zygote.h"
-#include <async_safe/log.h>
-
-// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
-#include <sys/mount.h>
-#include <linux/fs.h>
-#include <sys/types.h>
-#include <dirent.h>
-
#include <algorithm>
#include <array>
#include <atomic>
@@ -41,19 +33,18 @@
#include <android/fdsan.h>
#include <arpa/inet.h>
+#include <dirent.h>
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
#include <malloc.h>
#include <mntent.h>
-#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/auxv.h>
#include <sys/capability.h>
-#include <sys/cdefs.h>
#include <sys/eventfd.h>
+#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -66,6 +57,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <async_safe/log.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
diff --git a/core/tests/coretests/src/android/os/VibrationAttributesTest.java b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
index d8142c8..5bdae0e 100644
--- a/core/tests/coretests/src/android/os/VibrationAttributesTest.java
+++ b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
@@ -28,11 +28,9 @@
@Test
public void testSimple() throws Exception {
final VibrationAttributes attr = new VibrationAttributes.Builder()
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.setUsage(VibrationAttributes.USAGE_ALARM)
.build();
- assertEquals(VibrationAttributes.CATEGORY_KEYBOARD, attr.getCategory());
assertEquals(VibrationAttributes.USAGE_ALARM, attr.getUsage());
}
}
diff --git a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
new file mode 100644
index 0000000..7541a84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the RateLimitingCache class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RateLimitingCacheTest {
+
+ private int mCounter = 0;
+
+ @Before
+ public void before() {
+ mCounter = -1;
+ }
+
+ RateLimitingCache.ValueFetcher<Integer> mFetcher = () -> {
+ return ++mCounter;
+ };
+
+ /**
+ * Test zero period passed into RateLimitingCache. A new value should be returned for each
+ * time the cache's get() is invoked.
+ */
+ @Test
+ public void testTtl_Zero() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(0);
+
+ int first = s.get(mFetcher);
+ assertEquals(first, 0);
+ int second = s.get(mFetcher);
+ assertEquals(second, 1);
+ SystemClock.sleep(20);
+ int third = s.get(mFetcher);
+ assertEquals(third, 2);
+ }
+
+ /**
+ * Test a period of 100ms passed into RateLimitingCache. A new value should not be fetched
+ * any more frequently than every 100ms.
+ */
+ @Test
+ public void testTtl_100() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
+
+ int first = s.get(mFetcher);
+ assertEquals(first, 0);
+ int second = s.get(mFetcher);
+ // Too early to change
+ assertEquals(second, 0);
+ SystemClock.sleep(150);
+ int third = s.get(mFetcher);
+ // Changed by now
+ assertEquals(third, 1);
+ int fourth = s.get(mFetcher);
+ // Too early to change again
+ assertEquals(fourth, 1);
+ }
+
+ /**
+ * Test a negative period passed into RateLimitingCache. A new value should only be fetched the
+ * first call to get().
+ */
+ @Test
+ public void testTtl_Negative() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(-1);
+
+ int first = s.get(mFetcher);
+ assertEquals(first, 0);
+ SystemClock.sleep(200);
+ // Should return the original value every time
+ int second = s.get(mFetcher);
+ assertEquals(second, 0);
+ }
+
+ /**
+ * Test making tons of calls to the speed-limiter and make sure number of fetches does not
+ * exceed expected number of fetches.
+ */
+ @Test
+ public void testTtl_Spam() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
+ assertCount(s, 1000, 7, 15);
+ }
+
+ /**
+ * Test rate-limiting across multiple periods and make sure the expected number of fetches is
+ * within the specified rate.
+ */
+ @Test
+ public void testRate_10hz() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10);
+ // At 10 per second, 2 seconds should not exceed about 30, assuming overlap into left and
+ // right windows that allow 10 each
+ assertCount(s, 2000, 20, 33);
+ }
+
+ /**
+ * Test that using a different timebase works correctly.
+ */
+ @Test
+ public void testTimebase() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10) {
+ @Override
+ protected long getTime() {
+ return SystemClock.elapsedRealtime() / 2;
+ }
+ };
+ // Timebase is moving at half the speed, so only allows for 1 second worth in 2 seconds.
+ assertCount(s, 2000, 10, 22);
+ }
+
+ /**
+ * Helper to make repeated calls every 5 millis to verify the number of expected fetches for
+ * the given parameters.
+ * @param cache the cache object
+ * @param period the period for which to make get() calls
+ * @param minCount the lower end of the expected number of fetches, with a margin for error
+ * @param maxCount the higher end of the expected number of fetches, with a margin for error
+ */
+ private void assertCount(RateLimitingCache<Integer> cache, long period,
+ int minCount, int maxCount) {
+ long startTime = SystemClock.elapsedRealtime();
+ while (SystemClock.elapsedRealtime() < startTime + period) {
+ int value = cache.get(mFetcher);
+ SystemClock.sleep(5);
+ }
+ int latest = cache.get(mFetcher);
+ assertTrue("Latest should be between " + minCount + " and " + maxCount
+ + " but is " + latest, latest <= maxCount && latest >= minCount);
+ }
+}
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index c1e3578..471b402 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -68,6 +68,16 @@
assertThat(getBoolean("res3")).isTrue();
}
+ @Test
+ public void testFlagDisabledStringArrayElement() {
+ assertThat(getStringArray("strarr1")).isEqualTo(new String[]{"one", "two", "three"});
+ }
+
+ @Test
+ public void testFlagDisabledIntArrayElement() {
+ assertThat(getIntArray("intarr1")).isEqualTo(new int[]{1, 2, 3});
+ }
+
private boolean getBoolean(String name) {
int resId = mResources.getIdentifier(
name,
@@ -77,13 +87,22 @@
return mResources.getBoolean(resId);
}
- private String getString(String name) {
+ private String[] getStringArray(String name) {
int resId = mResources.getIdentifier(
name,
- "string",
+ "array",
"com.android.intenal.flaggedresources");
assertThat(resId).isNotEqualTo(0);
- return mResources.getString(resId);
+ return mResources.getStringArray(resId);
+ }
+
+ private int[] getIntArray(String name) {
+ int resId = mResources.getIdentifier(
+ name,
+ "array",
+ "com.android.intenal.flaggedresources");
+ assertThat(resId).isNotEqualTo(0);
+ return mResources.getIntArray(resId);
}
private String extractApkAndGetPath(int id) throws Exception {
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index bd3d944..4f76dd6 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -60,7 +60,7 @@
@RunWith(MockitoJUnitRunner.class)
public class VibrationEffectTest {
-
+ private static final float TOLERANCE = 1e-2f;
private static final String RINGTONE_URI_1 = "content://test/system/ringtone_1";
private static final String RINGTONE_URI_2 = "content://test/system/ringtone_2";
private static final String RINGTONE_URI_3 = "content://test/system/ringtone_3";
@@ -709,7 +709,7 @@
@Test
public void testScaleWaveform() {
VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f);
- assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f);
+ assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), TOLERANCE);
VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f);
assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude());
@@ -731,11 +731,11 @@
public void testScaleVendorEffect() {
VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
- VibrationEffect scaledUp = effect.scale(1.5f);
- assertEquals(effect, scaledUp);
+ VibrationEffect.VendorEffect scaledUp = (VibrationEffect.VendorEffect) effect.scale(1.5f);
+ assertEquals(1.5f, scaledUp.getScale());
- VibrationEffect scaledDown = effect.scale(0.5f);
- assertEquals(effect, scaledDown);
+ VibrationEffect.VendorEffect scaledDown = (VibrationEffect.VendorEffect) effect.scale(0.5f);
+ assertEquals(0.5f, scaledDown.getScale());
}
@Test
@@ -755,6 +755,70 @@
}
@Test
+ public void testApplyAdaptiveScaleOneShot() {
+ VibrationEffect oneShot = VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100);
+
+ VibrationEffect scaledUp = oneShot.applyAdaptiveScale(1.5f);
+ assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+ VibrationEffect scaledDown = oneShot.applyAdaptiveScale(0.5f);
+ assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+ }
+
+ @Test
+ public void testApplyAdaptiveScaleWaveform() {
+ VibrationEffect waveform = VibrationEffect.createWaveform(
+ new long[] { 100, 100 }, new int[] { 10, 0 }, -1);
+
+ VibrationEffect scaledUp = waveform.applyAdaptiveScale(1.5f);
+ assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(15 / 255f);
+
+ VibrationEffect scaledDown = waveform.applyAdaptiveScale(0.5f);
+ assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(5 / 255f);
+ }
+
+ @Test
+ public void testApplyAdaptiveScalePrebaked() {
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+ VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+ assertEquals(effect, scaledUp);
+
+ VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+ assertEquals(effect, scaledDown);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ public void testApplyAdaptiveScaleVendorEffect() {
+ VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+ VibrationEffect.VendorEffect scaledUp =
+ (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(1.5f);
+ assertEquals(1.5f, scaledUp.getAdaptiveScale());
+
+ VibrationEffect.VendorEffect scaledDown =
+ (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(0.5f);
+ assertEquals(0.5f, scaledDown.getAdaptiveScale());
+ }
+
+ @Test
+ public void testApplyAdaptiveScaleComposed() {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
+ .addEffect(VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100))
+ .compose();
+
+ VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+ assertThat(getPrimitiveSegment(scaledUp, 0).getScale()).isWithin(TOLERANCE).of(0.75f);
+ assertThat(getStepSegment(scaledUp, 1).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+ VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+ assertThat(getPrimitiveSegment(scaledDown, 0).getScale()).isWithin(TOLERANCE).of(0.25f);
+ assertThat(getStepSegment(scaledDown, 1).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+ }
+
+ @Test
public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() {
VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100);
VibrationEffect waveform = VibrationEffect.createWaveform(new long[] { 10, 20 }, 0);
diff --git a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
index ab4e29a..7b33534 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
@@ -17,19 +17,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
- android:viewportWidth="32.0"
- android:viewportHeight="32.0"
- android:tint="@color/decor_button_dark_color">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<group android:scaleX="0.5"
android:scaleY="0.5"
- android:translateX="8.0"
- android:translateY="8.0" >
+ android:translateX="6.0"
+ android:translateY="6.0" >
<path
- android:fillColor="@android:color/white"
- android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
- <path
- android:fillColor="@android:color/white"
- android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
+ android:fillColor="@android:color/black"
+ android:fillType="evenOdd"
+ android:pathData="M23.0,1.0v22.0H1V1h22zm-3,19H4V4h16v16z"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml
new file mode 100644
index 0000000..91c8f54
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32.0dp"
+ android:height="32.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <group android:scaleX="0.5"
+ android:scaleY="0.5"
+ android:translateX="6.0"
+ android:translateY="6.0" >
+ <path
+ android:fillColor="@android:color/black"
+ android:fillType="evenOdd"
+ android:pathData="M23,16H8V1h15v15zm-12,-3V4h9v9h-9zM4,8H1v15h15v-3H4V8z"/>
+ </group>
+</vector>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 06c1e68..51ce2c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -45,6 +45,7 @@
import com.android.wm.shell.pip2.phone.PipTouchHandler;
import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.pip2.phone.PipTransitionState;
+import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -73,10 +74,11 @@
Optional<PipController> pipController,
PipTouchHandler pipTouchHandler,
@NonNull PipScheduler pipScheduler,
- @NonNull PipTransitionState pipStackListenerController) {
+ @NonNull PipTransitionState pipStackListenerController,
+ @NonNull PipUiStateChangeController pipUiStateChangeController) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipScheduler,
- pipStackListenerController);
+ pipStackListenerController, pipUiStateChangeController);
}
@WMSingleton
@@ -181,4 +183,11 @@
static PipTransitionState providePipTransitionState(@ShellMainThread Handler handler) {
return new PipTransitionState(handler);
}
+
+ @WMSingleton
+ @Provides
+ static PipUiStateChangeController providePipUiStateChangeController(
+ PipTransitionState pipTransitionState) {
+ return new PipUiStateChangeController(pipTransitionState);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 05c9d02..b6f2a25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.internal.util.FrameworkStatsLog
import com.android.wm.shell.protolog.ShellProtoLogGroup
@@ -128,7 +129,10 @@
/* task_y */
taskUpdate.taskY,
/* session_id */
- sessionId
+ sessionId,
+ taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON,
+ taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON,
+
)
}
@@ -142,6 +146,8 @@
* @property taskWidth width of the task in px
* @property taskX x-coordinate of the top-left corner
* @property taskY y-coordinate of the top-left corner
+ * @property minimizeReason the reason the task was minimized
+ * @property unminimizeEvent the reason the task was unminimized
*
*/
data class TaskUpdate(
@@ -151,8 +157,52 @@
val taskWidth: Int,
val taskX: Int,
val taskY: Int,
+ val minimizeReason: MinimizeReason? = null,
+ val unminimizeReason: UnminimizeReason? = null,
)
+ // Default value used when the task was not minimized.
+ @VisibleForTesting
+ const val UNSET_MINIMIZE_REASON =
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__UNSET_MINIMIZE
+
+ /** The reason a task was minimized. */
+ enum class MinimizeReason (val reason: Int) {
+ TASK_LIMIT(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
+ ),
+ MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
+ ),
+ }
+
+ // Default value used when the task was not unminimized.
+ @VisibleForTesting
+ const val UNSET_UNMINIMIZE_REASON =
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNSET_UNMINIMIZE
+
+ /** The reason a task was unminimized. */
+ enum class UnminimizeReason (val reason: Int) {
+ UNKNOWN(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_UNKNOWN
+ ),
+ TASKBAR_TAP(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_TASKBAR_TAP
+ ),
+ ALT_TAB(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_ALT_TAB
+ ),
+ TASK_LAUNCH(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_TASK_LAUNCH
+ ),
+ }
+
/**
* Enum EnterReason mapped to the EnterReason definition in
* stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 846fa62..ed18712 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -119,7 +119,8 @@
PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipScheduler pipScheduler,
- PipTransitionState pipTransitionState) {
+ PipTransitionState pipTransitionState,
+ PipUiStateChangeController pipUiStateChangeController) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java
new file mode 100644
index 0000000..224016e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import android.app.ActivityTaskManager;
+import android.app.Flags;
+import android.app.PictureInPictureUiState;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.util.function.Consumer;
+
+/**
+ * Controller class manages the {@link android.app.PictureInPictureUiState} callbacks sent to app.
+ */
+public class PipUiStateChangeController implements
+ PipTransitionState.PipTransitionStateChangedListener {
+
+ private final PipTransitionState mPipTransitionState;
+
+ private Consumer<PictureInPictureUiState> mPictureInPictureUiStateConsumer;
+
+ public PipUiStateChangeController(PipTransitionState pipTransitionState) {
+ mPipTransitionState = pipTransitionState;
+ mPipTransitionState.addPipTransitionStateChangedListener(this);
+ mPictureInPictureUiStateConsumer = pictureInPictureUiState -> {
+ try {
+ ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
+ pictureInPictureUiState);
+ } catch (RemoteException | IllegalStateException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Failed to send PictureInPictureUiState.");
+ }
+ };
+ }
+
+ @Override
+ public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+ @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+ if (newState == PipTransitionState.SWIPING_TO_PIP) {
+ onIsTransitioningToPipUiStateChange(true /* isTransitioningToPip */);
+ } else if (newState == PipTransitionState.ENTERING_PIP
+ && !mPipTransitionState.isInSwipePipToHomeTransition()) {
+ onIsTransitioningToPipUiStateChange(true /* isTransitioningToPip */);
+ } else if (newState == PipTransitionState.ENTERED_PIP) {
+ onIsTransitioningToPipUiStateChange(false /* isTransitioningToPip */);
+ }
+ }
+
+ @VisibleForTesting
+ void setPictureInPictureUiStateConsumer(Consumer<PictureInPictureUiState> consumer) {
+ mPictureInPictureUiStateConsumer = consumer;
+ }
+
+ private void onIsTransitioningToPipUiStateChange(boolean isTransitioningToPip) {
+ if (Flags.enablePipUiStateCallbackOnEntering()
+ && mPictureInPictureUiStateConsumer != null) {
+ mPictureInPictureUiStateConsumer.accept(new PictureInPictureUiState.Builder()
+ .setTransitioningToPip(isTransitioningToPip)
+ .build());
+ }
+ }
+}
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 c850ff8..001bf26 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
@@ -717,7 +717,11 @@
Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
// The transition is already somewhere else in the pipeline, so just return here.
t.apply();
- existing.mFinishT.merge(finishT);
+ if (existing.mFinishT != null) {
+ existing.mFinishT.merge(finishT);
+ } else {
+ existing.mFinishT = finishT;
+ }
return;
}
// This usually means the system is in a bad state and may not recover; however,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 39260f6..231570f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -261,6 +261,8 @@
setupRootView();
}
+ bindData(mResult.mRootView, taskInfo);
+
if (!isDragResizeable) {
closeDragResizeListener();
return;
@@ -307,6 +309,14 @@
maximize.setOnClickListener(mOnCaptionButtonClickListener);
}
+ private void bindData(View rootView, RunningTaskInfo taskInfo) {
+ final boolean isFullscreen =
+ taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ rootView.findViewById(R.id.maximize_window)
+ .setBackgroundResource(isFullscreen ? R.drawable.decor_restore_button_dark
+ : R.drawable.decor_maximize_button_dark);
+ }
+
void setCaptionColor(int captionColor) {
if (mResult.mRootView == null) {
return;
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
index 35b2f56..a231e38 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_multitasking_windowing",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -30,6 +31,46 @@
],
}
+java_library {
+ name: "WMShellFlickerTestsSplitScreenBase",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "wm-shell-flicker-utils",
+ "androidx.test.ext.junit",
+ "flickertestapplib",
+ "flickerlib",
+ "flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
+ "launcher-helper-lib",
+ "launcher-aosp-tapl",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreen",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*.kt"],
+ exclude_srcs: ["src/**/benchmark/*.kt"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellFlickerTestsSplitScreenBase",
+ ],
+ data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merges
+
filegroup {
name: "WMShellFlickerTestsSplitScreenGroup1-src",
srcs: [
@@ -61,27 +102,6 @@
],
}
-java_library {
- name: "WMShellFlickerTestsSplitScreenBase",
- srcs: [
- ":WMShellFlickerTestsSplitScreenBase-src",
- ],
- static_libs: [
- "WMShellFlickerTestsBase",
- "wm-shell-flicker-utils",
- "androidx.test.ext.junit",
- "flickertestapplib",
- "flickerlib",
- "flickerlib-helpers",
- "flickerlib-trace_processor_shell",
- "platform-test-annotations",
- "wm-flicker-common-app-helpers",
- "wm-flicker-common-assertions",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- ],
-}
-
android_test {
name: "WMShellFlickerTestsSplitScreenGroup1",
defaults: ["WMShellFlickerTestsDefault"],
@@ -154,3 +174,156 @@
],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merges
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsRotation module
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-CatchAll",
+ base: "WMShellFlickerTestsSplitScreen",
+ exclude_filters: [
+ "com.android.wm.shell.flicker.splitscreen.CopyContentInSplit",
+ "com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByDivider",
+ "com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByGoHome",
+ "com.android.wm.shell.flicker.splitscreen.DragDividerToResize",
+ "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromAllApps",
+ "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromNotification",
+ "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromShortcut",
+ "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromTaskbar",
+ "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenFromOverview",
+ "com.android.wm.shell.flicker.splitscreen.MultipleShowImeRequestsInSplitScreen",
+ "com.android.wm.shell.flicker.splitscreen.SwitchAppByDoubleTapDivider",
+ "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromAnotherApp",
+ "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromHome",
+ "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromRecent",
+ "com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairs",
+ "com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairsNoPip",
+ "com.android.wm.shell.flicker.splitscreen.",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-CopyContentInSplit",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.CopyContentInSplit"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-DismissSplitScreenByDivider",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByDivider"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-DismissSplitScreenByGoHome",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByGoHome"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-DragDividerToResize",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.DragDividerToResize"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromAllApps",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromAllApps"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromNotification",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromNotification"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromShortcut",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromShortcut"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromTaskbar",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromTaskbar"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenFromOverview",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenFromOverview"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-MultipleShowImeRequestsInSplitScreen",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.MultipleShowImeRequestsInSplitScreen"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchAppByDoubleTapDivider",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchAppByDoubleTapDivider"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromAnotherApp",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromAnotherApp"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromHome",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromHome"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromRecent",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromRecent"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchBetweenSplitPairs",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairs"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-SwitchBetweenSplitPairsNoPip",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairsNoPip"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsSplitScreen-UnlockKeyguardToSplitScreen",
+ base: "WMShellFlickerTestsSplitScreen",
+ include_filters: ["com.android.wm.shell.flicker.splitscreen.UnlockKeyguardToSplitScreen"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
index e151ab2..29a9f10 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_app_compat",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -23,6 +24,9 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merge
+
filegroup {
name: "WMShellFlickerTestsAppCompat-src",
srcs: [
@@ -41,3 +45,80 @@
static_libs: ["WMShellFlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merge
+
+android_test {
+ name: "WMShellFlickerTestsAppCompat",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker",
+ instrumentation_target_package: "com.android.wm.shell.flicker",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: ["WMShellFlickerTestsBase"],
+ data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsAppCompat module
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-CatchAll",
+ base: "WMShellFlickerTestsAppCompat",
+ exclude_filters: [
+ "com.android.wm.shell.flicker.appcompat.OpenAppInSizeCompatModeTest",
+ "com.android.wm.shell.flicker.appcompat.OpenTransparentActivityTest",
+ "com.android.wm.shell.flicker.appcompat.QuickSwitchLauncherToLetterboxAppTest",
+ "com.android.wm.shell.flicker.appcompat.RepositionFixedPortraitAppTest",
+ "com.android.wm.shell.flicker.appcompat.RestartAppInSizeCompatModeTest",
+ "com.android.wm.shell.flicker.appcompat.RotateImmersiveAppInFullscreenTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-OpenAppInSizeCompatModeTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.OpenAppInSizeCompatModeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-OpenTransparentActivityTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.OpenTransparentActivityTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-QuickSwitchLauncherToLetterboxAppTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.QuickSwitchLauncherToLetterboxAppTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-RepositionFixedPortraitAppTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.RepositionFixedPortraitAppTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-RestartAppInSizeCompatModeTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.RestartAppInSizeCompatModeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsAppCompat-RotateImmersiveAppInFullscreenTest",
+ base: "WMShellFlickerTestsAppCompat",
+ include_filters: ["com.android.wm.shell.flicker.appcompat.RotateImmersiveAppInFullscreenTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
index f0b4f1f..2ff7ab2 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_multitasking_windowing",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -34,3 +35,57 @@
static_libs: ["WMShellFlickerTestsBase"],
data: ["trace_config/*"],
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsBubbles module
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-CatchAll",
+ base: "WMShellFlickerTestsBubbles",
+ exclude_filters: [
+ "com.android.wm.shell.flicker.bubble.ChangeActiveActivityFromBubbleTest",
+ "com.android.wm.shell.flicker.bubble.DragToDismissBubbleScreenTest",
+ "com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleOnLocksreenTest",
+ "com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleTest",
+ "com.android.wm.shell.flicker.bubble.SendBubbleNotificationTest",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-ChangeActiveActivityFromBubbleTest",
+ base: "WMShellFlickerTestsBubbles",
+ include_filters: ["com.android.wm.shell.flicker.bubble.ChangeActiveActivityFromBubbleTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-DragToDismissBubbleScreenTest",
+ base: "WMShellFlickerTestsBubbles",
+ include_filters: ["com.android.wm.shell.flicker.bubble.DragToDismissBubbleScreenTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-OpenActivityFromBubbleOnLocksreenTest",
+ base: "WMShellFlickerTestsBubbles",
+ include_filters: ["com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleOnLocksreenTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-OpenActivityFromBubbleTest",
+ base: "WMShellFlickerTestsBubbles",
+ include_filters: ["com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsBubbles-SendBubbleNotificationTest",
+ base: "WMShellFlickerTestsBubbles",
+ include_filters: ["com.android.wm.shell.flicker.bubble.SendBubbleNotificationTest"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for WMShellFlickerTestsBubbles module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index faeb342..4165ed0 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_multitasking_windowing",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -24,6 +25,14 @@
}
filegroup {
+ name: "WMShellFlickerTestsPipApps-src",
+ srcs: ["src/**/apps/*.kt"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merges
+
+filegroup {
name: "WMShellFlickerTestsPip1-src",
srcs: [
"src/**/A*.kt",
@@ -52,11 +61,6 @@
srcs: ["src/**/common/*.kt"],
}
-filegroup {
- name: "WMShellFlickerTestsPipApps-src",
- srcs: ["src/**/apps/*.kt"],
-}
-
android_test {
name: "WMShellFlickerTestsPip1",
defaults: ["WMShellFlickerTestsDefault"],
@@ -107,6 +111,21 @@
data: ["trace_config/*"],
}
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merges
+
+android_test {
+ name: "WMShellFlickerTestsPip",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*.kt"],
+ static_libs: ["WMShellFlickerTestsBase"],
+ data: ["trace_config/*"],
+}
+
android_test {
name: "WMShellFlickerTestsPipApps",
defaults: ["WMShellFlickerTestsDefault"],
@@ -146,3 +165,185 @@
test_plan_include: "csuitePlan.xml",
test_config_template: "csuiteDefaultTemplate.xml",
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsPip module
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-CatchAll",
+ base: "WMShellFlickerTestsPip",
+ exclude_filters: [
+ "com.android.wm.shell.flicker.pip.AutoEnterPipOnGoToHomeTest",
+ "com.android.wm.shell.flicker.pip.AutoEnterPipWithSourceRectHintTest",
+ "com.android.wm.shell.flicker.pip.ClosePipBySwipingDownTest",
+ "com.android.wm.shell.flicker.pip.ClosePipWithDismissButtonTest",
+ "com.android.wm.shell.flicker.pip.EnterPipOnUserLeaveHintTest",
+ "com.android.wm.shell.flicker.pip.EnterPipViaAppUiButtonTest",
+ "com.android.wm.shell.flicker.pip.ExitPipToAppViaExpandButtonTest",
+ "com.android.wm.shell.flicker.pip.ExitPipToAppViaIntentTest",
+ "com.android.wm.shell.flicker.pip.ExpandPipOnDoubleClickTest",
+ "com.android.wm.shell.flicker.pip.ExpandPipOnPinchOpenTest",
+ "com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest",
+ "com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest",
+ "com.android.wm.shell.flicker.pip.MovePipDownOnShelfHeightChange",
+ "com.android.wm.shell.flicker.pip.MovePipOnImeVisibilityChangeTest",
+ "com.android.wm.shell.flicker.pip.MovePipUpOnShelfHeightChangeTest",
+ "com.android.wm.shell.flicker.pip.PipAspectRatioChangeTest",
+ "com.android.wm.shell.flicker.pip.PipDragTest",
+ "com.android.wm.shell.flicker.pip.PipDragThenSnapTest",
+ "com.android.wm.shell.flicker.pip.PipPinchInTest",
+ "com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned",
+ "com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay",
+ ],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-AutoEnterPipOnGoToHomeTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.AutoEnterPipOnGoToHomeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-AutoEnterPipWithSourceRectHintTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.AutoEnterPipWithSourceRectHintTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ClosePipBySwipingDownTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ClosePipBySwipingDownTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ClosePipWithDismissButtonTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ClosePipWithDismissButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-EnterPipOnUserLeaveHintTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.EnterPipOnUserLeaveHintTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-EnterPipViaAppUiButtonTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.EnterPipViaAppUiButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ExitPipToAppViaExpandButtonTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ExitPipToAppViaExpandButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ExitPipToAppViaIntentTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ExitPipToAppViaIntentTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ExpandPipOnDoubleClickTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ExpandPipOnDoubleClickTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ExpandPipOnPinchOpenTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ExpandPipOnPinchOpenTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-FromSplitScreenAutoEnterPipOnGoToHomeTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-FromSplitScreenEnterPipOnUserLeaveHintTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-MovePipDownOnShelfHeightChange",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.MovePipDownOnShelfHeightChange"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-MovePipOnImeVisibilityChangeTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.MovePipOnImeVisibilityChangeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-MovePipUpOnShelfHeightChangeTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.MovePipUpOnShelfHeightChangeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-PipAspectRatioChangeTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.PipAspectRatioChangeTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-PipDragTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.PipDragTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-PipDragThenSnapTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.PipDragThenSnapTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-PipPinchInTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.PipPinchInTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-SetRequestedOrientationWhilePinned",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-ShowPipAndRotateDisplay",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay"],
+ test_suites: ["device-tests"],
+}
+
+// End breakdowns for WMShellFlickerTestsPip module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index 4548fcb..70b3661 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -16,14 +16,17 @@
package com.android.wm.shell.desktopmode
-import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
import kotlinx.coroutines.runBlocking
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.eq
@@ -33,7 +36,7 @@
*/
class DesktopModeEventLoggerTest {
- private val desktopModeEventLogger = DesktopModeEventLogger()
+ private val desktopModeEventLogger = DesktopModeEventLogger()
@JvmField
@Rule
@@ -44,7 +47,7 @@
fun logSessionEnter_enterReason() = runBlocking {
desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
- ExtendedMockito.verify {
+ verify {
FrameworkStatsLog.write(
eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
/* event */
@@ -63,7 +66,7 @@
fun logSessionExit_exitReason() = runBlocking {
desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
- ExtendedMockito.verify {
+ verify {
FrameworkStatsLog.write(
eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
/* event */
@@ -82,7 +85,7 @@
fun logTaskAdded_taskUpdate() = runBlocking {
desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
- ExtendedMockito.verify {
+ verify {
FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
@@ -99,7 +102,9 @@
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID))
+ eq(SESSION_ID),
+ eq(UNSET_MINIMIZE_REASON),
+ eq(UNSET_UNMINIMIZE_REASON))
}
}
@@ -107,7 +112,7 @@
fun logTaskRemoved_taskUpdate() = runBlocking {
desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
- ExtendedMockito.verify {
+ verify {
FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
@@ -124,7 +129,9 @@
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID))
+ eq(SESSION_ID),
+ eq(UNSET_MINIMIZE_REASON),
+ eq(UNSET_UNMINIMIZE_REASON))
}
}
@@ -132,10 +139,11 @@
fun logTaskInfoChanged_taskUpdate() = runBlocking {
desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
- ExtendedMockito.verify {
+ verify {
FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
- eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
/* instance_id */
eq(TASK_UPDATE.instanceId),
/* uid */
@@ -149,7 +157,71 @@
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID))
+ eq(SESSION_ID),
+ eq(UNSET_MINIMIZE_REASON),
+ eq(UNSET_UNMINIMIZE_REASON))
+ }
+ }
+
+ @Test
+ fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking {
+ desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
+ createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT))
+
+ verify {
+ FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ /* task_event */
+ eq(FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ /* instance_id */
+ eq(TASK_UPDATE.instanceId),
+ /* uid */
+ eq(TASK_UPDATE.uid),
+ /* task_height */
+ eq(TASK_UPDATE.taskHeight),
+ /* task_width */
+ eq(TASK_UPDATE.taskWidth),
+ /* task_x */
+ eq(TASK_UPDATE.taskX),
+ /* task_y */
+ eq(TASK_UPDATE.taskY),
+ /* session_id */
+ eq(SESSION_ID),
+ /* minimize_reason */
+ eq(MinimizeReason.TASK_LIMIT.reason),
+ /* unminimize_reason */
+ eq(UNSET_UNMINIMIZE_REASON))
+ }
+ }
+
+ @Test
+ fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking {
+ desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
+ createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP))
+
+ verify {
+ FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ /* task_event */
+ eq(FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ /* instance_id */
+ eq(TASK_UPDATE.instanceId),
+ /* uid */
+ eq(TASK_UPDATE.uid),
+ /* task_height */
+ eq(TASK_UPDATE.taskHeight),
+ /* task_width */
+ eq(TASK_UPDATE.taskWidth),
+ /* task_x */
+ eq(TASK_UPDATE.taskX),
+ /* task_y */
+ eq(TASK_UPDATE.taskY),
+ /* session_id */
+ eq(SESSION_ID),
+ /* minimize_reason */
+ eq(UNSET_MINIMIZE_REASON),
+ /* unminimize_reason */
+ eq(UnminimizeReason.TASKBAR_TAP.reason))
}
}
@@ -165,5 +237,11 @@
private val TASK_UPDATE = TaskUpdate(
TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y
)
+
+ private fun createTaskUpdate(
+ minimizeReason: MinimizeReason? = null,
+ unminimizeReason: UnminimizeReason? = null,
+ ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
+ unminimizeReason)
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
similarity index 95%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
index f3f3c37..571ae93 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip2;
+package com.android.wm.shell.pip2.phone;
import android.os.Bundle;
import android.os.Handler;
@@ -22,8 +22,6 @@
import android.testing.AndroidTestingRunner;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
-import com.android.wm.shell.pip2.phone.PipTransitionState;
import junit.framework.Assert;
@@ -33,7 +31,7 @@
import org.mockito.Mock;
/**
- * Unit test against {@link PhoneSizeSpecSource}.
+ * Unit test against {@link PipTransitionState}.
*
* This test mocks the PiP2 flag to be true.
*/
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
new file mode 100644
index 0000000..82cdfd5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.app.PictureInPictureUiState;
+import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Unit test against {@link PipUiStateChangeController}.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipUiStateChangeControllerTests {
+
+ @Mock
+ private PipTransitionState mPipTransitionState;
+
+ private Consumer<PictureInPictureUiState> mPictureInPictureUiStateConsumer;
+ private ArgumentCaptor<PictureInPictureUiState> mArgumentCaptor;
+
+ private PipUiStateChangeController mPipUiStateChangeController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mPipUiStateChangeController = new PipUiStateChangeController(mPipTransitionState);
+ mPictureInPictureUiStateConsumer = spy(pictureInPictureUiState -> {});
+ mPipUiStateChangeController.setPictureInPictureUiStateConsumer(
+ mPictureInPictureUiStateConsumer);
+ mArgumentCaptor = ArgumentCaptor.forClass(PictureInPictureUiState.class);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+ public void onPipTransitionStateChanged_swipePipStart_callbackIsTransitioningToPipTrue() {
+ when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+ mPipUiStateChangeController.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED, PipTransitionState.SWIPING_TO_PIP, Bundle.EMPTY);
+
+ verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+ assertTrue(mArgumentCaptor.getValue().isTransitioningToPip());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+ public void onPipTransitionStateChanged_swipePipOngoing_noCallbackIsTransitioningToPip() {
+ when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+ mPipUiStateChangeController.onPipTransitionStateChanged(
+ PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
+
+ verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+ public void onPipTransitionStateChanged_swipePipFinish_callbackIsTransitioningToPipFalse() {
+ when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+ mPipUiStateChangeController.onPipTransitionStateChanged(
+ PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERED_PIP, Bundle.EMPTY);
+
+ verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+ assertFalse(mArgumentCaptor.getValue().isTransitioningToPip());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+ public void onPipTransitionStateChanged_tapHomeStart_callbackIsTransitioningToPipTrue() {
+ when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(false);
+
+ mPipUiStateChangeController.onPipTransitionStateChanged(
+ PipTransitionState.UNDEFINED, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
+
+ verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+ assertTrue(mArgumentCaptor.getValue().isTransitioningToPip());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+ public void onPipTransitionStateChanged_tapHomeFinish_callbackIsTransitioningToPipFalse() {
+ when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(false);
+
+ mPipUiStateChangeController.onPipTransitionStateChanged(
+ PipTransitionState.ENTERING_PIP, PipTransitionState.ENTERED_PIP, Bundle.EMPTY);
+
+ verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+ assertFalse(mArgumentCaptor.getValue().isTransitioningToPip());
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index fa905e2..a734689 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -284,11 +284,8 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
fun testCreateAndDisposeEventReceiver() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- setUpMockDecorationForTask(task)
-
- onTaskOpening(task)
- desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+ val decor = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(decor.mTaskInfo)
verify(mockInputMonitorFactory).create(any(), any())
verify(mockInputMonitor).dispose()
@@ -357,16 +354,14 @@
}
@Test
- fun testCloseButtonInFreeform() {
- val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val windowDecor = setUpMockDecorationForTask(task)
+ fun testCloseButtonInFreeform_closeWindow() {
+ val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
+ as ArgumentCaptor<View.OnClickListener>
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onCaptionButtonClickListener = onClickListenerCaptor
+ )
- onTaskOpening(task)
- val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
- verify(windowDecor).setCaptionListeners(
- onClickListenerCaptor.capture(), any(), any(), any())
-
- val onClickListener = onClickListenerCaptor.firstValue
val view = mock(View::class.java)
whenever(view.id).thenReturn(R.id.close_window)
@@ -374,7 +369,7 @@
desktopModeWindowDecorViewModel
.setFreeformTaskTransitionStarter(freeformTaskTransitionStarter)
- onClickListener.onClick(view)
+ onClickListenerCaptor.value.onClick(view)
val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
@@ -383,7 +378,7 @@
assertEquals(1, wct.getHierarchyOps().size)
assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK,
wct.getHierarchyOps().get(0).getType())
- assertEquals(task.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+ assertEquals(decor.mTaskInfo.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
}
@Test
@@ -458,15 +453,9 @@
@Test
fun testKeyguardState_notifiesAllDecors() {
- val task1 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration1 = setUpMockDecorationForTask(task1)
- onTaskOpening(task1)
- val task2 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration2 = setUpMockDecorationForTask(task2)
- onTaskOpening(task2)
- val task3 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
- val decoration3 = setUpMockDecorationForTask(task3)
- onTaskOpening(task3)
+ val decoration1 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration2 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration3 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
desktopModeOnKeyguardChangedListener
.onKeyguardVisibilityChanged(true /* visible */, true /* occluded */,
@@ -1009,6 +998,8 @@
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onOpenInBrowserClickListener: ArgumentCaptor<Consumer<Uri>> =
forClass(Consumer::class.java) as ArgumentCaptor<Consumer<Uri>>,
+ onCaptionButtonClickListener: ArgumentCaptor<View.OnClickListener> =
+ forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>
): DesktopModeWindowDecoration {
val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
onTaskOpening(decor.mTaskInfo)
@@ -1019,6 +1010,8 @@
verify(decor).setOnToFullscreenClickListener(onToFullscreenClickListenerCaptor.capture())
verify(decor).setOnToSplitScreenClickListener(onToSplitScreenClickListenerCaptor.capture())
verify(decor).setOpenInBrowserClickListener(onOpenInBrowserClickListener.capture())
+ verify(decor).setCaptionListeners(
+ onCaptionButtonClickListener.capture(), any(), any(), any())
return decor
}
diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index 8ddc572..49ee8f7 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -119,7 +119,7 @@
auto err = ReadFile(stderr[0]);
result.stderr_str = err ? std::move(*err) : "";
close(stderr[0]);
- return std::move(result);
+ return result;
}
}
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index a1f5168..faea6d4 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -2,6 +2,13 @@
container: "system"
flag {
+ name: "runtime_color_filters_blenders"
+ namespace: "core_graphics"
+ description: "API for AGSL authored runtime color filters and blenders"
+ bug: "358126864"
+}
+
+flag {
name: "clip_shader"
is_exported: true
namespace: "core_graphics"
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 25c767a..c36eda9 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -84,6 +84,7 @@
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
@@ -4283,7 +4284,7 @@
final OnAudioFocusChangeListener listener =
fri.mRequest.getOnAudioFocusChangeListener();
if (listener != null) {
- Log.d(TAG, "dispatching onAudioFocusChange("
+ Slog.i(TAG, "dispatching onAudioFocusChange("
+ msg.arg1 + ") to " + msg.obj);
listener.onAudioFocusChange(msg.arg1);
}
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 2d7db5e..9896f64 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -131,7 +131,7 @@
private int mCasSystemId;
private int mUserId;
private TunerResourceManager mTunerResourceManager = null;
- private final Map<Session, Integer> mSessionMap = new HashMap<>();
+ private final Map<Session, Long> mSessionMap = new HashMap<>();
/**
* Scrambling modes used to open cas sessions.
@@ -1126,10 +1126,10 @@
}
}
- private int getSessionResourceHandle() throws MediaCasException {
+ private long getSessionResourceHandle() throws MediaCasException {
validateInternalStates();
- int[] sessionResourceHandle = new int[1];
+ long[] sessionResourceHandle = new long[1];
sessionResourceHandle[0] = -1;
if (mTunerResourceManager != null) {
CasSessionRequest casSessionRequest = new CasSessionRequest();
@@ -1144,8 +1144,7 @@
return sessionResourceHandle[0];
}
- private void addSessionToResourceMap(Session session, int sessionResourceHandle) {
-
+ private void addSessionToResourceMap(Session session, long sessionResourceHandle) {
if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
synchronized (mSessionMap) {
mSessionMap.put(session, sessionResourceHandle);
@@ -1178,13 +1177,14 @@
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public Session openSession() throws MediaCasException {
- int sessionResourceHandle = getSessionResourceHandle();
+ long sessionResourceHandle = getSessionResourceHandle();
try {
if (mICas != null) {
try {
byte[] sessionId = mICas.openSessionDefault();
Session session = createFromSessionId(sessionId);
+ addSessionToResourceMap(session, sessionResourceHandle);
Log.d(TAG, "Write Stats Log for succeed to Open Session.");
FrameworkStatsLog.write(
FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS,
@@ -1238,7 +1238,7 @@
@Nullable
public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode)
throws MediaCasException {
- int sessionResourceHandle = getSessionResourceHandle();
+ long sessionResourceHandle = getSessionResourceHandle();
if (mICas != null) {
try {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 2c71ee0..300ae5d 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -293,13 +293,13 @@
private EventHandler mHandler;
@Nullable
private FrontendInfo mFrontendInfo;
- private Integer mFrontendHandle;
+ private Long mFrontendHandle;
private Tuner mFeOwnerTuner = null;
private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
private Integer mDesiredFrontendId = null;
private int mUserId;
private Lnb mLnb;
- private Integer mLnbHandle;
+ private Long mLnbHandle;
@Nullable
private OnTuneEventListener mOnTuneEventListener;
@Nullable
@@ -322,10 +322,10 @@
private final ReentrantLock mDemuxLock = new ReentrantLock();
private int mRequestedCiCamId;
- private Integer mDemuxHandle;
- private Integer mFrontendCiCamHandle;
+ private Long mDemuxHandle;
+ private Long mFrontendCiCamHandle;
private Integer mFrontendCiCamId;
- private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
+ private Map<Long, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>();
private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
@@ -947,7 +947,7 @@
private void releaseDescramblers() {
synchronized (mDescramblers) {
if (!mDescramblers.isEmpty()) {
- for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
+ for (Map.Entry<Long, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
Descrambler descrambler = d.getValue().get();
if (descrambler != null) {
descrambler.close();
@@ -1008,7 +1008,7 @@
/**
* Native method to open frontend of the given ID.
*/
- private native Frontend nativeOpenFrontendByHandle(int handle);
+ private native Frontend nativeOpenFrontendByHandle(long handle);
private native int nativeShareFrontend(int id);
private native int nativeUnshareFrontend();
private native void nativeRegisterFeCbListener(long nativeContext);
@@ -1037,21 +1037,21 @@
private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
private native int nativeGetMaxNumberOfFrontends(int frontendType);
private native int nativeRemoveOutputPid(int pid);
- private native Lnb nativeOpenLnbByHandle(int handle);
+ private native Lnb nativeOpenLnbByHandle(long handle);
private native Lnb nativeOpenLnbByName(String name);
private native FrontendStatusReadiness[] nativeGetFrontendStatusReadiness(int[] statusTypes);
- private native Descrambler nativeOpenDescramblerByHandle(int handle);
- private native int nativeOpenDemuxByhandle(int handle);
+ private native Descrambler nativeOpenDescramblerByHandle(long handle);
+ private native int nativeOpenDemuxByhandle(long handle);
private native DvrRecorder nativeOpenDvrRecorder(long bufferSize);
private native DvrPlayback nativeOpenDvrPlayback(long bufferSize);
private native DemuxCapabilities nativeGetDemuxCapabilities();
- private native DemuxInfo nativeGetDemuxInfo(int demuxHandle);
+ private native DemuxInfo nativeGetDemuxInfo(long demuxHandle);
- private native int nativeCloseDemux(int handle);
- private native int nativeCloseFrontend(int handle);
+ private native int nativeCloseDemux(long handle);
+ private native int nativeCloseFrontend(long handle);
private native int nativeClose();
private static native SharedFilter nativeOpenSharedFilter(String token);
@@ -1369,7 +1369,7 @@
}
private boolean requestFrontend() {
- int[] feHandle = new int[1];
+ long[] feHandle = new long[1];
boolean granted = false;
try {
TunerFrontendRequest request = new TunerFrontendRequest();
@@ -2377,7 +2377,7 @@
}
private boolean requestLnb() {
- int[] lnbHandle = new int[1];
+ long[] lnbHandle = new long[1];
TunerLnbRequest request = new TunerLnbRequest();
request.clientId = mClientId;
boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
@@ -2660,7 +2660,7 @@
}
private boolean requestDemux() {
- int[] demuxHandle = new int[1];
+ long[] demuxHandle = new long[1];
TunerDemuxRequest request = new TunerDemuxRequest();
request.clientId = mClientId;
request.desiredFilterTypes = mDesiredDemuxInfo.getFilterTypes();
@@ -2673,14 +2673,14 @@
}
private Descrambler requestDescrambler() {
- int[] descramblerHandle = new int[1];
+ long[] descramblerHandle = new long[1];
TunerDescramblerRequest request = new TunerDescramblerRequest();
request.clientId = mClientId;
boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
if (!granted) {
return null;
}
- int handle = descramblerHandle[0];
+ long handle = descramblerHandle[0];
Descrambler descrambler = nativeOpenDescramblerByHandle(handle);
if (descrambler != null) {
synchronized (mDescramblers) {
@@ -2694,7 +2694,7 @@
}
private boolean requestFrontendCiCam(int ciCamId) {
- int[] ciCamHandle = new int[1];
+ long[] ciCamHandle = new long[1];
TunerCiCamRequest request = new TunerCiCamRequest();
request.clientId = mClientId;
request.ciCamId = ciCamId;
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index d268aeb..bb581eb 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -66,7 +66,7 @@
private static final String TAG = "TunerResourceManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int INVALID_RESOURCE_HANDLE = -1;
+ public static final long INVALID_RESOURCE_HANDLE = -1;
public static final int INVALID_OWNER_ID = -1;
/**
* Tuner resource type to help generate resource handle
@@ -275,7 +275,7 @@
* Updates the current TRM of the TunerHAL Frontend information.
*
* <p><strong>Note:</strong> This update must happen before the first
- * {@link #requestFrontend(TunerFrontendRequest, int[])} and
+ * {@link #requestFrontend(TunerFrontendRequest, long[])} and
* {@link #releaseFrontend(int, int)} call.
*
* @param infos an array of the available {@link TunerFrontendInfo} information.
@@ -331,7 +331,7 @@
*
* @param lnbIds ids of the updating lnbs.
*/
- public void setLnbInfoList(int[] lnbIds) {
+ public void setLnbInfoList(long[] lnbIds) {
try {
mService.setLnbInfoList(lnbIds);
} catch (RemoteException e) {
@@ -406,8 +406,8 @@
*
* @return true if there is frontend granted.
*/
- public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @Nullable int[] frontendHandle) {
+ public boolean requestFrontend(
+ @NonNull TunerFrontendRequest request, @Nullable long[] frontendHandle) {
boolean result = false;
try {
result = mService.requestFrontend(request, frontendHandle);
@@ -511,7 +511,7 @@
*
* @return true if there is Demux granted.
*/
- public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) {
+ public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle) {
boolean result = false;
try {
result = mService.requestDemux(request, demuxHandle);
@@ -544,8 +544,8 @@
*
* @return true if there is Descrambler granted.
*/
- public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
- @NonNull int[] descramblerHandle) {
+ public boolean requestDescrambler(
+ @NonNull TunerDescramblerRequest request, @NonNull long[] descramblerHandle) {
boolean result = false;
try {
result = mService.requestDescrambler(request, descramblerHandle);
@@ -577,8 +577,8 @@
*
* @return true if there is CAS session granted.
*/
- public boolean requestCasSession(@NonNull CasSessionRequest request,
- @NonNull int[] casSessionHandle) {
+ public boolean requestCasSession(
+ @NonNull CasSessionRequest request, @NonNull long[] casSessionHandle) {
boolean result = false;
try {
result = mService.requestCasSession(request, casSessionHandle);
@@ -610,7 +610,7 @@
*
* @return true if there is ciCam granted.
*/
- public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) {
+ public boolean requestCiCam(TunerCiCamRequest request, long[] ciCamHandle) {
boolean result = false;
try {
result = mService.requestCiCam(request, ciCamHandle);
@@ -635,7 +635,7 @@
* <li>If no Lnb system can be granted, the API would return false.
* <ul>
*
- * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request.
+ * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this request.
*
* @param request {@link TunerLnbRequest} information of the current request.
* @param lnbHandle a one-element array to return the granted Lnb handle.
@@ -643,7 +643,7 @@
*
* @return true if there is Lnb granted.
*/
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle) {
boolean result = false;
try {
result = mService.requestLnb(request, lnbHandle);
@@ -664,7 +664,7 @@
* @param frontendHandle the handle of the released frontend.
* @param clientId the id of the client that is releasing the frontend.
*/
- public void releaseFrontend(int frontendHandle, int clientId) {
+ public void releaseFrontend(long frontendHandle, int clientId) {
try {
mService.releaseFrontend(frontendHandle, clientId);
} catch (RemoteException e) {
@@ -680,7 +680,7 @@
* @param demuxHandle the handle of the released Tuner Demux.
* @param clientId the id of the client that is releasing the demux.
*/
- public void releaseDemux(int demuxHandle, int clientId) {
+ public void releaseDemux(long demuxHandle, int clientId) {
try {
mService.releaseDemux(demuxHandle, clientId);
} catch (RemoteException e) {
@@ -696,7 +696,7 @@
* @param descramblerHandle the handle of the released Tuner Descrambler.
* @param clientId the id of the client that is releasing the descrambler.
*/
- public void releaseDescrambler(int descramblerHandle, int clientId) {
+ public void releaseDescrambler(long descramblerHandle, int clientId) {
try {
mService.releaseDescrambler(descramblerHandle, clientId);
} catch (RemoteException e) {
@@ -715,7 +715,7 @@
* @param casSessionHandle the handle of the released CAS session.
* @param clientId the id of the client that is releasing the cas session.
*/
- public void releaseCasSession(int casSessionHandle, int clientId) {
+ public void releaseCasSession(long casSessionHandle, int clientId) {
try {
mService.releaseCasSession(casSessionHandle, clientId);
} catch (RemoteException e) {
@@ -734,7 +734,7 @@
* @param ciCamHandle the handle of the releasing CiCam.
* @param clientId the id of the client that is releasing the CiCam.
*/
- public void releaseCiCam(int ciCamHandle, int clientId) {
+ public void releaseCiCam(long ciCamHandle, int clientId) {
try {
mService.releaseCiCam(ciCamHandle, clientId);
} catch (RemoteException e) {
@@ -747,12 +747,12 @@
*
* <p>Client must call this whenever it releases an Lnb.
*
- * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release.
+ * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this release.
*
* @param lnbHandle the handle of the released Tuner Lnb.
* @param clientId the id of the client that is releasing the lnb.
*/
- public void releaseLnb(int lnbHandle, int clientId) {
+ public void releaseLnb(long lnbHandle, int clientId) {
try {
mService.releaseLnb(lnbHandle, clientId);
} catch (RemoteException e) {
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 5399697..109c791 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -149,7 +149,7 @@
*
* @param lnbIds ids of the updating lnbs.
*/
- void setLnbInfoList(in int[] lnbIds);
+ void setLnbInfoList(in long[] lnbIds);
/*
* This API is used by the Tuner framework to request a frontend from the TunerHAL.
@@ -185,7 +185,7 @@
*
* @return true if there is frontend granted.
*/
- boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle);
+ boolean requestFrontend(in TunerFrontendRequest request, out long[] frontendHandle);
/*
* Sets the maximum usable frontends number of a given frontend type. It is used to enable or
@@ -253,7 +253,7 @@
*
* @return true if there is demux granted.
*/
- boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle);
+ boolean requestDemux(in TunerDemuxRequest request, out long[] demuxHandle);
/*
* This API is used by the Tuner framework to request an available descrambler from the
@@ -277,7 +277,7 @@
*
* @return true if there is Descrambler granted.
*/
- boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle);
+ boolean requestDescrambler(in TunerDescramblerRequest request, out long[] descramblerHandle);
/*
* This API is used by the Tuner framework to request an available Cas session. This session
@@ -303,7 +303,7 @@
*
* @return true if there is CAS session granted.
*/
- boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle);
+ boolean requestCasSession(in CasSessionRequest request, out long[] casSessionHandle);
/*
* This API is used by the Tuner framework to request an available CuCam.
@@ -328,7 +328,7 @@
*
* @return true if there is CiCam granted.
*/
- boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle);
+ boolean requestCiCam(in TunerCiCamRequest request, out long[] ciCamHandle);
/*
* This API is used by the Tuner framework to request an available Lnb from the TunerHAL.
@@ -352,7 +352,7 @@
*
* @return true if there is Lnb granted.
*/
- boolean requestLnb(in TunerLnbRequest request, out int[] lnbHandle);
+ boolean requestLnb(in TunerLnbRequest request, out long[] lnbHandle);
/*
* Notifies the TRM that the given frontend has been released.
@@ -365,7 +365,7 @@
* @param frontendHandle the handle of the released frontend.
* @param clientId the id of the client that is releasing the frontend.
*/
- void releaseFrontend(in int frontendHandle, int clientId);
+ void releaseFrontend(in long frontendHandle, int clientId);
/*
* Notifies the TRM that the Demux with the given handle was released.
@@ -375,7 +375,7 @@
* @param demuxHandle the handle of the released Tuner Demux.
* @param clientId the id of the client that is releasing the demux.
*/
- void releaseDemux(in int demuxHandle, int clientId);
+ void releaseDemux(in long demuxHandle, int clientId);
/*
* Notifies the TRM that the Descrambler with the given handle was released.
@@ -385,7 +385,7 @@
* @param descramblerHandle the handle of the released Tuner Descrambler.
* @param clientId the id of the client that is releasing the descrambler.
*/
- void releaseDescrambler(in int descramblerHandle, int clientId);
+ void releaseDescrambler(in long descramblerHandle, int clientId);
/*
* Notifies the TRM that the given Cas session has been released.
@@ -397,7 +397,7 @@
* @param casSessionHandle the handle of the released CAS session.
* @param clientId the id of the client that is releasing the cas session.
*/
- void releaseCasSession(in int casSessionHandle, int clientId);
+ void releaseCasSession(in long casSessionHandle, int clientId);
/**
* Notifies the TRM that the given CiCam has been released.
@@ -410,7 +410,7 @@
* @param ciCamHandle the handle of the releasing CiCam.
* @param clientId the id of the client that is releasing the CiCam.
*/
- void releaseCiCam(in int ciCamHandle, int clientId);
+ void releaseCiCam(in long ciCamHandle, int clientId);
/*
* Notifies the TRM that the Lnb with the given handle was released.
@@ -422,7 +422,7 @@
* @param lnbHandle the handle of the released Tuner Lnb.
* @param clientId the id of the client that is releasing the lnb.
*/
- void releaseLnb(in int lnbHandle, int clientId);
+ void releaseLnb(in long lnbHandle, int clientId);
/*
* Compare two clients' priority.
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
index c14caf5..7984c38 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
@@ -26,7 +26,7 @@
/**
* Demux handle
*/
- int handle;
+ long handle;
/**
* Supported filter types (defined in {@link android.media.tv.tuner.filter.Filter})
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
index 8981ce0..274367e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
@@ -26,7 +26,7 @@
/**
* Frontend Handle
*/
- int handle;
+ long handle;
/**
* Frontend Type
diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h
index 060abfd..ffbf0a8 100644
--- a/media/jni/android_media_MediaCodecLinearBlock.h
+++ b/media/jni/android_media_MediaCodecLinearBlock.h
@@ -62,7 +62,7 @@
std::shared_ptr<C2Buffer> buffer =
C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
for (const std::shared_ptr<const C2Info> &info : mBuffer->info()) {
- std::shared_ptr<C2Param> param = std::move(C2Param::Copy(*info));
+ std::shared_ptr<C2Param> param = C2Param::Copy(*info);
buffer->setInfo(std::static_pointer_cast<C2Info>(param));
}
return buffer;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 00b0e57..5eb2485 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1448,7 +1448,7 @@
return obj;
}
-jobject JTuner::openFrontendByHandle(int feHandle) {
+jobject JTuner::openFrontendByHandle(jlong feHandle) {
// TODO: Handle reopening frontend with different handle
sp<FrontendClient> feClient = sTunerClient->openFrontend(feHandle);
if (feClient == nullptr) {
@@ -1824,7 +1824,7 @@
return valObj;
}
-jobject JTuner::openLnbByHandle(int handle) {
+jobject JTuner::openLnbByHandle(jlong handle) {
if (sTunerClient == nullptr) {
return nullptr;
}
@@ -1833,7 +1833,7 @@
sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
lnbClient = sTunerClient->openLnb(handle);
if (lnbClient == nullptr) {
- ALOGD("Failed to open lnb, handle = %d", handle);
+ ALOGD("Failed to open lnb, handle = %ld", handle);
return nullptr;
}
@@ -1947,7 +1947,7 @@
return (int)result;
}
-Result JTuner::openDemux(int handle) {
+Result JTuner::openDemux(jlong handle) {
if (sTunerClient == nullptr) {
return Result::NOT_INITIALIZED;
}
@@ -2215,7 +2215,7 @@
numBytesInSectionFilter, filterCaps, filterCapsList, linkCaps, bTimeFilter);
}
-jobject JTuner::getDemuxInfo(int handle) {
+jobject JTuner::getDemuxInfo(jlong handle) {
if (sTunerClient == nullptr) {
ALOGE("tuner is not initialized");
return nullptr;
@@ -3768,8 +3768,8 @@
return tuner->getFrontendIds();
}
-static jobject android_media_tv_Tuner_open_frontend_by_handle(
- JNIEnv *env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_open_frontend_by_handle(JNIEnv *env, jobject thiz,
+ jlong handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->openFrontendByHandle(handle);
}
@@ -3901,7 +3901,7 @@
return tuner->getFrontendInfo(id);
}
-static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jlong handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->openLnbByHandle(handle);
}
@@ -4622,7 +4622,7 @@
return (int)r;
}
-static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jint) {
+static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jlong) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->openDescrambler();
}
@@ -4690,12 +4690,12 @@
return tuner->getDemuxCaps();
}
-static jobject android_media_tv_Tuner_get_demux_info(JNIEnv* env, jobject thiz, jint handle) {
+static jobject android_media_tv_Tuner_get_demux_info(JNIEnv *env, jobject thiz, jlong handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getDemuxInfo(handle);
}
-static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) {
+static jint android_media_tv_Tuner_open_demux(JNIEnv *env, jobject thiz, jlong handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return (jint)tuner->openDemux(handle);
}
@@ -4706,7 +4706,7 @@
return (jint)tuner->close();
}
-static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) {
+static jint android_media_tv_Tuner_close_demux(JNIEnv *env, jobject thiz, jlong /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeDemux();
}
@@ -4766,7 +4766,7 @@
return tuner->getFrontendStatusReadiness(types);
}
-static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
+static jint android_media_tv_Tuner_close_frontend(JNIEnv *env, jobject thiz, jlong /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeFrontend();
}
@@ -5035,7 +5035,7 @@
{ "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version },
{ "nativeGetFrontendIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_frontend_ids },
- { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+ { "nativeOpenFrontendByHandle", "(J)Landroid/media/tv/tuner/Tuner$Frontend;",
(void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeShareFrontend", "(I)I",
(void *)android_media_tv_Tuner_share_frontend },
@@ -5074,11 +5074,11 @@
(void *)android_media_tv_Tuner_open_filter },
{ "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;",
(void *)android_media_tv_Tuner_open_time_filter },
- { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
+ { "nativeOpenLnbByHandle", "(J)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_handle },
{ "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_name },
- { "nativeOpenDescramblerByHandle", "(I)Landroid/media/tv/tuner/Descrambler;",
+ { "nativeOpenDescramblerByHandle", "(J)Landroid/media/tv/tuner/Descrambler;",
(void *)android_media_tv_Tuner_open_descrambler },
{ "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;",
(void *)android_media_tv_Tuner_open_dvr_recorder },
@@ -5086,12 +5086,12 @@
(void *)android_media_tv_Tuner_open_dvr_playback },
{ "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
(void *)android_media_tv_Tuner_get_demux_caps },
- { "nativeGetDemuxInfo", "(I)Landroid/media/tv/tuner/DemuxInfo;",
+ { "nativeGetDemuxInfo", "(J)Landroid/media/tv/tuner/DemuxInfo;",
(void *)android_media_tv_Tuner_get_demux_info },
- { "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux },
+ { "nativeOpenDemuxByhandle", "(J)I", (void *)android_media_tv_Tuner_open_demux },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
- { "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
- { "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
+ { "nativeCloseFrontend", "(J)I", (void *)android_media_tv_Tuner_close_frontend },
+ { "nativeCloseDemux", "(J)I", (void *)android_media_tv_Tuner_close_demux },
{ "nativeOpenSharedFilter",
"(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
(void *)android_media_tv_Tuner_open_shared_filter},
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 3de3ab9..7af2cd7 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -206,7 +206,7 @@
int disconnectCiCam();
int unlinkCiCam(jint id);
jobject getFrontendIds();
- jobject openFrontendByHandle(int feHandle);
+ jobject openFrontendByHandle(jlong feHandle);
int shareFrontend(int feId);
int unshareFrontend();
void registerFeCbListener(JTuner* jtuner);
@@ -221,16 +221,16 @@
int setLnb(sp<LnbClient> lnbClient);
bool isLnaSupported();
int setLna(bool enable);
- jobject openLnbByHandle(int handle);
+ jobject openLnbByHandle(jlong handle);
jobject openLnbByName(jstring name);
jobject openFilter(DemuxFilterType type, int bufferSize);
jobject openTimeFilter();
jobject openDescrambler();
jobject openDvr(DvrType type, jlong bufferSize);
jobject getDemuxCaps();
- jobject getDemuxInfo(int handle);
+ jobject getDemuxInfo(jlong handle);
jobject getFrontendStatus(jintArray types);
- Result openDemux(int handle);
+ Result openDemux(jlong handle);
jint close();
jint closeFrontend();
jint closeDemux();
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index ea623d9..0097710 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -56,7 +56,7 @@
return ids;
}
-sp<FrontendClient> TunerClient::openFrontend(int32_t frontendHandle) {
+sp<FrontendClient> TunerClient::openFrontend(int64_t frontendHandle) {
if (mTunerService != nullptr) {
shared_ptr<ITunerFrontend> tunerFrontend;
Status s = mTunerService->openFrontend(frontendHandle, &tunerFrontend);
@@ -94,7 +94,7 @@
return nullptr;
}
-sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) {
+sp<DemuxClient> TunerClient::openDemux(int64_t demuxHandle) {
if (mTunerService != nullptr) {
shared_ptr<ITunerDemux> tunerDemux;
Status s = mTunerService->openDemux(demuxHandle, &tunerDemux);
@@ -107,7 +107,7 @@
return nullptr;
}
-shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int32_t demuxHandle) {
+shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int64_t demuxHandle) {
if (mTunerService != nullptr) {
DemuxInfo aidlDemuxInfo;
Status s = mTunerService->getDemuxInfo(demuxHandle, &aidlDemuxInfo);
@@ -141,7 +141,7 @@
return nullptr;
}
-sp<DescramblerClient> TunerClient::openDescrambler(int32_t descramblerHandle) {
+sp<DescramblerClient> TunerClient::openDescrambler(int64_t descramblerHandle) {
if (mTunerService != nullptr) {
shared_ptr<ITunerDescrambler> tunerDescrambler;
Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
@@ -154,7 +154,7 @@
return nullptr;
}
-sp<LnbClient> TunerClient::openLnb(int32_t lnbHandle) {
+sp<LnbClient> TunerClient::openLnb(int64_t lnbHandle) {
if (mTunerService != nullptr) {
shared_ptr<ITunerLnb> tunerLnb;
Status s = mTunerService->openLnb(lnbHandle, &tunerLnb);
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ab120b..a348586 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -65,7 +65,7 @@
* @param frontendHandle the handle of the frontend granted by TRM.
* @return a newly created FrontendClient interface.
*/
- sp<FrontendClient> openFrontend(int32_t frontendHandle);
+ sp<FrontendClient> openFrontend(int64_t frontendHandle);
/**
* Retrieve the granted frontend's information.
@@ -81,7 +81,7 @@
* @param demuxHandle the handle of the demux granted by TRM.
* @return a newly created DemuxClient interface.
*/
- sp<DemuxClient> openDemux(int32_t demuxHandle);
+ sp<DemuxClient> openDemux(int64_t demuxHandle);
/**
* Retrieve the DemuxInfo of a specific demux
@@ -89,7 +89,7 @@
* @param demuxHandle the handle of the demux to query demux info for
* @return the demux info
*/
- shared_ptr<DemuxInfo> getDemuxInfo(int32_t demuxHandle);
+ shared_ptr<DemuxInfo> getDemuxInfo(int64_t demuxHandle);
/**
* Retrieve a list of demux info
@@ -111,7 +111,7 @@
* @param descramblerHandle the handle of the descrambler granted by TRM.
* @return a newly created DescramblerClient interface.
*/
- sp<DescramblerClient> openDescrambler(int32_t descramblerHandle);
+ sp<DescramblerClient> openDescrambler(int64_t descramblerHandle);
/**
* Open a new interface of LnbClient given an lnbHandle.
@@ -119,7 +119,7 @@
* @param lnbHandle the handle of the LNB granted by TRM.
* @return a newly created LnbClient interface.
*/
- sp<LnbClient> openLnb(int32_t lnbHandle);
+ sp<LnbClient> openLnb(int64_t lnbHandle);
/**
* Open a new interface of LnbClient given a LNB name.
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index b0d1f71..447e980 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -220,11 +220,14 @@
field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
field public static final String CATEGORY_OTHER = "other";
field public static final String CATEGORY_PAYMENT = "payment";
+ field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String DH = "DH";
+ field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String ESE = "ESE";
field public static final String EXTRA_CATEGORY = "category";
field public static final String EXTRA_SERVICE_COMPONENT = "component";
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final String UICC = "UICC";
}
public abstract class HostApduService extends android.app.Service {
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index ae63e19..2ff9829 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -75,6 +75,8 @@
public final class CardEmulation {
method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+ method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, @Nullable String, @Nullable String);
+ method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
}
}
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index cb97f23..79f1275 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -48,6 +48,6 @@
boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
- boolean overrideRoutingTable(int userHandle, String protocol, String technology);
- boolean recoverRoutingTable(int userHandle);
+ void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
+ void recoverRoutingTable(int userHandle);
}
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index e0438ce..03372b2 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -23,6 +23,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
@@ -43,6 +44,8 @@
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.List;
@@ -148,6 +151,21 @@
* that service will be invoked directly.
*/
public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+ /**
+ * Route to Device Host (DH).
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+ public static final String DH = "DH";
+ /**
+ * Route to eSE.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+ public static final String ESE = "ESE";
+ /**
+ * Route to UICC.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+ public static final String UICC = "UICC";
static boolean sIsInitialized = false;
static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
@@ -865,11 +883,22 @@
sService.setServiceEnabledForCategoryOther(userId, service, status), false);
}
+ /** @hide */
+ @StringDef({
+ DH,
+ ESE,
+ UICC
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtocolAndTechnologyRoute {}
+
/**
* Setting NFC controller routing table, which includes Protocol Route and Technology Route,
* while this Activity is in the foreground.
*
- * The parameter set to null can be used to keep current values for that entry.
+ * The parameter set to null can be used to keep current values for that entry. Either
+ * Protocol Route or Technology Route should be override when calling this API, otherwise
+ * throw {@link IllegalArgumentException}.
* <p>
* Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
* <pre>
@@ -877,26 +906,39 @@
* mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
* }</pre>
* </p>
- * Also activities must call this method when it goes to the background,
- * with all parameters set to null.
+ * Also activities must call {@link #recoverRoutingTable(Activity)}
+ * when it goes to the background. Only the package of the
+ * currently preferred service (the service set as preferred by the current foreground
+ * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
+ * current Default Wallet Role Holder {@link android.app.role.RoleManager#ROLE_WALLET}),
+ * otherwise a call to this method will fail and throw {@link SecurityException}.
* @param activity The Activity that requests NFC controller routing table to be changed.
* @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
* @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
- * @return true if operation is successful and false otherwise
- *
+ * @throws SecurityException if the caller is not the preferred NFC service
+ * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the
+ * foreground, or both protocol route and technology route are null.
+ * <p>
* This is a high risk API and only included to support mainline effort
* @hide
*/
- public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
- if (activity == null) {
- throw new NullPointerException("activity or service or category is null");
- }
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+ public void overrideRoutingTable(
+ @NonNull Activity activity, @ProtocolAndTechnologyRoute @Nullable String protocol,
+ @ProtocolAndTechnologyRoute @Nullable String technology) {
if (!activity.isResumed()) {
throw new IllegalArgumentException("Activity must be resumed.");
}
- return callServiceReturn(() ->
+ if (protocol == null && technology == null) {
+ throw new IllegalArgumentException(("Both Protocol and Technology are null."));
+ }
+ callService(() ->
sService.overrideRoutingTable(
- mContext.getUser().getIdentifier(), protocol, technology), false);
+ mContext.getUser().getIdentifier(),
+ protocol,
+ technology,
+ mContext.getPackageName()));
}
/**
@@ -904,20 +946,19 @@
* which was changed by {@link #overrideRoutingTable(Activity, String, String)}
*
* @param activity The Activity that requested NFC controller routing table to be changed.
- * @return true if operation is successful and false otherwise
+ * @throws IllegalArgumentException if the caller is not in the foreground.
*
* @hide
*/
- public boolean recoverRoutingTable(Activity activity) {
- if (activity == null) {
- throw new NullPointerException("activity is null");
- }
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+ public void recoverRoutingTable(@NonNull Activity activity) {
if (!activity.isResumed()) {
throw new IllegalArgumentException("Activity must be resumed.");
}
- return callServiceReturn(() ->
+ callService(() ->
sService.recoverRoutingTable(
- mContext.getUser().getIdentifier()), false);
+ mContext.getUser().getIdentifier()));
}
/**
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 5819b98..0fda91d 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -133,3 +133,11 @@
description: "Add Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS"
bug: "358129872"
}
+
+flag {
+ name: "nfc_override_recover_routing_table"
+ is_exported: true
+ namespace: "nfc"
+ description: "Enable override and recover routing table"
+ bug: "329043523"
+}
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index ff83610..da6efee 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -30,14 +30,47 @@
"name": "CtsIntentSignatureTestCases"
},
{
- "name": "CtsPackageInstallerCUJTestCases",
+ "name": "CtsPackageInstallerCUJInstallationTestCases",
"options":[
- {
- "exclude-annotation":"androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation":"org.junit.Ignore"
- }
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUninstallationTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
]
}
]
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
index d33433f..2fb32a7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
@@ -16,10 +16,12 @@
package com.android.packageinstaller;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.BroadcastOptions;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.PendingIntent;
@@ -161,25 +163,31 @@
return;
}
+ // Allow the error handling actvities to start in the background.
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
switch (mStatus) {
case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
- null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+ null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+ options.toBundle());
break;
case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
if (mExtraIntent != null) {
activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
- null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+ null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+ options.toBundle());
} else {
Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
- startActivity(intent);
+ startActivity(intent, options.toBundle());
}
break;
case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", mInstallerPackageName, null);
intent.setData(uri);
- startActivity(intent);
+ startActivity(intent, options.toBundle());
break;
default:
// Do nothing. The rest of the dialogs are purely informational.
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
index 4c75344..526ce14 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
@@ -19,8 +19,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp">
+ android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+ android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd">
<Spinner
android:id="@+id/spinner"
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index f36344a..a543450 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-beta05"
+ extra["jetpackComposeVersion"] = "1.7.0-beta07"
}
subprojects {
@@ -37,7 +37,7 @@
plugins.withType<AndroidBasePlugin> {
configure<BaseExtension> {
- compileSdkVersion(34)
+ compileSdkVersion(35)
defaultConfig {
minSdk = 21
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 1cca73a..3507605 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.5.1"
+agp = "8.5.2"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index ce3d96e..e9153e3 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,13 +54,13 @@
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.3.0-beta04")
+ api("androidx.compose.material3:material3:1.3.0-beta05")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-beta05")
+ api("androidx.navigation:navigation-compose:2.8.0-beta07")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index de60fdc2..b3ac54a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -21,6 +21,8 @@
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.provider.DeviceConfig;
@@ -41,9 +43,12 @@
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import com.google.common.collect.ImmutableSet;
+
import java.io.IOException;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -57,6 +62,9 @@
public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
+ private static final Set<Integer> SA_PROFILES =
+ ImmutableSet.of(
+ BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
private static ErrorListener sErrorListener;
@@ -895,4 +903,62 @@
}
return null;
}
+
+ /**
+ * Gets {@link AudioDeviceAttributes} of bluetooth device for spatial audio. Returns null if
+ * it's not an audio device(no A2DP, LE Audio and Hearing Aid profile).
+ */
+ @Nullable
+ public static AudioDeviceAttributes getAudioDeviceAttributesForSpatialAudio(
+ CachedBluetoothDevice cachedDevice,
+ @AudioManager.AudioDeviceCategory int audioDeviceCategory) {
+ AudioDeviceAttributes saDevice = null;
+ for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) {
+ // pick first enabled profile that is compatible with spatial audio
+ if (SA_PROFILES.contains(profile.getProfileId())
+ && profile.isEnabled(cachedDevice.getDevice())) {
+ switch (profile.getProfileId()) {
+ case BluetoothProfile.A2DP:
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ cachedDevice.getAddress());
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ if (audioDeviceCategory
+ == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ cachedDevice.getAddress());
+ } else {
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ cachedDevice.getAddress());
+ }
+
+ break;
+ case BluetoothProfile.HEARING_AID:
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ cachedDevice.getAddress());
+ break;
+ default:
+ Log.i(
+ TAG,
+ "unrecognized profile for spatial audio: "
+ + profile.getProfileId());
+ break;
+ }
+ break;
+ }
+ }
+ return saDevice;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
index 20a0339..58dc8c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -108,6 +108,12 @@
/** Device setting ID for device details footer. */
int DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER = 19;
+ /** Device setting ID for spatial audio group. */
+ int DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE = 20;
+
+ /** Device setting ID for "More Settings" page. */
+ int DEVICE_SETTING_ID_MORE_SETTINGS = 21;
+
/** Device setting ID for ANC. */
int DEVICE_SETTING_ID_ANC = 1001;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index a599dd1..ce7064c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -29,6 +29,7 @@
import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
@@ -117,7 +118,7 @@
id = settingId,
title = pref.title,
summary = pref.summary,
- icon = pref.icon,
+ icon = pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) },
isAllowedChangingState = pref.isAllowedChangingState,
intent = pref.intent,
switchState =
@@ -153,5 +154,6 @@
else -> DeviceSettingModel.Unknown(cachedDevice, settingId)
}
- private fun ToggleInfo.toModel(): ToggleModel = ToggleModel(label, icon)
+ private fun ToggleInfo.toModel(): ToggleModel =
+ ToggleModel(label, DeviceSettingIcon.BitmapIcon(icon))
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
index 136abad..e97f76c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
@@ -35,7 +35,7 @@
/** A built-in item in Settings. */
data class BuiltinItem(
@DeviceSettingId override val settingId: Int,
- val preferenceKey: String
+ val preferenceKey: String?
) : DeviceSettingConfigItemModel
/** A remote item provided by other apps. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
index db78280..2a63217 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
@@ -18,6 +18,7 @@
import android.content.Intent
import android.graphics.Bitmap
+import androidx.annotation.DrawableRes
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
@@ -32,7 +33,7 @@
@DeviceSettingId override val id: Int,
val title: String,
val summary: String? = null,
- val icon: Bitmap? = null,
+ val icon: DeviceSettingIcon? = null,
val intent: Intent? = null,
val switchState: DeviceSettingStateModel.ActionSwitchPreferenceState? = null,
val isAllowedChangingState: Boolean = true,
@@ -59,4 +60,12 @@
}
/** Models a toggle in [DeviceSettingModel.MultiTogglePreference]. */
-data class ToggleModel(val label: String, val icon: Bitmap)
+data class ToggleModel(val label: String, val icon: DeviceSettingIcon)
+
+/** Models an icon in device settings. */
+sealed interface DeviceSettingIcon {
+
+ data class BitmapIcon(val bitmap: Bitmap) : DeviceSettingIcon
+
+ data class ResourceIcon(@DrawableRes val resId: Int) : DeviceSettingIcon
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 4b141e7..69ef718 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenModeConfig.tryParseCountdownConditionId;
import static android.service.notification.ZenModeConfig.tryParseEventConditionId;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
@@ -129,7 +130,11 @@
}
public static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
- return new ZenMode(MANUAL_DND_MODE_ID, manualRule,
+ // Manual rule is owned by the system, so we set it here
+ AutomaticZenRule manualRuleWithPkg = new AutomaticZenRule.Builder(manualRule)
+ .setPackage(PACKAGE_ANDROID)
+ .build();
+ return new ZenMode(MANUAL_DND_MODE_ID, manualRuleWithPkg,
isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED, true);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 4551f1e..926d3cb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -31,10 +31,13 @@
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.Uri;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -70,6 +73,9 @@
@Mock private BluetoothDevice mBluetoothDevice;
@Mock private AudioManager mAudioManager;
@Mock private PackageManager mPackageManager;
+ @Mock private LeAudioProfile mA2dpProfile;
+ @Mock private LeAudioProfile mLeAudioProfile;
+ @Mock private LeAudioProfile mHearingAid;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private LocalBluetoothManager mLocalBluetoothManager;
@@ -100,6 +106,9 @@
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
+ when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
+ when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+ when(mHearingAid.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
}
@Test
@@ -756,4 +765,84 @@
mContext.getContentResolver(), mLocalBluetoothManager))
.isEqualTo(mCachedBluetoothDevice);
}
+
+ @Test
+ public void getAudioDeviceAttributesForSpatialAudio_bleHeadset() {
+ String address = "11:22:33:44:55:66";
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
+ when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+ AudioDeviceAttributes attr =
+ BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+ mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+
+ assertThat(attr)
+ .isEqualTo(
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ address));
+ }
+
+ @Test
+ public void getAudioDeviceAttributesForSpatialAudio_bleSpeaker() {
+ String address = "11:22:33:44:55:66";
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
+ when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+ AudioDeviceAttributes attr =
+ BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+ mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER);
+
+ assertThat(attr)
+ .isEqualTo(
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ address));
+ }
+
+ @Test
+ public void getAudioDeviceAttributesForSpatialAudio_a2dp() {
+ String address = "11:22:33:44:55:66";
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mA2dpProfile));
+ when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+ AudioDeviceAttributes attr =
+ BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+ mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+
+ assertThat(attr)
+ .isEqualTo(
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ address));
+ }
+
+ @Test
+ public void getAudioDeviceAttributesForSpatialAudio_hearingAid() {
+ String address = "11:22:33:44:55:66";
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mHearingAid));
+ when(mHearingAid.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+ AudioDeviceAttributes attr =
+ BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+ mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID);
+
+ assertThat(attr)
+ .isEqualTo(
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ address));
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index fee2394..4c5ee9e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -40,6 +40,7 @@
import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
@@ -352,7 +353,7 @@
val pref = serviceResponse.preference as ActionSwitchPreference
assertThat(actual.title).isEqualTo(pref.title)
assertThat(actual.summary).isEqualTo(pref.summary)
- assertThat(actual.icon).isEqualTo(pref.icon)
+ assertThat(actual.icon).isEqualTo(DeviceSettingIcon.BitmapIcon(pref.icon!!))
assertThat(actual.isAllowedChangingState).isEqualTo(pref.isAllowedChangingState)
if (pref.hasSwitch()) {
assertThat(actual.switchState!!.checked).isEqualTo(pref.checked)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
index d9fdcc38..f464247 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -19,6 +19,7 @@
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -67,6 +68,7 @@
assertThat(manualMode.canEditNameAndIcon()).isFalse();
assertThat(manualMode.canBeDeleted()).isFalse();
assertThat(manualMode.isActive()).isFalse();
+ assertThat(manualMode.getRule().getPackageName()).isEqualTo(PACKAGE_ANDROID);
}
@Test
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6d78705..5ea75be 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -748,6 +748,7 @@
"//frameworks/libs/systemui:motion_tool_lib",
"//frameworks/libs/systemui:contextualeducationlib",
"androidx.core_core-animation-testing",
+ "androidx.lifecycle_lifecycle-runtime-testing",
"androidx.compose.ui_ui",
"flag-junit",
"ravenwood-junit",
@@ -789,6 +790,7 @@
"SystemUI-tests-base",
"androidx.test.uiautomator_uiautomator",
"androidx.core_core-animation-testing",
+ "androidx.lifecycle_lifecycle-runtime-testing",
"mockito-target-extended-minus-junit4",
"mockito-kotlin-nodeps",
"androidx.test.ext.junit",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 5632e30..047c097 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -380,6 +380,17 @@
}
flag {
+ name: "status_bar_stop_updating_window_height"
+ namespace: "systemui"
+ description: "Don't have PhoneStatusBarView manually trigger an update of the height in "
+ "StatusBarWindowController"
+ bug: "360115167"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -1038,6 +1049,13 @@
}
flag {
+ name: "media_controls_button_media3"
+ namespace: "systemui"
+ description: "Enable media action buttons updates using media3"
+ bug: "360196209"
+}
+
+flag {
namespace: "systemui"
name: "enable_view_capture_tracing"
description: "Enables view capture tracing in System UI."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index b93b049..7472d81 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -1410,7 +1410,10 @@
R.string.accessibility_action_label_close_communal_hub
)
) {
- viewModel.changeScene(CommunalScenes.Blank)
+ viewModel.changeScene(
+ CommunalScenes.Blank,
+ "closed by accessibility"
+ )
true
},
CustomAccessibilityAction(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index eea00c4..fb7c422 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -29,8 +29,6 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -42,6 +40,7 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.rememberViewModel
@@ -114,7 +113,7 @@
}
@Composable
-private fun ShadeBody(
+fun ShadeBody(
viewModel: QuickSettingsContainerViewModel,
) {
val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
@@ -131,6 +130,7 @@
} else {
QuickSettingsLayout(
viewModel = viewModel,
+ modifier = Modifier.sysuiResTag("quick_settings_panel")
)
}
}
@@ -158,11 +158,6 @@
Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
viewModel.editModeViewModel::startEditing,
)
- Button(
- onClick = { viewModel.editModeViewModel.startEditing() },
- ) {
- Text("Edit mode")
- }
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index b329534..3487730 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -81,6 +81,7 @@
enabled: () -> Boolean,
startDragImmediately: (startedPosition: Offset) -> Boolean,
onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ onFirstPointerDown: () -> Unit = {},
swipeDetector: SwipeDetector = DefaultSwipeDetector,
dispatcher: NestedScrollDispatcher,
): Modifier =
@@ -90,6 +91,7 @@
enabled,
startDragImmediately,
onDragStarted,
+ onFirstPointerDown,
swipeDetector,
dispatcher,
)
@@ -101,6 +103,7 @@
private val startDragImmediately: (startedPosition: Offset) -> Boolean,
private val onDragStarted:
(startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ private val onFirstPointerDown: () -> Unit,
private val swipeDetector: SwipeDetector,
private val dispatcher: NestedScrollDispatcher,
) : ModifierNodeElement<MultiPointerDraggableNode>() {
@@ -110,6 +113,7 @@
enabled = enabled,
startDragImmediately = startDragImmediately,
onDragStarted = onDragStarted,
+ onFirstPointerDown = onFirstPointerDown,
swipeDetector = swipeDetector,
dispatcher = dispatcher,
)
@@ -119,6 +123,7 @@
node.enabled = enabled
node.startDragImmediately = startDragImmediately
node.onDragStarted = onDragStarted
+ node.onFirstPointerDown = onFirstPointerDown
node.swipeDetector = swipeDetector
}
}
@@ -129,6 +134,7 @@
var startDragImmediately: (startedPosition: Offset) -> Boolean,
var onDragStarted:
(startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+ var onFirstPointerDown: () -> Unit,
var swipeDetector: SwipeDetector = DefaultSwipeDetector,
private val dispatcher: NestedScrollDispatcher,
) :
@@ -225,6 +231,7 @@
startedPosition = null
} else if (startedPosition == null) {
startedPosition = pointers.first().position
+ onFirstPointerDown()
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index f062146..d1e83ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -67,6 +67,7 @@
enabled = ::enabled,
startDragImmediately = ::startDragImmediately,
onDragStarted = draggableHandler::onDragStarted,
+ onFirstPointerDown = ::onFirstPointerDown,
swipeDetector = swipeDetector,
dispatcher = dispatcher,
)
@@ -101,6 +102,15 @@
delegate(ScrollBehaviorOwnerNode(draggableHandler.nestedScrollKey, nestedScrollHandlerImpl))
}
+ private fun onFirstPointerDown() {
+ // When we drag our finger across the screen, the NestedScrollConnection keeps track of all
+ // the scroll events until we lift our finger. However, in some cases, the connection might
+ // not receive the "up" event. This can lead to an incorrect initial state for the gesture.
+ // To prevent this issue, we can call the reset() method when the first finger touches the
+ // screen. This ensures that the NestedScrollConnection starts from a correct state.
+ nestedScrollHandlerImpl.connection.reset()
+ }
+
override fun onDetach() {
// Make sure we reset the scroll connection when this modifier is removed from composition
nestedScrollHandlerImpl.connection.reset()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 228f7ba..16fb533b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -129,7 +129,11 @@
return onPriorityStop(available)
}
- /** Method to call before destroying the object or to reset the initial state. */
+ /**
+ * Method to call before destroying the object or to reset the initial state.
+ *
+ * TODO(b/303224944) This method should be removed.
+ */
fun reset() {
// Step 3c: To ensure that an onStop is always called for every onStart.
onPriorityStop(velocity = Velocity.Zero)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 9ebc426..d8a06f5 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -23,6 +23,9 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalViewConfiguration
@@ -266,4 +269,38 @@
val transition = assertThat(state.transitionState).isTransition()
assertThat(transition).hasProgress(0.5f)
}
+
+ @Test
+ fun resetScrollTracking_afterMissingPointerUpEvent() {
+ var canScroll = true
+ var hasScrollable by mutableStateOf(true)
+ val state = setup2ScenesAndScrollTouchSlop {
+ if (hasScrollable) {
+ Modifier.scrollable(rememberScrollableState { if (canScroll) it else 0f }, Vertical)
+ } else {
+ Modifier
+ }
+ }
+
+ // The gesture is consumed by the component in the scene.
+ scrollUp(percent = 0.2f)
+
+ // STL keeps track of the scroll consumed. The scene remains in Idle.
+ assertThat(state.transitionState).isIdle()
+
+ // The scrollable component disappears, and does not send the signal (pointer up) to reset
+ // the consumed amount.
+ hasScrollable = false
+ pointerUp()
+
+ // A new scrollable component appears and allows the scene to consume the scroll.
+ hasScrollable = true
+ canScroll = false
+ pointerDownAndScrollTouchSlop()
+ scrollUp(percent = 0.2f)
+
+ // STL can only start the transition if it has reset the amount of scroll consumed.
+ val transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasProgress(0.2f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 9ccf99b..70529cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -112,7 +112,7 @@
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setEditModeState(EditModeState.STARTING)
@@ -133,7 +133,7 @@
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(true)
@@ -154,7 +154,7 @@
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(false)
@@ -174,7 +174,7 @@
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalInteractor.setEditModeOpen(true)
@@ -213,7 +213,7 @@
testScope.runTest {
whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(false)
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
updateDocked(true)
@@ -233,7 +233,7 @@
testScope.runTest {
whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(true)
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
updateDocked(true)
@@ -270,7 +270,7 @@
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -292,7 +292,7 @@
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
assertThat(scene).isEqualTo(CommunalScenes.Communal)
fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -320,7 +320,7 @@
fun dockingOnLockscreen_forcesCommunal() =
with(kosmos) {
testScope.runTest {
- communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
// device is docked while on the lockscreen
@@ -342,7 +342,7 @@
fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
with(kosmos) {
testScope.runTest {
- communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
// device is docked while on the lockscreen
@@ -374,7 +374,7 @@
testScope.runTest {
// Device is dreaming and on communal.
updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -391,7 +391,7 @@
testScope.runTest {
// Device is not dreaming and on communal.
updateDreaming(false)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
// Scene stays as Communal
advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
@@ -406,7 +406,7 @@
testScope.runTest {
// Device is dreaming and on communal.
updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -429,7 +429,7 @@
testScope.runTest {
// Device is on communal, but not dreaming.
updateDreaming(false)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -450,7 +450,7 @@
with(kosmos) {
testScope.runTest {
// Device is on communal.
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
// Device stays on the hub after the timeout since we're not dreaming.
advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
@@ -471,7 +471,7 @@
testScope.runTest {
// Device is dreaming and on communal.
updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -500,7 +500,7 @@
// Device is dreaming and on communal.
updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
val scene by collectLastValue(communalSceneInteractor.currentScene)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -520,7 +520,7 @@
with(kosmos) {
testScope.runTest {
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
assertThat(scene).isEqualTo(CommunalScenes.Blank)
fakeKeyguardTransitionRepository.sendTransitionSteps(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index e57a4cb..864795b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -482,7 +482,7 @@
testScope.runTest {
val targetScene = CommunalScenes.Communal
- underTest.changeScene(targetScene)
+ underTest.changeScene(targetScene, "test")
val desiredScene = collectLastValue(communalRepository.currentScene)
runCurrent()
@@ -635,7 +635,7 @@
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(false)
- underTest.changeScene(CommunalScenes.Communal)
+ underTest.changeScene(CommunalScenes.Communal, "test")
isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
runCurrent()
@@ -659,12 +659,12 @@
assertThat(isCommunalShowing).isFalse()
// Verify scene changes (without the flag) to communal sets the value to true
- underTest.changeScene(CommunalScenes.Communal)
+ underTest.changeScene(CommunalScenes.Communal, "test")
runCurrent()
assertThat(isCommunalShowing).isTrue()
// Verify scene changes (without the flag) to blank sets the value back to false
- underTest.changeScene(CommunalScenes.Blank)
+ underTest.changeScene(CommunalScenes.Blank, "test")
runCurrent()
assertThat(isCommunalShowing).isFalse()
}
@@ -679,7 +679,7 @@
assertThat(isCommunalShowing).isFalse()
// Verify scene changes without the flag doesn't have any impact
- underTest.changeScene(CommunalScenes.Communal)
+ underTest.changeScene(CommunalScenes.Communal, "test")
runCurrent()
assertThat(isCommunalShowing).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index 43293c7..ed7e910 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -53,7 +53,7 @@
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
- underTest.changeScene(CommunalScenes.Communal)
+ underTest.changeScene(CommunalScenes.Communal, "test")
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
@@ -63,7 +63,7 @@
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
- underTest.snapToScene(CommunalScenes.Communal)
+ underTest.snapToScene(CommunalScenes.Communal, "test")
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
@@ -75,6 +75,7 @@
assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
underTest.snapToScene(
CommunalScenes.Communal,
+ "test",
ActivityTransitionAnimator.TIMINGS.totalDuration
)
assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
@@ -86,7 +87,7 @@
fun changeSceneForActivityStartOnDismissKeyguard() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- underTest.snapToScene(CommunalScenes.Communal)
+ underTest.snapToScene(CommunalScenes.Communal, "test")
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
underTest.changeSceneForActivityStartOnDismissKeyguard()
@@ -97,7 +98,7 @@
fun changeSceneForActivityStartOnDismissKeyguard_willNotChangeScene_forEditModeActivity() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- underTest.snapToScene(CommunalScenes.Communal)
+ underTest.snapToScene(CommunalScenes.Communal, "test")
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
underTest.setEditModeState(EditModeState.STARTING)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 3a23e14..7e28e19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -158,7 +158,7 @@
kosmos.setCommunalAvailable(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalInteractor.changeScene(CommunalScenes.Blank)
+ communalInteractor.changeScene(CommunalScenes.Blank, "test")
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
}
@@ -171,7 +171,7 @@
goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalInteractor.changeScene(CommunalScenes.Blank)
+ communalInteractor.changeScene(CommunalScenes.Blank, "test")
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -184,13 +184,13 @@
goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalInteractor.changeScene(CommunalScenes.Blank)
+ communalInteractor.changeScene(CommunalScenes.Blank, "test")
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
private suspend fun goToCommunal() {
kosmos.setCommunalAvailable(true)
- communalInteractor.changeScene(CommunalScenes.Communal)
+ communalInteractor.changeScene(CommunalScenes.Communal, "test")
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
index e36fd75..a052b07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
@@ -76,7 +76,7 @@
val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(true)
assertTrue(launching!!)
@@ -103,7 +103,7 @@
val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
communalSceneInteractor.setIsLaunchingWidget(true)
assertTrue(launching!!)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 7a86e57..da82b5f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -68,7 +68,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.touch.TouchInsetManager
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -87,6 +86,7 @@
import org.mockito.Mockito.isNull
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
@@ -669,7 +669,7 @@
runCurrent()
verify(mDreamOverlayCallback).onRedirectWake(true)
client.onWakeRequested()
- verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), isNull())
+ verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), isNull())
verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 8c1e8de..9792c28 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -845,7 +845,7 @@
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
// WHEN the glanceable hub is shown
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
assertThat(transitionRepository)
@@ -1004,7 +1004,7 @@
fun alternateBouncerToGlanceableHub() =
testScope.runTest {
// GIVEN the device is idle on the glanceable hub
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
@@ -1123,7 +1123,7 @@
fun primaryBouncerToGlanceableHub() =
testScope.runTest {
// GIVEN the device is idle on the glanceable hub
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
// GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1157,7 +1157,7 @@
advanceTimeBy(600L)
// GIVEN the device is idle on the glanceable hub
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
// GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1971,7 +1971,7 @@
fun glanceableHubToLockscreen_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
@@ -2035,7 +2035,7 @@
fun glanceableHubToDozing_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
@@ -2136,7 +2136,7 @@
fun glanceableHubToOccluded_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
@@ -2184,7 +2184,7 @@
fun glanceableHubToGone_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
@@ -2265,7 +2265,7 @@
advanceTimeBy(600L)
// GIVEN a prior transition has run to GLANCEABLE_HUB
- communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
runCurrent()
clearInvocations(transitionRepository)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
new file mode 100644
index 0000000..5999265
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment.viewmodel
+
+import android.content.testableContext
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.fgsManagerController
+import com.android.systemui.res.R
+import com.android.systemui.shade.largeScreenHeaderHelper
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestResult
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSFragmentComposeViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val lifecycleOwner =
+ TestLifecycleOwner(
+ initialState = Lifecycle.State.CREATED,
+ coroutineDispatcher = kosmos.testDispatcher,
+ )
+
+ private val underTest by lazy {
+ kosmos.qsFragmentComposeViewModelFactory.create(lifecycleOwner.lifecycleScope)
+ }
+
+ @Before
+ fun setUp() {
+ Dispatchers.setMain(kosmos.testDispatcher)
+ }
+
+ @After
+ fun teardown() {
+ Dispatchers.resetMain()
+ }
+
+ // For now the state changes at 0.5f expansion. This will change once we implement animation
+ // (and this test will fail)
+ @Test
+ fun qsExpansionValueChanges_correctExpansionState() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ val expansionState by collectLastValue(underTest.expansionState)
+
+ underTest.qsExpansionValue = 0f
+ assertThat(expansionState)
+ .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
+
+ underTest.qsExpansionValue = 0.3f
+ assertThat(expansionState)
+ .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
+
+ underTest.qsExpansionValue = 0.7f
+ assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+
+ underTest.qsExpansionValue = 1f
+ assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+ }
+ }
+
+ @Test
+ fun qqsHeaderHeight_largeScreenHeader_0() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ val qqsHeaderHeight by collectLastValue(underTest.qqsHeaderHeight)
+
+ testableContext.orCreateTestableResources.addOverride(
+ R.bool.config_use_large_screen_shade_header,
+ true
+ )
+ fakeConfigurationRepository.onConfigurationChange()
+
+ assertThat(qqsHeaderHeight).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun qqsHeaderHeight_noLargeScreenHeader_providedByHelper() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ val qqsHeaderHeight by collectLastValue(underTest.qqsHeaderHeight)
+
+ testableContext.orCreateTestableResources.addOverride(
+ R.bool.config_use_large_screen_shade_header,
+ false
+ )
+ fakeConfigurationRepository.onConfigurationChange()
+
+ assertThat(qqsHeaderHeight)
+ .isEqualTo(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
+ }
+ }
+
+ @Test
+ fun footerActionsControllerInit() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest
+ runCurrent()
+ assertThat(fgsManagerController.initialized).isTrue()
+ }
+ }
+
+ private inline fun TestScope.testWithinLifecycle(
+ crossinline block: suspend TestScope.() -> TestResult
+ ): TestResult {
+ return runTest {
+ lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
+ block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
index 9563538..1118a61 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.ui.viewmodel
import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.LifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -59,7 +60,7 @@
private val footerActionsViewModel = mock<FooterActionsViewModel>()
private val footerActionsViewModelFactory =
mock<FooterActionsViewModel.Factory> {
- whenever(create(any())).thenReturn(footerActionsViewModel)
+ whenever(create(any<LifecycleOwner>())).thenReturn(footerActionsViewModel)
}
private val footerActionsController = mock<FooterActionsController>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 9005ae3..89aa670 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -241,7 +241,7 @@
alm.showNotification(entry);
final boolean removedImmediately = alm.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false);
+ entry.getKey(), /* releaseImmediately = */ false, "removeDeferred");
assertFalse(removedImmediately);
assertTrue(alm.isHeadsUpEntry(entry.getKey()));
}
@@ -254,7 +254,7 @@
alm.showNotification(entry);
final boolean removedImmediately = alm.removeNotification(
- entry.getKey(), /* releaseImmediately = */ true);
+ entry.getKey(), /* releaseImmediately = */ true, "forceRemove");
assertTrue(removedImmediately);
assertFalse(alm.isHeadsUpEntry(entry.getKey()));
}
@@ -430,7 +430,7 @@
hum.showNotification(entry);
final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false);
+ entry.getKey(), /* releaseImmediately = */ false, "beforeMinimumDisplayTime");
assertFalse(removedImmediately);
assertTrue(hum.isHeadsUpEntry(entry.getKey()));
@@ -452,7 +452,7 @@
assertTrue(hum.isHeadsUpEntry(entry.getKey()));
final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ false);
+ entry.getKey(), /* releaseImmediately = */ false, "afterMinimumDisplayTime");
assertTrue(removedImmediately);
assertFalse(hum.isHeadsUpEntry(entry.getKey()));
}
@@ -466,7 +466,7 @@
hum.showNotification(entry);
final boolean removedImmediately = hum.removeNotification(
- entry.getKey(), /* releaseImmediately = */ true);
+ entry.getKey(), /* releaseImmediately = */ true, "afterMinimumDisplayTime");
assertTrue(removedImmediately);
assertFalse(hum.isHeadsUpEntry(entry.getKey()));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
index 7a6838a..ca106fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
@@ -179,8 +179,8 @@
mContext
.getOrCreateTestableResources()
.addOverride(R.integer.ambient_notification_extension_time, 500)
- mAvalancheController = AvalancheController(dumpManager, mUiEventLogger,
- mHeadsUpManagerLogger, mBgHandler)
+ mAvalancheController =
+ AvalancheController(dumpManager, mUiEventLogger, mHeadsUpManagerLogger, mBgHandler)
}
@Test
@@ -200,7 +200,12 @@
hmp.addSwipedOutNotification(entry.key)
// Remove should succeed because the notification is swiped out
- val removedImmediately = hmp.removeNotification(entry.key, /* releaseImmediately= */ false)
+ val removedImmediately =
+ hmp.removeNotification(
+ entry.key,
+ /* releaseImmediately= */ false,
+ /* reason= */ "swipe out"
+ )
Assert.assertTrue(removedImmediately)
Assert.assertFalse(hmp.isHeadsUpEntry(entry.key))
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
index 69207ba..3efabd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
@@ -100,7 +100,7 @@
@Override
public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
- boolean animate) {
+ boolean animate, @NonNull String reason) {
throw new UnsupportedOperationException();
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
index bcad7e7..54b7d25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt
@@ -23,6 +23,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.settingslib.notification.modes.ZenMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
@@ -30,6 +31,7 @@
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,6 +42,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@SmallTest
@@ -50,9 +53,16 @@
private val repository = kosmos.fakeZenModeRepository
private val interactor = kosmos.zenModeInteractor
private val mockDialogDelegate = kosmos.mockModesDialogDelegate
+ private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger
private val underTest =
- ModesDialogViewModel(context, interactor, kosmos.testDispatcher, mockDialogDelegate)
+ ModesDialogViewModel(
+ context,
+ interactor,
+ kosmos.testDispatcher,
+ mockDialogDelegate,
+ mockDialogEventLogger
+ )
@Test
fun tiles_filtersOutUserDisabledModes() =
@@ -432,4 +442,84 @@
assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
.isEqualTo("B")
}
+
+ @Test
+ fun onClick_logsOnOffEvents() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+
+ repository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder()
+ .setId("id1")
+ .setName("Inactive Mode One")
+ .setActive(false)
+ .setManualInvocationAllowed(true)
+ .build(),
+ TestModeBuilder()
+ .setId("id2")
+ .setName("Active Non-Invokable Mode Two") // but can be turned off by tile
+ .setActive(true)
+ .setManualInvocationAllowed(false)
+ .build(),
+ )
+ )
+ runCurrent()
+
+ assertThat(tiles?.size).isEqualTo(3)
+
+ // Trigger onClick for each tile in sequence
+ tiles?.forEach { it.onClick.invoke() }
+ runCurrent()
+
+ val onModeCaptor = argumentCaptor<ZenMode>()
+ val offModeCaptor = argumentCaptor<ZenMode>()
+
+ // manual mode and mode 2 should have turned off
+ verify(mockDialogEventLogger, times(2)).logModeOff(offModeCaptor.capture())
+ val off0 = offModeCaptor.firstValue
+ assertThat(off0.isManualDnd).isTrue()
+
+ val off1 = offModeCaptor.secondValue
+ assertThat(off1.id).isEqualTo("id2")
+
+ // should also have logged turning mode 1 on
+ verify(mockDialogEventLogger).logModeOn(onModeCaptor.capture())
+ val on = onModeCaptor.lastValue
+ assertThat(on.id).isEqualTo("id1")
+ }
+
+ @Test
+ fun onLongClick_logsSettingsEvents() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+
+ repository.addModes(
+ listOf(
+ TestModeBuilder.MANUAL_DND_ACTIVE,
+ TestModeBuilder()
+ .setId("id1")
+ .setName("Inactive Mode One")
+ .setActive(false)
+ .setManualInvocationAllowed(true)
+ .build(),
+ )
+ )
+ runCurrent()
+
+ assertThat(tiles?.size).isEqualTo(2)
+ val modeCaptor = argumentCaptor<ZenMode>()
+
+ // long click manual DND and then automatic mode
+ tiles?.forEach { it.onLongClick.invoke() }
+ runCurrent()
+
+ verify(mockDialogEventLogger, times(2)).logModeSettings(modeCaptor.capture())
+ val manualMode = modeCaptor.firstValue
+ assertThat(manualMode.isManualDnd).isTrue()
+
+ val automaticMode = modeCaptor.lastValue
+ assertThat(automaticMode.id).isEqualTo("id1")
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index 7385a47..7c55f7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -32,7 +32,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.data.repository.audioSharingRepository
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.localMediaRepository
@@ -222,32 +221,4 @@
val testIcon = TestStubDrawable()
}
-
- @Test
- fun inAudioSharing_returnTrue() {
- with(kosmos) {
- testScope.runTest {
- audioSharingRepository.setInAudioSharing(true)
-
- val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
- runCurrent()
-
- assertThat(inAudioSharing).isTrue()
- }
- }
- }
-
- @Test
- fun notInAudioSharing_returnFalse() {
- with(kosmos) {
- testScope.runTest {
- audioSharingRepository.setInAudioSharing(false)
-
- val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
- runCurrent()
-
- assertThat(inAudioSharing).isFalse()
- }
- }
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
index a1fcfcd..c9d147b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
@@ -49,6 +49,24 @@
}
@Test
+ fun handleInAudioSharingChange() {
+ with(kosmos) {
+ testScope.runTest {
+ with(audioSharingRepository) { setInAudioSharing(true) }
+ val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
+ runCurrent()
+
+ Truth.assertThat(inAudioSharing).isEqualTo(true)
+
+ with(audioSharingRepository) { setInAudioSharing(false) }
+ runCurrent()
+
+ Truth.assertThat(inAudioSharing).isEqualTo(false)
+ }
+ }
+ }
+
+ @Test
fun handlePrimaryGroupChange_nullVolume() {
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f0c8894..823ff9f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1053,4 +1053,15 @@
<!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
<string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
+
+ <!--
+ Whether to enable the desktop specific feature set.
+
+ Refrain from using this from code that needs to make decisions
+ regarding the size or density of the display.
+
+ Variant owners and OEMs should override this to true when they want to
+ enable the desktop specific features.
+ -->
+ <bool name="config_enableDesktopFeatureSet">false</bool>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0da252d..60fff28 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -104,6 +104,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
@@ -119,6 +120,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -140,6 +142,9 @@
import com.android.systemui.plugins.clocks.WeatherData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -150,6 +155,7 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dalvik.annotation.optimization.NeverCompile;
@@ -279,6 +285,9 @@
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
private final boolean mIsSystemUser;
+ private final Provider<JavaAdapter> mJavaAdapter;
+ private final Provider<SceneInteractor> mSceneInteractor;
+ private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -563,7 +572,7 @@
*/
private boolean shouldDismissKeyguardOnTrustGrantedWithCurrentUser(TrustGrantFlags flags) {
final boolean isBouncerShowing =
- mPrimaryBouncerIsOrWillBeShowing || mAlternateBouncerShowing;
+ isPrimaryBouncerShowingOrWillBeShowing() || isAlternateBouncerShowing();
return (flags.isInitiatedByUser() || flags.dismissKeyguardRequested())
&& (mDeviceInteractive || flags.temporaryAndRenewable())
&& (isBouncerShowing || flags.dismissKeyguardRequested());
@@ -1170,8 +1179,8 @@
Assert.isMainThread();
String reason =
mKeyguardBypassController.canBypass() ? "bypass"
- : mAlternateBouncerShowing ? "alternateBouncer"
- : mPrimaryBouncerFullyShown ? "bouncer"
+ : isAlternateBouncerShowing() ? "alternateBouncer"
+ : isPrimaryBouncerFullyShown() ? "bouncer"
: "udfpsFpDown";
requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
@@ -2169,7 +2178,10 @@
Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
TaskStackChangeListeners taskStackChangeListeners,
SelectedUserInteractor selectedUserInteractor,
- IActivityTaskManager activityTaskManagerService) {
+ IActivityTaskManager activityTaskManagerService,
+ Provider<AlternateBouncerInteractor> alternateBouncerInteractor,
+ Provider<JavaAdapter> javaAdapter,
+ Provider<SceneInteractor> sceneInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2214,6 +2226,9 @@
mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
mIsSystemUser = mUserManager.isSystemUser();
+ mAlternateBouncerInteractor = alternateBouncerInteractor;
+ mJavaAdapter = javaAdapter;
+ mSceneInteractor = sceneInteractor;
mHandler = new Handler(mainLooper) {
@Override
@@ -2470,6 +2485,30 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TIME_12_24),
false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
+
+ if (SceneContainerFlag.isEnabled()) {
+ mJavaAdapter.get().alwaysCollectFlow(
+ mAlternateBouncerInteractor.get().isVisible(),
+ this::onAlternateBouncerVisibilityChange);
+ mJavaAdapter.get().alwaysCollectFlow(
+ mSceneInteractor.get().getTransitionState(),
+ this::onTransitionStateChanged
+ );
+ }
+ }
+
+ @VisibleForTesting
+ void onAlternateBouncerVisibilityChange(boolean isAlternateBouncerVisible) {
+ setAlternateBouncerShowing(isAlternateBouncerVisible);
+ }
+
+
+ @VisibleForTesting
+ void onTransitionStateChanged(ObservableTransitionState transitionState) {
+ int primaryBouncerFullyShown = isPrimaryBouncerFullyShown(transitionState) ? 1 : 0;
+ int primaryBouncerIsOrWillBeShowing =
+ isPrimaryBouncerShowingOrWillBeShowing(transitionState) ? 1 : 0;
+ handlePrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing, primaryBouncerFullyShown);
}
private void initializeSimState() {
@@ -2717,8 +2756,8 @@
requestActiveUnlock(
requestOrigin,
extraReason, canFaceBypass
- || mAlternateBouncerShowing
- || mPrimaryBouncerFullyShown
+ || isAlternateBouncerShowing()
+ || isPrimaryBouncerFullyShown()
|| mAuthController.isUdfpsFingerDown());
}
@@ -2739,7 +2778,7 @@
*/
public void setAlternateBouncerShowing(boolean showing) {
mAlternateBouncerShowing = showing;
- if (mAlternateBouncerShowing) {
+ if (isAlternateBouncerShowing()) {
requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
"alternateBouncer");
@@ -2747,6 +2786,45 @@
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
+ private boolean isAlternateBouncerShowing() {
+ if (SceneContainerFlag.isEnabled()) {
+ return mAlternateBouncerInteractor.get().isVisibleState();
+ } else {
+ return mAlternateBouncerShowing;
+ }
+ }
+
+ private boolean isPrimaryBouncerShowingOrWillBeShowing() {
+ if (SceneContainerFlag.isEnabled()) {
+ return isPrimaryBouncerShowingOrWillBeShowing(
+ mSceneInteractor.get().getTransitionState().getValue());
+ } else {
+ return mPrimaryBouncerIsOrWillBeShowing;
+ }
+ }
+
+ private boolean isPrimaryBouncerFullyShown() {
+ if (SceneContainerFlag.isEnabled()) {
+ return isPrimaryBouncerFullyShown(
+ mSceneInteractor.get().getTransitionState().getValue());
+ } else {
+ return mPrimaryBouncerFullyShown;
+ }
+ }
+
+ private boolean isPrimaryBouncerShowingOrWillBeShowing(
+ ObservableTransitionState transitionState
+ ) {
+ SceneContainerFlag.assertInNewMode();
+ return isPrimaryBouncerFullyShown(transitionState)
+ || transitionState.isTransitioning(null, Scenes.Bouncer);
+ }
+
+ private boolean isPrimaryBouncerFullyShown(ObservableTransitionState transitionState) {
+ SceneContainerFlag.assertInNewMode();
+ return transitionState.isIdle(Scenes.Bouncer);
+ }
+
/**
* If the current state of the device allows for triggering active unlock. This does not
* include active unlock availability.
@@ -2762,7 +2840,7 @@
private boolean shouldTriggerActiveUnlock(boolean shouldLog) {
// Triggers:
final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
- final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mAlternateBouncerShowing
+ final boolean awakeKeyguard = isPrimaryBouncerFullyShown() || isAlternateBouncerShowing()
|| (isKeyguardVisible() && !mGoingToSleep
&& mStatusBarState != StatusBarState.SHADE_LOCKED);
@@ -2830,14 +2908,14 @@
final boolean shouldListenKeyguardState =
isKeyguardVisible()
|| !mDeviceInteractive
- || (mPrimaryBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
+ || (isPrimaryBouncerShowingOrWillBeShowing() && !mKeyguardGoingAway)
|| mGoingToSleep
|| shouldListenForFingerprintAssistant
|| (mKeyguardOccluded && mIsDreaming)
|| (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing
&& (mOccludingAppRequestingFp
|| isUdfps
- || mAlternateBouncerShowing
+ || isAlternateBouncerShowing()
|| mAllowFingerprintOnCurrentOccludingActivity
)
);
@@ -2856,7 +2934,7 @@
&& !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
final boolean shouldListenBouncerState =
- !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
+ !strongerAuthRequired || !isPrimaryBouncerShowingOrWillBeShowing();
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
@@ -2872,10 +2950,10 @@
user,
shouldListen,
mAllowFingerprintOnCurrentOccludingActivity,
- mAlternateBouncerShowing,
+ isAlternateBouncerShowing(),
biometricEnabledForUser,
mBiometricPromptShowing,
- mPrimaryBouncerIsOrWillBeShowing,
+ isPrimaryBouncerShowingOrWillBeShowing(),
userCanSkipBouncer,
mCredentialAttempted,
mDeviceInteractive,
@@ -3614,6 +3692,7 @@
*/
public void sendPrimaryBouncerChanged(boolean primaryBouncerIsOrWillBeShowing,
boolean primaryBouncerFullyShown) {
+ SceneContainerFlag.assertInLegacyMode();
mLogger.logSendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
primaryBouncerFullyShown);
Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED);
@@ -4031,10 +4110,10 @@
if (isUdfpsSupported()) {
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
- pw.println(" mPrimaryBouncerIsOrWillBeShowing="
- + mPrimaryBouncerIsOrWillBeShowing);
+ pw.println(" isPrimaryBouncerShowingOrWillBeShowing="
+ + isPrimaryBouncerShowingOrWillBeShowing());
pw.println(" mStatusBarState=" + StatusBarState.toString(mStatusBarState));
- pw.println(" mAlternateBouncerShowing=" + mAlternateBouncerShowing);
+ pw.println(" isAlternateBouncerShowing=" + isAlternateBouncerShowing());
} else if (isSfpsSupported()) {
pw.println(" sfpsEnrolled=" + isSfpsEnrolled());
pw.println(" shouldListenForSfps=" + shouldListenForFingerprint(false));
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index baf8f5a..a301155 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,7 +49,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -140,7 +139,6 @@
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
@Inject Lazy<CommandQueue> mCommandQueue;
@Inject Lazy<UiEventLogger> mUiEventLogger;
- @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject Lazy<NotificationSectionsManager> mNotificationSectionsManagerLazy;
@Inject Lazy<ScreenOffAnimationController> mScreenOffAnimationController;
@@ -186,7 +184,6 @@
mProviders.put(CommandQueue.class, mCommandQueue::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
- mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
mProviders.put(NotificationSectionsManager.class, mNotificationSectionsManagerLazy::get);
mProviders.put(ScreenOffAnimationController.class, mScreenOffAnimationController::get);
mProviders.put(AmbientState.class, mAmbientStateLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index b7c02ea..6e01393 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -112,7 +112,11 @@
communalSceneInteractor.editModeState.value == EditModeState.STARTING ||
communalSceneInteractor.isLaunchingWidget.value
if (!delaySceneTransition) {
- communalSceneInteractor.changeScene(nextScene, nextTransition)
+ communalSceneInteractor.changeScene(
+ newScene = nextScene,
+ loggingReason = "KTF syncing",
+ transitionKey = nextTransition,
+ )
}
}
.launchIn(applicationScope)
@@ -176,7 +180,10 @@
if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
// If dreaming starts after timeout has expired, ex. if dream restarts under
// the hub, just close the hub immediately.
- communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ communalSceneInteractor.changeScene(
+ CommunalScenes.Blank,
+ "dream started after timeout",
+ )
}
}
}
@@ -201,7 +208,10 @@
bgScope.launch {
delay(screenTimeout.milliseconds)
if (isDreaming) {
- communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "hub timeout",
+ )
}
timeoutJob = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 99bcc12..7181b15 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -329,8 +329,11 @@
@Deprecated(
"Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
)
- fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) =
- communalSceneInteractor.changeScene(newScene, transitionKey)
+ fun changeScene(
+ newScene: SceneKey,
+ loggingReason: String,
+ transitionKey: TransitionKey? = null
+ ) = communalSceneInteractor.changeScene(newScene, loggingReason, transitionKey)
fun setEditModeOpen(isOpen: Boolean) {
_editModeOpen.value = isOpen
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index e45a695..a0b9966 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -22,6 +22,7 @@
import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.communal.data.repository.CommunalSceneRepository
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.shared.log.CommunalSceneLogger
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.shared.model.EditModeState
@@ -29,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.pairwiseBy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,8 +44,8 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
@@ -51,7 +53,8 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- private val communalSceneRepository: CommunalSceneRepository,
+ private val repository: CommunalSceneRepository,
+ private val logger: CommunalSceneLogger,
) {
private val _isLaunchingWidget = MutableStateFlow(false)
@@ -80,25 +83,39 @@
*/
fun changeScene(
newScene: SceneKey,
+ loggingReason: String,
transitionKey: TransitionKey? = null,
keyguardState: KeyguardState? = null,
) {
- applicationScope.launch {
+ applicationScope.launch("$TAG#changeScene") {
+ logger.logSceneChangeRequested(
+ from = currentScene.value,
+ to = newScene,
+ reason = loggingReason,
+ isInstant = false,
+ )
notifyListeners(newScene, keyguardState)
- communalSceneRepository.changeScene(newScene, transitionKey)
+ repository.changeScene(newScene, transitionKey)
}
}
/** Immediately snaps to the new scene. */
fun snapToScene(
newScene: SceneKey,
+ loggingReason: String,
delayMillis: Long = 0,
keyguardState: KeyguardState? = null
) {
applicationScope.launch("$TAG#snapToScene") {
delay(delayMillis)
+ logger.logSceneChangeRequested(
+ from = currentScene.value,
+ to = newScene,
+ reason = loggingReason,
+ isInstant = true,
+ )
notifyListeners(newScene, keyguardState)
- communalSceneRepository.snapToScene(newScene)
+ repository.snapToScene(newScene)
}
}
@@ -113,13 +130,30 @@
if (_editModeState.value == EditModeState.STARTING) {
return
}
- changeScene(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
+ changeScene(
+ CommunalScenes.Blank,
+ "activity start dismissing keyguard",
+ CommunalTransitionKeys.SimpleFade,
+ )
}
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
*/
- val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene
+ val currentScene: StateFlow<SceneKey> =
+ repository.currentScene
+ .pairwiseBy(initialValue = repository.currentScene.value) { from, to ->
+ logger.logSceneChangeCommitted(
+ from = from,
+ to = to,
+ )
+ to
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = repository.currentScene.value,
+ )
private val _editModeState = MutableStateFlow<EditModeState?>(null)
/**
@@ -134,7 +168,13 @@
/** Transition state of the hub mode. */
val transitionState: StateFlow<ObservableTransitionState> =
- communalSceneRepository.transitionState
+ repository.transitionState
+ .onEach { logger.logSceneTransition(it) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = repository.transitionState.value,
+ )
/**
* Updates the transition state of the hub [SceneTransitionLayout].
@@ -142,7 +182,7 @@
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
- communalSceneRepository.setTransitionState(transitionState)
+ repository.setTransitionState(transitionState)
}
/** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
new file mode 100644
index 0000000..aed9215
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.log
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+
+class CommunalSceneLogger @Inject constructor(@CommunalLog private val logBuffer: LogBuffer) {
+
+ fun logSceneChangeRequested(
+ from: SceneKey,
+ to: SceneKey,
+ reason: String,
+ isInstant: Boolean,
+ ) {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {
+ str1 = from.toString()
+ str2 = to.toString()
+ str3 = reason
+ bool1 = isInstant
+ },
+ messagePrinter = {
+ buildString {
+ append("Scene change requested: $str1 → $str2")
+ if (isInstant) {
+ append(" (instant)")
+ }
+ append(", reason: $str3")
+ }
+ },
+ )
+ }
+
+ fun logSceneChangeCommitted(
+ from: SceneKey,
+ to: SceneKey,
+ ) {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {
+ str1 = from.toString()
+ str2 = to.toString()
+ },
+ messagePrinter = { "Scene change committed: $str1 → $str2" },
+ )
+ }
+
+ fun logSceneTransition(transitionState: ObservableTransitionState) {
+ when (transitionState) {
+ is ObservableTransitionState.Transition -> {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {
+ str1 = transitionState.fromScene.toString()
+ str2 = transitionState.toScene.toString()
+ },
+ messagePrinter = { "Scene transition started: $str1 → $str2" },
+ )
+ }
+ is ObservableTransitionState.Idle -> {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = { str1 = transitionState.currentScene.toString() },
+ messagePrinter = { "Scene transition idle on: $str1" },
+ )
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "CommunalSceneLogger"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index d1a5a4b..b822133 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -106,10 +106,11 @@
*/
fun changeScene(
scene: SceneKey,
+ loggingReason: String,
transitionKey: TransitionKey? = null,
keyguardState: KeyguardState? = null
) {
- communalSceneInteractor.changeScene(scene, transitionKey, keyguardState)
+ communalSceneInteractor.changeScene(scene, loggingReason, transitionKey, keyguardState)
}
fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index bbd8596..6239373 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -67,7 +67,10 @@
* transition.
*/
fun snapToCommunal() {
- communalSceneInteractor.snapToScene(CommunalScenes.Communal)
+ communalSceneInteractor.snapToScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "transition view model",
+ )
}
// Show UMO on glanceable hub immediately on transition into glanceable hub
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
index 0844462..e7cedc6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
@@ -42,6 +42,7 @@
// TODO(b/330672236): move this to onTransitionAnimationEnd() without the delay.
communalSceneInteractor.snapToScene(
CommunalScenes.Blank,
+ "CommunalTransitionAnimatorController",
ActivityTransitionAnimator.TIMINGS.totalDuration
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 668fef6..6d7cdc4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -218,9 +218,10 @@
lifecycleScope.launch {
communalViewModel.canShowEditMode.collect {
communalViewModel.changeScene(
- CommunalScenes.Blank,
- CommunalTransitionKeys.ToEditMode,
- KeyguardState.GONE,
+ scene = CommunalScenes.Blank,
+ loggingReason = "edit mode opening",
+ transitionKey = CommunalTransitionKeys.ToEditMode,
+ keyguardState = KeyguardState.GONE,
)
// wait till transitioned to Blank scene, then animate in communal content in
// edit mode
@@ -252,8 +253,9 @@
communalViewModel.cleanupEditModeState()
communalViewModel.changeScene(
- CommunalScenes.Communal,
- CommunalTransitionKeys.FromEditMode
+ scene = CommunalScenes.Communal,
+ loggingReason = "edit mode closing",
+ transitionKey = CommunalTransitionKeys.FromEditMode
)
// Wait for the current scene to be idle on communal.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 4b9e5a0..0c1fb72 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -442,7 +442,9 @@
@Override
public void onWakeRequested() {
mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START);
- mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+ mCommunalInteractor.changeScene(CommunalScenes.Communal,
+ "dream wake requested",
+ null);
}
private Lifecycle.State getLifecycleStateLocked() {
@@ -493,7 +495,7 @@
mSystemDialogsCloser.closeSystemDialogs();
// Hide glanceable hub (this is a nop if glanceable hub is not open).
- mCommunalInteractor.changeScene(CommunalScenes.Blank, null);
+ mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 4b07f78..5c0335a6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -20,9 +20,9 @@
import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -51,6 +51,7 @@
fromGlanceableHubTransitionInteractor: GlanceableHubToDreamingTransitionViewModel,
toGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
private val toLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+ private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
private val communalInteractor: CommunalInteractor,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val userTracker: UserTracker,
@@ -61,11 +62,9 @@
val showGlanceableHub =
communalInteractor.isCommunalEnabled.value &&
!keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
- if (showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()) {
- communalInteractor.changeScene(CommunalScenes.Communal)
- } else {
- toLockscreenTransitionViewModel.startTransition()
- }
+ fromDreamingTransitionInteractor.startToLockscreenOrGlanceableHubTransition(
+ showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()
+ )
}
val dreamOverlayTranslationX: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt
deleted file mode 100644
index 3e382d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/view/KeyboardTouchpadTutorialActivity.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.inputdevice.tutorial.ui.view
-
-import android.os.Bundle
-import android.view.WindowManager
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.activity.viewModels
-import androidx.compose.runtime.Composable
-import com.android.compose.theme.PlatformTheme
-import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
-import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
-import java.util.Optional
-import javax.inject.Inject
-
-/**
- * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
- * either of them are actually not connected when this is launched
- */
-class KeyboardTouchpadTutorialActivity
-@Inject
-constructor(
- private val viewModelFactory: KeyboardTouchpadTutorialViewModel.Factory,
- private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
-) : ComponentActivity() {
-
- private val vm by
- viewModels<KeyboardTouchpadTutorialViewModel>(factoryProducer = { viewModelFactory })
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- enableEdgeToEdge()
- setContent {
- PlatformTheme {
- KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) { finish() }
- }
- }
- // required to handle 3+ fingers on touchpad
- window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
- }
-
- override fun onResume() {
- super.onResume()
- vm.onOpened()
- }
-
- override fun onPause() {
- super.onPause()
- vm.onClosed()
- }
-}
-
-@Composable
-fun KeyboardTouchpadTutorialContainer(
- vm: KeyboardTouchpadTutorialViewModel,
- touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
- closeTutorial: () -> Unit
-) {}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
deleted file mode 100644
index 39b1ec0..0000000
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.inputdevice.tutorial.ui.viewmodel
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
-import java.util.Optional
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-class KeyboardTouchpadTutorialViewModel(
- private val gesturesInteractor: Optional<TouchpadGesturesInteractor>
-) : ViewModel() {
-
- private val _screen = MutableStateFlow(Screen.BACK_GESTURE)
- val screen: StateFlow<Screen> = _screen
-
- fun goTo(screen: Screen) {
- _screen.value = screen
- }
-
- fun onOpened() {
- gesturesInteractor.ifPresent { it.disableGestures() }
- }
-
- fun onClosed() {
- gesturesInteractor.ifPresent { it.enableGestures() }
- }
-
- class Factory
- @Inject
- constructor(private val gesturesInteractor: Optional<TouchpadGesturesInteractor>) :
- ViewModelProvider.Factory {
-
- @Suppress("UNCHECKED_CAST")
- override fun <T : ViewModel> create(modelClass: Class<T>): T {
- return KeyboardTouchpadTutorialViewModel(gesturesInteractor) as T
- }
- }
-}
-
-enum class Screen {
- BACK_GESTURE,
- HOME_GESTURE,
- ACTION_KEY
-}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialModule.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/KeyboardTouchpadTutorialModule.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialModule.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/TouchpadTutorialScreensProvider.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/TouchpadTutorialScreensProvider.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.kt
new file mode 100644
index 0000000..3f1f68a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+@SysUISingleton
+class KeyboardTouchpadConnectionInteractor
+@Inject
+constructor(
+ keyboardRepository: KeyboardRepository,
+ touchpadRepository: TouchpadRepository,
+) {
+
+ val connectionState: Flow<ConnectionState> =
+ combine(
+ keyboardRepository.isAnyKeyboardConnected,
+ touchpadRepository.isAnyTouchpadConnected
+ ) { keyboardConnected, touchpadConnected ->
+ ConnectionState(keyboardConnected, touchpadConnected)
+ }
+}
+
+data class ConnectionState(val keyboardConnected: Boolean, val touchpadConnected: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionKeyTutorialScreen.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/ActionTutorialContent.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialComponents.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/inputdevice/oobe/ui/composable/TutorialScreenConfig.kt
rename to packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
new file mode 100644
index 0000000..34ecc95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.view
+
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.Lifecycle.State.STARTED
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel.Factory.ViewModelFactoryAssistedProvider
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.launch
+
+/**
+ * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
+ * either of them are actually not connected when this is launched
+ */
+class KeyboardTouchpadTutorialActivity
+@Inject
+constructor(
+ private val viewModelFactoryAssistedProvider: ViewModelFactoryAssistedProvider,
+ private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
+) : ComponentActivity() {
+
+ companion object {
+ const val INTENT_TUTORIAL_TYPE_KEY = "tutorial_type"
+ const val INTENT_TUTORIAL_TYPE_TOUCHPAD = "touchpad"
+ const val INTENT_TUTORIAL_TYPE_KEYBOARD = "keyboard"
+ }
+
+ private val vm by
+ viewModels<KeyboardTouchpadTutorialViewModel>(
+ factoryProducer = {
+ viewModelFactoryAssistedProvider.create(touchpadTutorialScreensProvider.isPresent)
+ }
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ // required to handle 3+ fingers on touchpad
+ window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+ lifecycle.addObserver(vm)
+ lifecycleScope.launch {
+ vm.closeActivity.collect { finish ->
+ if (finish) {
+ finish()
+ }
+ }
+ }
+ setContent {
+ PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) }
+ }
+ }
+}
+
+@Composable
+fun KeyboardTouchpadTutorialContainer(
+ vm: KeyboardTouchpadTutorialViewModel,
+ touchpadScreens: Optional<TouchpadTutorialScreensProvider>,
+) {
+ val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
+ when (activeScreen) {
+ BACK_GESTURE ->
+ touchpadScreens
+ .get()
+ .BackGesture(onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack)
+ HOME_GESTURE ->
+ touchpadScreens
+ .get()
+ .HomeGesture(onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack)
+ ACTION_KEY ->
+ ActionKeyTutorialScreen(
+ onDoneButtonClicked = vm::onDoneButtonClicked,
+ onBack = vm::onBack
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
new file mode 100644
index 0000000..315c102
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.viewmodel
+
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.systemui.inputdevice.tutorial.domain.interactor.ConnectionState
+import com.android.systemui.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.Optional
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.launch
+
+class KeyboardTouchpadTutorialViewModel(
+ private val gesturesInteractor: Optional<TouchpadGesturesInteractor>,
+ private val keyboardTouchpadConnectionInteractor: KeyboardTouchpadConnectionInteractor,
+ private val hasTouchpadTutorialScreens: Boolean,
+ handle: SavedStateHandle
+) : ViewModel(), DefaultLifecycleObserver {
+
+ private fun startingScreen(handle: SavedStateHandle): Screen {
+ val tutorialType: String? = handle[INTENT_TUTORIAL_TYPE_KEY]
+ return if (tutorialType == INTENT_TUTORIAL_TYPE_KEYBOARD) ACTION_KEY else BACK_GESTURE
+ }
+
+ private val _screen = MutableStateFlow(startingScreen(handle))
+ val screen: Flow<Screen> = _screen.filter { it.canBeShown() }
+
+ private val _closeActivity: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ val closeActivity: StateFlow<Boolean> = _closeActivity
+
+ private val screensBackStack = ArrayDeque(listOf(_screen.value))
+
+ private var connectionState: ConnectionState =
+ ConnectionState(keyboardConnected = false, touchpadConnected = false)
+
+ init {
+ viewModelScope.launch {
+ keyboardTouchpadConnectionInteractor.connectionState.collect { connectionState = it }
+ }
+
+ viewModelScope.launch {
+ screen
+ .runningFold<Screen, Pair<Screen?, Screen?>>(null to null) {
+ previousScreensPair,
+ currentScreen ->
+ previousScreensPair.second to currentScreen
+ }
+ .collect { (previousScreen, currentScreen) ->
+ // ignore first empty emission
+ if (currentScreen != null) {
+ setupDeviceState(previousScreen, currentScreen)
+ }
+ }
+ }
+
+ viewModelScope.launch {
+ // close activity if screen requires touchpad but we don't have it. This can only happen
+ // when current sysui build doesn't contain touchpad module dependency
+ _screen.filterNot { it.canBeShown() }.collect { _closeActivity.value = true }
+ }
+ }
+
+ override fun onCleared() {
+ // this shouldn't be needed as onTutorialInvisible should already clear device state but
+ // it'd be really bad if we'd block gestures/shortcuts after leaving tutorial so just to be
+ // extra sure...
+ clearDeviceStateForScreen(_screen.value)
+ }
+
+ override fun onStart(owner: LifecycleOwner) {
+ setupDeviceState(previousScreen = null, currentScreen = _screen.value)
+ }
+
+ override fun onStop(owner: LifecycleOwner) {
+ clearDeviceStateForScreen(_screen.value)
+ }
+
+ fun onDoneButtonClicked() {
+ var nextScreen = _screen.value.next()
+ while (nextScreen != null) {
+ if (requiredHardwarePresent(nextScreen)) {
+ break
+ }
+ nextScreen = nextScreen.next()
+ }
+ if (nextScreen == null) {
+ _closeActivity.value = true
+ } else {
+ _screen.value = nextScreen
+ screensBackStack.add(nextScreen)
+ }
+ }
+
+ private fun Screen.canBeShown() = requiredHardware != TOUCHPAD || hasTouchpadTutorialScreens
+
+ private fun setupDeviceState(previousScreen: Screen?, currentScreen: Screen) {
+ if (previousScreen?.requiredHardware == currentScreen.requiredHardware) return
+ previousScreen?.let { clearDeviceStateForScreen(it) }
+ when (currentScreen.requiredHardware) {
+ TOUCHPAD -> gesturesInteractor.get().disableGestures()
+ KEYBOARD -> {} // TODO(b/358587037) disabled keyboard shortcuts
+ }
+ }
+
+ private fun clearDeviceStateForScreen(screen: Screen) {
+ when (screen.requiredHardware) {
+ TOUCHPAD -> gesturesInteractor.get().enableGestures()
+ KEYBOARD -> {} // TODO(b/358587037) enable keyboard shortcuts
+ }
+ }
+
+ private fun requiredHardwarePresent(screen: Screen): Boolean =
+ when (screen.requiredHardware) {
+ KEYBOARD -> connectionState.keyboardConnected
+ TOUCHPAD -> connectionState.touchpadConnected
+ }
+
+ fun onBack() {
+ if (screensBackStack.size <= 1) {
+ _closeActivity.value = true
+ } else {
+ screensBackStack.removeLast()
+ _screen.value = screensBackStack.last()
+ }
+ }
+
+ class Factory
+ @AssistedInject
+ constructor(
+ private val gesturesInteractor: Optional<TouchpadGesturesInteractor>,
+ private val keyboardTouchpadConnected: KeyboardTouchpadConnectionInteractor,
+ @Assisted private val hasTouchpadTutorialScreens: Boolean,
+ ) : AbstractSavedStateViewModelFactory() {
+
+ @AssistedFactory
+ fun interface ViewModelFactoryAssistedProvider {
+ fun create(@Assisted hasTouchpadTutorialScreens: Boolean): Factory
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ key: String,
+ modelClass: Class<T>,
+ handle: SavedStateHandle
+ ): T =
+ KeyboardTouchpadTutorialViewModel(
+ gesturesInteractor,
+ keyboardTouchpadConnected,
+ hasTouchpadTutorialScreens,
+ handle
+ )
+ as T
+ }
+}
+
+enum class RequiredHardware {
+ TOUCHPAD,
+ KEYBOARD
+}
+
+enum class Screen(val requiredHardware: RequiredHardware) {
+ BACK_GESTURE(requiredHardware = TOUCHPAD),
+ HOME_GESTURE(requiredHardware = TOUCHPAD),
+ ACTION_KEY(requiredHardware = KEYBOARD);
+
+ fun next(): Screen? =
+ when (this) {
+ BACK_GESTURE -> HOME_GESTURE
+ HOME_GESTURE -> ACTION_KEY
+ ACTION_KEY -> null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 0c12f8c..90aaf0d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -273,9 +273,10 @@
private suspend fun transitionToGlanceableHub() {
if (communalSceneKtfRefactor()) {
communalSceneInteractor.changeScene(
- CommunalScenes.Communal,
+ newScene = CommunalScenes.Communal,
+ loggingReason = "from dozing to hub",
// Immediately show the hub when transitioning from dozing to hub.
- CommunalTransitionKeys.Immediately,
+ transitionKey = CommunalTransitionKeys.Immediately,
)
} else {
startTransitionTo(KeyguardState.GLANCEABLE_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 7bf9c2f1..4666430 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -20,7 +20,9 @@
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -58,6 +60,7 @@
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
+ private val communalSceneInteractor: CommunalSceneInteractor,
private val communalSettingsInteractor: CommunalSettingsInteractor,
powerInteractor: PowerInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -126,17 +129,24 @@
}
}
- fun startToLockscreenTransition() {
+ fun startToLockscreenOrGlanceableHubTransition(openHub: Boolean) {
scope.launch {
if (
transitionInteractor.startedKeyguardState.replayCache.last() ==
KeyguardState.DREAMING
) {
if (powerInteractor.detailedWakefulness.value.isAwake()) {
- startTransitionTo(
- KeyguardState.LOCKSCREEN,
- ownerReason = "Dream has ended and device is awake"
- )
+ if (openHub) {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "FromDreamingTransitionInteractor",
+ )
+ } else {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason = "Dream has ended and device is awake"
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index befcc9e..c9db26d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -159,6 +159,7 @@
if (communalSceneKtfRefactor()) {
communalSceneInteractor.changeScene(
newScene = CommunalScenes.Blank,
+ loggingReason = "hub to dozing",
transitionKey = CommunalTransitionKeys.Immediately,
keyguardState = KeyguardState.DOZING,
)
@@ -182,6 +183,7 @@
if (communalSceneKtfRefactor()) {
communalSceneInteractor.changeScene(
newScene = CommunalScenes.Blank,
+ loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
transitionKey = CommunalTransitionKeys.SimpleFade,
keyguardState = state,
)
@@ -211,6 +213,7 @@
.collect { _ ->
communalSceneInteractor.changeScene(
newScene = CommunalScenes.Blank,
+ loggingReason = "hub to occluded",
transitionKey = CommunalTransitionKeys.SimpleFade,
keyguardState = KeyguardState.OCCLUDED,
)
@@ -254,6 +257,7 @@
} else {
communalSceneInteractor.changeScene(
newScene = CommunalScenes.Blank,
+ loggingReason = "hub to gone",
transitionKey = CommunalTransitionKeys.SimpleFade,
keyguardState = KeyguardState.GONE
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 905ca8e..7b6949f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -146,8 +146,9 @@
if (SceneContainerFlag.isEnabled) return
if (communalSceneKtfRefactor()) {
communalSceneInteractor.changeScene(
- CommunalScenes.Communal,
- CommunalTransitionKeys.SimpleFade
+ newScene = CommunalScenes.Communal,
+ loggingReason = "occluded to hub",
+ transitionKey = CommunalTransitionKeys.SimpleFade
)
} else {
startTransitionTo(KeyguardState.GLANCEABLE_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 2823b93..0118f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -175,7 +175,10 @@
!communalSceneInteractor.isLaunchingWidget.value &&
communalSceneInteractor.editModeState.value == null
) {
- communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+ communalSceneInteractor.snapToScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "FromPrimaryBouncerTransitionInteractor",
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
index 34c1436..38f5d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
@@ -50,5 +50,9 @@
}
.distinctUntilChanged()
- fun openCommunalHub() = communalInteractor.changeScene(CommunalScenes.Communal)
+ fun openCommunalHub() =
+ communalInteractor.changeScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "accessibility",
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index b5ec7a6..10605b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,6 @@
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -39,10 +38,8 @@
class DreamingToLockscreenTransitionViewModel
@Inject
constructor(
- private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
- fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
private val transitionAnimation =
animationFlow.setup(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
index 9fa6769..bb238f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -19,6 +19,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.fragments.FragmentService
+import com.android.systemui.qs.composefragment.QSFragmentCompose
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -31,13 +32,18 @@
@Inject
constructor(
private val fragmentService: FragmentService,
- private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
+ private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>,
+ private val qsFragmentComposeProvider: Provider<QSFragmentCompose>,
) : CoreStartable {
override fun start() {
fragmentService.addFragmentInstantiationProvider(
QSFragmentLegacy::class.java,
qsFragmentLegacyProvider
)
+ fragmentService.addFragmentInstantiationProvider(
+ QSFragmentCompose::class.java,
+ qsFragmentComposeProvider
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.kt
new file mode 100644
index 0000000..1891c41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** Events of user interactions with modes from the QS Modes dialog. {@see ModesDialogViewModel} */
+enum class QSModesEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "User turned manual Do Not Disturb on via modes dialog") QS_MODES_DND_ON(1870),
+ @UiEvent(doc = "User turned manual Do Not Disturb off via modes dialog") QS_MODES_DND_OFF(1871),
+ @UiEvent(doc = "User opened mode settings from the Do Not Disturb tile in the modes dialog")
+ QS_MODES_DND_SETTINGS(1872),
+ @UiEvent(doc = "User turned automatic mode on via modes dialog") QS_MODES_MODE_ON(1873),
+ @UiEvent(doc = "User turned automatic mode off via modes dialog") QS_MODES_MODE_OFF(1874),
+ @UiEvent(doc = "User opened mode settings from a mode tile in the modes dialog")
+ QS_MODES_MODE_SETTINGS(1875),
+ @UiEvent(doc = "User clicked on Settings from the modes dialog") QS_MODES_SETTINGS(1876),
+ @UiEvent(doc = "User clicked on Do Not Disturb tile, opening the time selection dialog")
+ QS_MODES_DURATION_DIALOG(1879);
+
+ override fun getId() = _id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
new file mode 100644
index 0000000..5d81d4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment
+
+import android.annotation.SuppressLint
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.round
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.modifiers.height
+import com.android.compose.modifiers.padding
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.panels.ui.compose.QuickQuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettingsTheme
+import com.android.systemui.qs.ui.composable.ShadeBody
+import com.android.systemui.res.R
+import com.android.systemui.util.LifecycleFragment
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+@SuppressLint("ValidFragment")
+class QSFragmentCompose
+@Inject
+constructor(
+ private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory,
+) : LifecycleFragment(), QS {
+
+ private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null)
+ private val heightListener = MutableStateFlow<QS.HeightListener?>(null)
+ private val qsContainerController = MutableStateFlow<QSContainerController?>(null)
+
+ private lateinit var viewModel: QSFragmentComposeViewModel
+
+ // Starting with a non-zero value makes it so that it has a non-zero height on first expansion
+ // This is important for `QuickSettingsControllerImpl.mMinExpansionHeight` to detect a "change".
+ private val qqsHeight = MutableStateFlow(1)
+ private val qsHeight = MutableStateFlow(0)
+ private val qqsVisible = MutableStateFlow(false)
+ private val qqsPositionOnRoot = Rect()
+ private val composeViewPositionOnScreen = Rect()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ QSComposeFragment.isUnexpectedlyInLegacyMode()
+ viewModel = qsFragmentComposeViewModelFactory.create(lifecycleScope)
+
+ setListenerCollections()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val context = inflater.context
+ return ComposeView(context).apply {
+ setBackPressedDispatcher()
+ setContent {
+ PlatformTheme {
+ val visible by viewModel.qsVisible.collectAsStateWithLifecycle()
+ val qsState by viewModel.expansionState.collectAsStateWithLifecycle()
+
+ AnimatedVisibility(
+ visible = visible,
+ modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars)
+ ) {
+ AnimatedContent(targetState = qsState) {
+ when (it) {
+ QSFragmentComposeViewModel.QSExpansionState.QQS -> {
+ QuickQuickSettingsElement()
+ }
+ QSFragmentComposeViewModel.QSExpansionState.QS -> {
+ QuickSettingsElement()
+ }
+ else -> {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun setPanelView(notificationPanelView: QS.HeightListener?) {
+ heightListener.value = notificationPanelView
+ }
+
+ override fun hideImmediately() {
+ // view?.animate()?.cancel()
+ // view?.y = -qsMinExpansionHeight.toFloat()
+ }
+
+ override fun getQsMinExpansionHeight(): Int {
+ // TODO (b/353253277) implement split screen
+ return qqsHeight.value
+ }
+
+ override fun getDesiredHeight(): Int {
+ /*
+ * Looking at the code, it seems that
+ * * If customizing, then the height is that of the view post-layout, which is set by
+ * QSContainerImpl.calculateContainerHeight, which is the height the customizer takes
+ * * If not customizing, it's the measured height. So we may want to surface that.
+ */
+ return view?.height ?: 0
+ }
+
+ override fun setHeightOverride(desiredHeight: Int) {
+ viewModel.heightOverrideValue = desiredHeight
+ }
+
+ override fun setHeaderClickable(qsExpansionEnabled: Boolean) {
+ // Empty method
+ }
+
+ override fun isCustomizing(): Boolean {
+ return viewModel.containerViewModel.editModeViewModel.isEditing.value
+ }
+
+ override fun closeCustomizer() {
+ viewModel.containerViewModel.editModeViewModel.stopEditing()
+ }
+
+ override fun setOverscrolling(overscrolling: Boolean) {
+ viewModel.stackScrollerOverscrollingValue = overscrolling
+ }
+
+ override fun setExpanded(qsExpanded: Boolean) {
+ viewModel.isQSExpanded = qsExpanded
+ }
+
+ override fun setListening(listening: Boolean) {
+ // Not needed, views start listening and collection when composed
+ }
+
+ override fun setQsVisible(qsVisible: Boolean) {
+ viewModel.isQSVisible = qsVisible
+ }
+
+ override fun isShowingDetail(): Boolean {
+ return isCustomizing
+ }
+
+ override fun closeDetail() {
+ closeCustomizer()
+ }
+
+ override fun animateHeaderSlidingOut() {
+ // TODO(b/353254353)
+ }
+
+ override fun setQsExpansion(
+ qsExpansionFraction: Float,
+ panelExpansionFraction: Float,
+ headerTranslation: Float,
+ squishinessFraction: Float
+ ) {
+ viewModel.qsExpansionValue = qsExpansionFraction
+ viewModel.panelExpansionFractionValue = panelExpansionFraction
+ viewModel.squishinessFractionValue = squishinessFraction
+
+ // TODO(b/353254353) Handle header translation
+ }
+
+ override fun setHeaderListening(listening: Boolean) {
+ // Not needed, header will start listening as soon as it's composed
+ }
+
+ override fun notifyCustomizeChanged() {
+ // Not needed, only called from inside customizer
+ }
+
+ override fun setContainerController(controller: QSContainerController?) {
+ qsContainerController.value = controller
+ }
+
+ override fun setCollapseExpandAction(action: Runnable?) {
+ // Nothing to do yet. But this should be wired to a11y
+ }
+
+ override fun getHeightDiff(): Int {
+ return 0 // For now TODO(b/353254353)
+ }
+
+ override fun getHeader(): View? {
+ QSComposeFragment.isUnexpectedlyInLegacyMode()
+ return null
+ }
+
+ override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) {
+ super.setShouldUpdateSquishinessOnMedia(shouldUpdate)
+ // TODO (b/353253280)
+ }
+
+ override fun setInSplitShade(shouldTranslate: Boolean) {
+ // TODO (b/356435605)
+ }
+
+ override fun setTransitionToFullShadeProgress(
+ isTransitioningToFullShade: Boolean,
+ qsTransitionFraction: Float,
+ qsSquishinessFraction: Float
+ ) {
+ super.setTransitionToFullShadeProgress(
+ isTransitioningToFullShade,
+ qsTransitionFraction,
+ qsSquishinessFraction
+ )
+ }
+
+ override fun setFancyClipping(
+ leftInset: Int,
+ top: Int,
+ rightInset: Int,
+ bottom: Int,
+ cornerRadius: Int,
+ visible: Boolean,
+ fullWidth: Boolean
+ ) {}
+
+ override fun isFullyCollapsed(): Boolean {
+ return !viewModel.isQSVisible
+ }
+
+ override fun setCollapsedMediaVisibilityChangedListener(listener: Consumer<Boolean>?) {
+ // TODO (b/353253280)
+ }
+
+ override fun setScrollListener(scrollListener: QS.ScrollListener?) {
+ this.scrollListener.value = scrollListener
+ }
+
+ override fun setOverScrollAmount(overScrollAmount: Int) {
+ super.setOverScrollAmount(overScrollAmount)
+ }
+
+ override fun setIsNotificationPanelFullWidth(isFullWidth: Boolean) {
+ viewModel.isSmallScreenValue = isFullWidth
+ }
+
+ override fun getHeaderTop(): Int {
+ return viewModel.qqsHeaderHeight.value
+ }
+
+ override fun getHeaderBottom(): Int {
+ return headerTop + qqsHeight.value
+ }
+
+ override fun getHeaderLeft(): Int {
+ return qqsPositionOnRoot.left
+ }
+
+ override fun getHeaderBoundsOnScreen(outBounds: Rect) {
+ outBounds.set(qqsPositionOnRoot)
+ view?.getBoundsOnScreen(composeViewPositionOnScreen)
+ ?: run { composeViewPositionOnScreen.setEmpty() }
+ qqsPositionOnRoot.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top)
+ }
+
+ override fun isHeaderShown(): Boolean {
+ return qqsVisible.value
+ }
+
+ private fun setListenerCollections() {
+ lifecycleScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ // TODO
+ // setListenerJob(
+ // scrollListener,
+ //
+ // )
+ }
+ launch {
+ setListenerJob(
+ heightListener,
+ viewModel.containerViewModel.editModeViewModel.isEditing
+ ) {
+ onQsHeightChanged()
+ }
+ }
+ launch {
+ setListenerJob(
+ qsContainerController,
+ viewModel.containerViewModel.editModeViewModel.isEditing
+ ) {
+ setCustomizerShowing(it)
+ }
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun QuickQuickSettingsElement() {
+ val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+ DisposableEffect(Unit) {
+ qqsVisible.value = true
+
+ onDispose { qqsVisible.value = false }
+ }
+ Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
+ QuickQuickSettings(
+ viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
+ modifier =
+ Modifier.onGloballyPositioned { coordinates ->
+ val (leftFromRoot, topFromRoot) = coordinates.positionInRoot().round()
+ val (width, height) = coordinates.size
+ qqsPositionOnRoot.set(
+ leftFromRoot,
+ topFromRoot,
+ leftFromRoot + width,
+ topFromRoot + height
+ )
+ }
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ qqsHeight.value = placeable.height
+
+ layout(placeable.width, placeable.height) { placeable.place(0, 0) }
+ }
+ .padding(top = { qqsPadding })
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ }
+
+ @Composable
+ private fun QuickSettingsElement() {
+ val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+ val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
+ Column {
+ Box(modifier = Modifier.fillMaxSize().weight(1f)) {
+ Column {
+ Spacer(modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() })
+ ShadeBody(viewModel = viewModel.containerViewModel)
+ }
+ }
+ QuickSettingsTheme {
+ FooterActions(
+ viewModel = viewModel.footerActionsViewModel,
+ qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+ modifier = Modifier.sysuiResTag("qs_footer_actions")
+ )
+ }
+ }
+ }
+}
+
+private fun View.setBackPressedDispatcher() {
+ repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(it.viewRootImpl.onBackInvokedDispatcher)
+ }
+
+ override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle
+ }
+ )
+ }
+ }
+}
+
+private suspend inline fun <Listener : Any, Data> setListenerJob(
+ listenerFlow: MutableStateFlow<Listener?>,
+ dataFlow: Flow<Data>,
+ crossinline onCollect: suspend Listener.(Data) -> Unit
+) {
+ coroutineScope {
+ try {
+ listenerFlow.collectLatest { listenerOrNull ->
+ listenerOrNull?.let { currentListener ->
+ launch {
+ // Called when editing mode changes
+ dataFlow.collect { currentListener.onCollect(it) }
+ }
+ }
+ }
+ awaitCancellation()
+ } finally {
+ listenerFlow.value = null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
new file mode 100644
index 0000000..9e109e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment.viewmodel
+
+import android.content.res.Resources
+import android.graphics.Rect
+import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.util.LargeScreenUtils
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class QSFragmentComposeViewModel
+@AssistedInject
+constructor(
+ val containerViewModel: QuickSettingsContainerViewModel,
+ @Main private val resources: Resources,
+ private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
+ private val footerActionsController: FooterActionsController,
+ private val sysuiStatusBarStateController: SysuiStatusBarStateController,
+ private val keyguardBypassController: KeyguardBypassController,
+ private val disableFlagsRepository: DisableFlagsRepository,
+ private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
+ private val configurationInteractor: ConfigurationInteractor,
+ private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
+ @Assisted private val lifecycleScope: LifecycleCoroutineScope,
+) {
+ val footerActionsViewModel =
+ footerActionsViewModelFactory.create(lifecycleScope).also {
+ lifecycleScope.launch { footerActionsController.init() }
+ }
+
+ private val _qsBounds = MutableStateFlow(Rect())
+
+ private val _qsExpanded = MutableStateFlow(false)
+ var isQSExpanded: Boolean
+ get() = _qsExpanded.value
+ set(value) {
+ _qsExpanded.value = value
+ }
+
+ private val _qsVisible = MutableStateFlow(false)
+ val qsVisible = _qsVisible.asStateFlow()
+ var isQSVisible: Boolean
+ get() = qsVisible.value
+ set(value) {
+ _qsVisible.value = value
+ }
+
+ private val _qsExpansion = MutableStateFlow(0f)
+ var qsExpansionValue: Float
+ get() = _qsExpansion.value
+ set(value) {
+ _qsExpansion.value = value
+ }
+
+ private val _panelFraction = MutableStateFlow(0f)
+ var panelExpansionFractionValue: Float
+ get() = _panelFraction.value
+ set(value) {
+ _panelFraction.value = value
+ }
+
+ private val _squishinessFraction = MutableStateFlow(0f)
+ var squishinessFractionValue: Float
+ get() = _squishinessFraction.value
+ set(value) {
+ _squishinessFraction.value = value
+ }
+
+ val qqsHeaderHeight =
+ configurationInteractor.onAnyConfigurationChange
+ .map {
+ if (LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)) {
+ 0
+ } else {
+ largeScreenHeaderHelper.getLargeScreenHeaderHeight()
+ }
+ }
+ .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), 0)
+
+ private val _headerAnimating = MutableStateFlow(false)
+
+ private val _stackScrollerOverscrolling = MutableStateFlow(false)
+ var stackScrollerOverscrollingValue: Boolean
+ get() = _stackScrollerOverscrolling.value
+ set(value) {
+ _stackScrollerOverscrolling.value = value
+ }
+
+ private val qsDisabled =
+ disableFlagsRepository.disableFlags
+ .map { !it.isQuickSettingsEnabled() }
+ .stateIn(
+ lifecycleScope,
+ SharingStarted.WhileSubscribed(),
+ !disableFlagsRepository.disableFlags.value.isQuickSettingsEnabled()
+ )
+
+ private val _showCollapsedOnKeyguard = MutableStateFlow(false)
+
+ private val _keyguardAndExpanded = MutableStateFlow(false)
+
+ private val _statusBarState = MutableStateFlow(-1)
+
+ private val _viewHeight = MutableStateFlow(0)
+
+ private val _headerTranslation = MutableStateFlow(0f)
+
+ private val _inSplitShade = MutableStateFlow(false)
+
+ private val _transitioningToFullShade = MutableStateFlow(false)
+
+ private val _lockscreenToShadeProgress = MutableStateFlow(false)
+
+ private val _overscrolling = MutableStateFlow(false)
+
+ private val _isSmallScreen = MutableStateFlow(false)
+ var isSmallScreenValue: Boolean
+ get() = _isSmallScreen.value
+ set(value) {
+ _isSmallScreen.value = value
+ }
+
+ private val _shouldUpdateMediaSquishiness = MutableStateFlow(false)
+
+ private val _heightOverride = MutableStateFlow(-1)
+ val heightOverride = _heightOverride.asStateFlow()
+ var heightOverrideValue: Int
+ get() = heightOverride.value
+ set(value) {
+ _heightOverride.value = value
+ }
+
+ val expansionState: StateFlow<QSExpansionState> =
+ combine(
+ _stackScrollerOverscrolling,
+ _qsExpanded,
+ _qsExpansion,
+ ) { args: Array<Any> ->
+ val expansion = args[2] as Float
+ if (expansion > 0.5f) {
+ QSExpansionState.QS
+ } else {
+ QSExpansionState.QQS
+ }
+ }
+ .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), QSExpansionState.QQS)
+
+ @AssistedFactory
+ interface Factory {
+ fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel
+ }
+
+ sealed interface QSExpansionState {
+ data object QQS : QSExpansionState
+
+ data object QS : QSExpansionState
+
+ @JvmInline value class Expanding(val progress: Float) : QSExpansionState
+
+ @JvmInline value class Collapsing(val progress: Float) : QSExpansionState
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index ba45d17..6dc101a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -21,6 +21,7 @@
import android.view.ContextThemeWrapper
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
@@ -41,6 +42,7 @@
import javax.inject.Named
import javax.inject.Provider
import kotlin.math.max
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -48,6 +50,8 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
private const val TAG = "FooterActionsViewModel"
@@ -140,6 +144,30 @@
showPowerButton,
)
}
+
+ fun create(lifecycleCoroutineScope: LifecycleCoroutineScope): FooterActionsViewModel {
+ val globalActionsDialogLite = globalActionsDialogLiteProvider.get()
+ if (lifecycleCoroutineScope.isActive) {
+ lifecycleCoroutineScope.launch {
+ try {
+ awaitCancellation()
+ } finally {
+ globalActionsDialogLite.destroy()
+ }
+ }
+ } else {
+ globalActionsDialogLite.destroy()
+ }
+
+ return FooterActionsViewModel(
+ context,
+ footerActionsInteractor,
+ falsingManager,
+ globalActionsDialogLite,
+ activityStarter,
+ showPowerButton,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 2ee957e..08a56bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -39,6 +39,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
@@ -77,7 +78,7 @@
Column {
HorizontalPager(
state = pagerState,
- modifier = Modifier,
+ modifier = Modifier.sysuiResTag("qs_pager"),
pageSpacing = if (pages.size > 1) InterPageSpacing else 0.dp,
beyondViewportPageCount = 1,
verticalAlignment = Alignment.Top,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index af3803b..a9027ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.res.R
@@ -44,7 +45,10 @@
}
val columns by viewModel.columns.collectAsStateWithLifecycle()
- TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
+ TileLazyGrid(
+ modifier = modifier.sysuiResTag("qqs_tile_layout"),
+ columns = GridCells.Fixed(columns)
+ ) {
items(
tiles.size,
key = { index -> sizedTiles[index].tile.spec.spec },
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 7e6ccd6..9c0701e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -22,7 +22,6 @@
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
import android.text.TextUtils
-import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
@@ -593,15 +592,15 @@
}
@Composable
-private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon {
+private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon {
val context = LocalContext.current
- return icon.get().let {
+ return icon.get()?.let {
if (it is QSTileImpl.ResourceIcon) {
Icon.Resource(it.resId, null)
} else {
Icon.Loaded(it.getDrawable(context), null)
}
- }
+ } ?: Icon.Resource(R.drawable.ic_error_outline, null)
}
@OptIn(ExperimentalAnimationGraphicsApi::class)
@@ -618,7 +617,7 @@
remember(icon, context) {
when (icon) {
is Icon.Loaded -> icon.drawable
- is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
+ is Icon.Resource -> context.getDrawable(icon.res)
}
}
if (loadedDrawable !is Animatable) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 4ec59c9..c83e3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -25,7 +25,7 @@
val label: String,
val secondaryLabel: String,
val state: Int,
- val icon: Supplier<QSTile.Icon>,
+ val icon: Supplier<QSTile.Icon?>,
)
fun QSTile.State.toUiState(): TileUiState {
@@ -33,6 +33,6 @@
label?.toString() ?: "",
secondaryLabel?.toString() ?: "",
state,
- icon?.let { Supplier { icon } } ?: iconSupplier,
+ icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 2b7df7d..67c53d46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -142,14 +142,15 @@
}
override fun onIntentStarted(willAnimate: Boolean) {
+ val reason = "onIntentStarted(willAnimate=$willAnimate)"
if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
- Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
+ Log.d(TAG, reason)
}
notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
if (!willAnimate) {
- removeHun(animate = true)
+ removeHun(animate = true, reason)
onFinishAnimationCallback?.run()
}
}
@@ -166,13 +167,18 @@
}
}
- private fun removeHun(animate: Boolean) {
+ private fun removeHun(animate: Boolean, reason: String) {
val row = headsUpNotificationRow ?: return
// TODO: b/297247841 - Call on the row we're removing, which may differ from notification.
HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
- headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
+ headsUpManager.removeNotification(
+ row.entry.key,
+ true /* releaseImmediately */,
+ animate,
+ reason
+ )
}
override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
@@ -184,7 +190,7 @@
// here?
notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false)
notificationEntry.isExpandAnimationRunning = false
- removeHun(animate = true)
+ removeHun(animate = true, "onLaunchAnimationCancelled()")
onFinishAnimationCallback?.run()
}
@@ -206,7 +212,7 @@
notificationEntry.isExpandAnimationRunning = false
notificationListContainer.setExpandingNotification(null)
applyParams(null)
- removeHun(animate = false)
+ removeHun(animate = false, "onLaunchAnimationEnd()")
onFinishAnimationCallback?.run()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index e50d64b..ec8566b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -496,7 +496,11 @@
if (posted?.shouldHeadsUpEver == false) {
if (posted.isHeadsUpEntry) {
// We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+ mHeadsUpManager.removeNotification(
+ posted.key,
+ /* removeImmediately= */ false,
+ "onEntryUpdated"
+ )
} else if (posted.isBinding) {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
@@ -520,7 +524,11 @@
val removeImmediatelyForRemoteInput =
(mRemoteInputManager.isSpinning(entryKey) &&
!NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
- mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
+ mHeadsUpManager.removeNotification(
+ entry.key,
+ removeImmediatelyForRemoteInput,
+ "onEntryRemoved, reason: $reason"
+ )
}
}
@@ -721,7 +729,9 @@
{
mHeadsUpManager.removeNotification(
entry.key, /* releaseImmediately */
- true
+ true,
+ "cancel lifetime extension - extended for reason: " +
+ "$reason, isSticky: true"
)
},
removeAfterMillis
@@ -730,7 +740,9 @@
mExecutor.execute {
mHeadsUpManager.removeNotification(
entry.key, /* releaseImmediately */
- false
+ false,
+ "lifetime extension - extended for reason: $reason" +
+ ", isSticky: false"
)
}
mNotifsExtendingLifetime[entry] = null
@@ -902,7 +914,7 @@
fun commitModifications() {
deferred.forEach { (key, releaseImmediately) ->
- headsUpManager.removeNotification(key, releaseImmediately)
+ headsUpManager.removeNotification(key, releaseImmediately, "commitModifications")
}
deferred.clear()
}
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 41195aa..fa12bb9 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
@@ -638,8 +638,11 @@
if (row.isPinned() && !canChildBeDismissed(row)
&& row.getEntry().getSbn().getNotification().fullScreenIntent
== null) {
- mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
- true /* removeImmediately */);
+ mHeadsUpManager.removeNotification(
+ row.getEntry().getSbn().getKey(),
+ /* removeImmediately= */ true ,
+ /* reason= */ "onChildSnappedBack"
+ );
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index 107bf1e..d4ef42c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -613,8 +613,9 @@
super.onTransitionAnimationStart(isExpandingFullyAbove)
if (Flags.communalHub()) {
communalSceneInteractor.snapToScene(
- CommunalScenes.Blank,
- ActivityTransitionAnimator.TIMINGS.totalDuration
+ newScene = CommunalScenes.Blank,
+ loggingReason = "ActivityStarterInternalImpl",
+ delayMillis = ActivityTransitionAnimator.TIMINGS.totalDuration
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index c4fbc37..94dd9bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -160,6 +160,8 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.composefragment.QSFragmentCompose;
+import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -1432,9 +1434,15 @@
}
protected QS createDefaultQSFragment() {
+ Class<? extends QS> klass;
+ if (QSComposeFragment.isEnabled()) {
+ klass = QSFragmentCompose.class;
+ } else {
+ klass = QSFragmentLegacy.class;
+ }
return mFragmentService
.getFragmentHostManager(getNotificationShadeWindowView())
- .create(QSFragmentLegacy.class);
+ .create(klass);
}
private void setUpPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ac10155..ec92990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -193,7 +193,11 @@
void fireNotificationPulse(NotificationEntry entry) {
Runnable pulseSuppressedListener = () -> {
mHeadsUpManager.removeNotification(
- entry.getKey(), /* releaseImmediately= */ true, /* animate= */ false);
+ entry.getKey(),
+ /* releaseImmediately= */ true,
+ /* animate= */ false,
+ "fireNotificationPulse"
+ );
};
Assert.isMainThread();
for (Callback callback : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 25d9cc7..544a8a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -60,11 +60,6 @@
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
-import kotlinx.coroutines.flow.Flow;
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.flow.StateFlow;
-import kotlinx.coroutines.flow.StateFlowKt;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -75,6 +70,11 @@
import javax.inject.Inject;
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+import kotlinx.coroutines.flow.StateFlowKt;
+
/** A implementation of HeadsUpManager for phone. */
@SysUISingleton
public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
@@ -365,12 +365,14 @@
@Override
public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
- boolean animate) {
+ boolean animate, @NonNull String reason) {
if (animate) {
- return removeNotification(key, releaseImmediately);
+ return removeNotification(key, releaseImmediately,
+ "removeNotification(animate: true), reason: " + reason);
} else {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
- boolean removed = removeNotification(key, releaseImmediately);
+ final boolean removed = removeNotification(key, releaseImmediately,
+ "removeNotification(animate: false), reason: " + reason);
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
return removed;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 04604e0..dda02db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -32,6 +32,8 @@
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Flags;
@@ -47,7 +49,6 @@
public class PhoneStatusBarView extends FrameLayout {
private static final String TAG = "PhoneStatusBarView";
- private final StatusBarContentInsetsProvider mContentInsetsProvider;
private final StatusBarWindowController mStatusBarWindowController;
private int mRotationOrientation = -1;
@@ -60,6 +61,10 @@
private int mStatusBarHeight;
@Nullable
private Gefingerpoken mTouchEventHandler;
+ @Nullable
+ private HasCornerCutoutFetcher mHasCornerCutoutFetcher;
+ @Nullable
+ private InsetsFetcher mInsetsFetcher;
private int mDensity;
private float mFontScale;
@@ -70,7 +75,6 @@
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
}
@@ -78,6 +82,14 @@
mTouchEventHandler = handler;
}
+ void setHasCornerCutoutFetcher(@NonNull HasCornerCutoutFetcher cornerCutoutFetcher) {
+ mHasCornerCutoutFetcher = cornerCutoutFetcher;
+ }
+
+ void setInsetsFetcher(@NonNull InsetsFetcher insetsFetcher) {
+ mInsetsFetcher = insetsFetcher;
+ }
+
void init(StatusBarUserChipViewModel viewModel) {
StatusBarUserSwitcherContainer container = findViewById(R.id.user_switcher_container);
StatusBarUserChipViewBinder.bind(container, viewModel);
@@ -270,7 +282,14 @@
return;
}
- boolean hasCornerCutout = mContentInsetsProvider.currentRotationHasCornerCutout();
+ boolean hasCornerCutout;
+ if (mHasCornerCutoutFetcher != null) {
+ hasCornerCutout = mHasCornerCutoutFetcher.fetchHasCornerCutout();
+ } else {
+ Log.e(TAG, "mHasCornerCutoutFetcher unexpectedly null");
+ hasCornerCutout = true;
+ }
+
if (mDisplayCutout == null || mDisplayCutout.isEmpty() || hasCornerCutout) {
mCutoutSpace.setVisibility(View.GONE);
return;
@@ -288,8 +307,12 @@
}
private void updateSafeInsets() {
- Insets insets = mContentInsetsProvider
- .getStatusBarContentInsetsForCurrentRotation();
+ if (mInsetsFetcher == null) {
+ Log.e(TAG, "mInsetsFetcher unexpectedly null");
+ return;
+ }
+
+ Insets insets = mInsetsFetcher.fetchInsets();
setPadding(
insets.left,
insets.top,
@@ -298,6 +321,17 @@
}
private void updateWindowHeight() {
+ if (Flags.statusBarStopUpdatingWindowHeight()) {
+ return;
+ }
mStatusBarWindowController.refreshStatusBarHeight();
}
+
+ interface HasCornerCutoutFetcher {
+ boolean fetchHasCornerCutout();
+ }
+
+ interface InsetsFetcher {
+ Insets fetchInsets();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 468a3c3..456265b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -73,6 +73,7 @@
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
+ private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
) : ViewController<PhoneStatusBarView>(view) {
private lateinit var battery: BatteryMeterView
@@ -155,7 +156,14 @@
}
init {
+ // These should likely be done in `onInit`, not `init`.
mView.setTouchEventHandler(PhoneStatusBarViewTouchHandler())
+ mView.setHasCornerCutoutFetcher {
+ statusBarContentInsetsProvider.currentRotationHasCornerCutout()
+ }
+ mView.setInsetsFetcher {
+ statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ }
mView.init(userChipViewModel)
}
@@ -310,6 +318,7 @@
private val configurationController: ConfigurationController,
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
+ private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
@@ -335,6 +344,7 @@
configurationController,
statusOverlayHoverListenerFactory,
darkIconDispatcher,
+ statusBarContentInsetsProvider,
)
}
}
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 5486abb..de4d14d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -1007,7 +1007,9 @@
mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
mKeyguardMessageAreaController.setMessage("");
}
- mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
+ if (!SceneContainerFlag.isEnabled()) {
+ mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
+ }
if (updateScrim) {
mCentralSurfaces.updateScrimController();
@@ -1449,10 +1451,13 @@
mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing);
mCentralSurfaces.setBouncerShowing(primaryBouncerShowing);
}
- if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate
- || isPrimaryBouncerShowingChanged) {
- mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
- primaryBouncerShowing);
+ if (!SceneContainerFlag.isEnabled()) {
+ if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing
+ || mFirstUpdate
+ || isPrimaryBouncerShowingChanged) {
+ mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+ primaryBouncerShowing);
+ }
}
mFirstUpdate = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e92058b..0a6e7f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -230,7 +230,8 @@
Runnable action = () -> {
mBubblesManagerOptional.ifPresent(bubblesManager ->
bubblesManager.onUserChangedBubble(entry, !entry.isBubble()));
- mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true);
+ mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true,
+ /* reason= */ "onNotificationBubbleIconClicked");
};
if (entry.isBubble()) {
// entry is being un-bubbled, no need to unlock
@@ -621,7 +622,8 @@
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
// become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.removeNotification(key, true /* releaseImmediately */);
+ mHeadsUpManager.removeNotification(key, /* releaseImmediately= */ true,
+ "removeHunAfterClick");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 3786958..f37393a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -40,13 +40,14 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.time.SystemClock;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -191,12 +192,14 @@
* enough and needs to be kept around.
* @param key the key of the notification to remove
* @param releaseImmediately force a remove regardless of earliest removal time
+ * @param reason reason for removing the notification
* @return true if notification is removed, false otherwise
*/
@Override
- public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
+ public boolean removeNotification(@NotNull String key, boolean releaseImmediately,
+ @NonNull String reason) {
final boolean isWaiting = mAvalancheController.isWaiting(key);
- mLogger.logRemoveNotification(key, releaseImmediately, isWaiting);
+ mLogger.logRemoveNotification(key, releaseImmediately, isWaiting, reason);
if (mAvalancheController.isWaiting(key)) {
removeEntry(key, "removeNotification (isWaiting)");
@@ -204,6 +207,7 @@
}
HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
if (headsUpEntry == null) {
+ mLogger.logNullEntry(key, reason);
return true;
}
if (releaseImmediately) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index fcf77d5..04fe6b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -96,9 +96,10 @@
*
* @param key the key of the notification to remove
* @param releaseImmediately force a remove regardless of earliest removal time
+ * @param reason reason for removing the notification
* @return true if notification is removed, false otherwise
*/
- fun removeNotification(key: String, releaseImmediately: Boolean): Boolean
+ fun removeNotification(key: String, releaseImmediately: Boolean, reason: String): Boolean
/**
* Try to remove the notification. May not succeed if the notification has not been shown long
@@ -107,9 +108,15 @@
* @param key the key of the notification to remove
* @param releaseImmediately force a remove regardless of earliest removal time
* @param animate if true, animate the removal
+ * @param reason reason for removing the notification
* @return true if notification is removed, false otherwise
*/
- fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean
+ fun removeNotification(
+ key: String,
+ releaseImmediately: Boolean,
+ animate: Boolean,
+ reason: String
+ ): Boolean
/** Clears all managed notifications. */
fun releaseAllImmediately()
@@ -246,11 +253,16 @@
override fun removeListener(listener: OnHeadsUpChangedListener) {}
- override fun removeNotification(key: String, releaseImmediately: Boolean) = false
-
- override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) =
+ override fun removeNotification(key: String, releaseImmediately: Boolean, reason: String) =
false
+ override fun removeNotification(
+ key: String,
+ releaseImmediately: Boolean,
+ animate: Boolean,
+ reason: String
+ ) = false
+
override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 80c595f..c6fc547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -16,244 +16,283 @@
package com.android.systemui.statusbar.policy
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
/** Logger for [HeadsUpManager]. */
-class HeadsUpManagerLogger @Inject constructor(
- @NotificationHeadsUpLog private val buffer: LogBuffer
-) {
+class HeadsUpManagerLogger
+@Inject
+constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
fun logPackageSnoozed(snoozeKey: String) {
- buffer.log(TAG, INFO, {
- str1 = snoozeKey
- }, {
- "package snoozed $str1"
- })
+ buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package snoozed $str1" })
}
fun logPackageUnsnoozed(snoozeKey: String) {
- buffer.log(TAG, INFO, {
- str1 = snoozeKey
- }, {
- "package unsnoozed $str1"
- })
+ buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package unsnoozed $str1" })
}
fun logIsSnoozedReturned(snoozeKey: String) {
- buffer.log(TAG, INFO, {
- str1 = snoozeKey
- }, {
- "package snoozed when queried $str1"
- })
+ buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package snoozed when queried $str1" })
}
fun logReleaseAllImmediately() {
- buffer.log(TAG, INFO, { }, {
- "release all immediately"
- })
+ buffer.log(TAG, INFO, {}, { "release all immediately" })
}
fun logShowNotificationRequest(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "request: show notification $str1"
- })
+ buffer.log(TAG, INFO, { str1 = entry.logKey }, { "request: show notification $str1" })
}
- fun logAvalancheUpdate(caller: String, isEnabled: Boolean, notifEntryKey: String,
- outcome: String) {
- buffer.log(TAG, INFO, {
- str1 = caller
- str2 = notifEntryKey
- str3 = outcome
- bool1 = isEnabled
- }, {
- "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3"
- })
+ fun logAvalancheUpdate(
+ caller: String,
+ isEnabled: Boolean,
+ notifEntryKey: String,
+ outcome: String
+ ) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = caller
+ str2 = notifEntryKey
+ str3 = outcome
+ bool1 = isEnabled
+ },
+ { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" }
+ )
}
- fun logAvalancheDelete(caller: String, isEnabled: Boolean, notifEntryKey: String,
- outcome: String) {
- buffer.log(TAG, INFO, {
- str1 = caller
- str2 = notifEntryKey
- str3 = outcome
- bool1 = isEnabled
- }, {
- "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3"
- })
+ fun logAvalancheDelete(
+ caller: String,
+ isEnabled: Boolean,
+ notifEntryKey: String,
+ outcome: String
+ ) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = caller
+ str2 = notifEntryKey
+ str3 = outcome
+ bool1 = isEnabled
+ },
+ { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" }
+ )
}
fun logShowNotification(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "show notification $str1"
- })
+ buffer.log(TAG, INFO, { str1 = entry.logKey }, { "show notification $str1" })
}
fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- long1 = delayMillis
- str2 = reason
- }, {
- "schedule auto remove of $str1 in $long1 ms reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ long1 = delayMillis
+ str2 = reason
+ },
+ { "schedule auto remove of $str1 in $long1 ms reason: $str2" }
+ )
}
fun logAutoRemoveRequest(entry: NotificationEntry, reason: String) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- str2 = reason
- }, {
- "request: reschedule auto remove of $str1 reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ str2 = reason
+ },
+ { "request: reschedule auto remove of $str1 reason: $str2" }
+ )
}
fun logAutoRemoveRescheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- long1 = delayMillis
- str2 = reason
- }, {
- "reschedule auto remove of $str1 in $long1 ms reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ long1 = delayMillis
+ str2 = reason
+ },
+ { "reschedule auto remove of $str1 in $long1 ms reason: $str2" }
+ )
}
fun logAutoRemoveCancelRequest(entry: NotificationEntry, reason: String?) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- str2 = reason ?: "unknown"
- }, {
- "request: cancel auto remove of $str1 reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ str2 = reason ?: "unknown"
+ },
+ { "request: cancel auto remove of $str1 reason: $str2" }
+ )
}
fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- str2 = reason ?: "unknown"
- }, {
- "cancel auto remove of $str1 reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ str2 = reason ?: "unknown"
+ },
+ { "cancel auto remove of $str1 reason: $str2" }
+ )
}
fun logRemoveEntryRequest(key: String, reason: String, isWaiting: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- str2 = reason
- bool1 = isWaiting
- }, {
- "request: $str2 => remove entry $str1 isWaiting: $isWaiting"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ str2 = reason
+ bool1 = isWaiting
+ },
+ { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" }
+ )
}
fun logRemoveEntry(key: String, reason: String, isWaiting: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- str2 = reason
- bool1 = isWaiting
- }, {
- "$str2 => remove entry $str1 isWaiting: $isWaiting"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ str2 = reason
+ bool1 = isWaiting
+ },
+ { "$str2 => remove entry $str1 isWaiting: $isWaiting" }
+ )
}
fun logUnpinEntryRequest(key: String) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- }, {
- "request: unpin entry $str1"
- })
+ buffer.log(TAG, INFO, { str1 = logKey(key) }, { "request: unpin entry $str1" })
}
fun logUnpinEntry(key: String) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- }, {
- "unpin entry $str1"
- })
+ buffer.log(TAG, INFO, { str1 = logKey(key) }, { "unpin entry $str1" })
}
- fun logRemoveNotification(key: String, releaseImmediately: Boolean, isWaiting: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- bool1 = releaseImmediately
- bool2 = isWaiting
- }, {
- "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2"
- })
+ fun logRemoveNotification(
+ key: String,
+ releaseImmediately: Boolean,
+ isWaiting: Boolean,
+ reason: String
+ ) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ bool1 = releaseImmediately
+ bool2 = isWaiting
+ str2 = reason
+ },
+ {
+ "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2 " +
+ "reason: $str2"
+ }
+ )
+ }
+
+ fun logNullEntry(key: String, reason: String) {
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ str2 = reason
+ },
+ { "remove notification $str1 when headsUpEntry is null, reason: $str2" }
+ )
}
fun logNotificationActuallyRemoved(entry: NotificationEntry) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- }, {
- "notification removed $str1 "
- })
+ buffer.log(TAG, INFO, { str1 = entry.logKey }, { "notification removed $str1 " })
}
fun logUpdateNotificationRequest(key: String, alert: Boolean, hasEntry: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- bool1 = alert
- bool2 = hasEntry
- }, {
- "request: update notification $str1 alert: $bool1 hasEntry: $bool2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ bool1 = alert
+ bool2 = hasEntry
+ },
+ { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" }
+ )
}
fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
- buffer.log(TAG, INFO, {
- str1 = logKey(key)
- bool1 = alert
- bool2 = hasEntry
- }, {
- "update notification $str1 alert: $bool1 hasEntry: $bool2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = logKey(key)
+ bool1 = alert
+ bool2 = hasEntry
+ },
+ { "update notification $str1 alert: $bool1 hasEntry: $bool2" }
+ )
}
fun logUpdateEntry(entry: NotificationEntry, updatePostTime: Boolean, reason: String?) {
- buffer.log(TAG, INFO, {
- str1 = entry.logKey
- bool1 = updatePostTime
- str2 = reason ?: "unknown"
- }, {
- "update entry $str1 updatePostTime: $bool1 reason: $str2"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ bool1 = updatePostTime
+ str2 = reason ?: "unknown"
+ },
+ { "update entry $str1 updatePostTime: $bool1 reason: $str2" }
+ )
}
fun logSnoozeLengthChange(packageSnoozeLengthMs: Int) {
- buffer.log(TAG, INFO, {
- int1 = packageSnoozeLengthMs
- }, {
- "snooze length changed: ${int1}ms"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { int1 = packageSnoozeLengthMs },
+ { "snooze length changed: ${int1}ms" }
+ )
}
fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) {
- buffer.log(TAG, VERBOSE, {
- str1 = entry.logKey
- bool1 = isPinned
- str2 = reason
- }, {
- "$str2 => set entry pinned $str1 pinned: $bool1"
- })
+ buffer.log(
+ TAG,
+ VERBOSE,
+ {
+ str1 = entry.logKey
+ bool1 = isPinned
+ str2 = reason
+ },
+ { "$str2 => set entry pinned $str1 pinned: $bool1" }
+ )
}
fun logUpdatePinnedMode(hasPinnedNotification: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = hasPinnedNotification
- }, {
- "has pinned notification changed to $bool1"
- })
+ buffer.log(
+ TAG,
+ INFO,
+ { bool1 = hasPinnedNotification },
+ { "has pinned notification changed to $bool1" }
+ )
}
}
-private const val TAG = "HeadsUpManager"
\ No newline at end of file
+private const val TAG = "HeadsUpManager"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index 8aa989f..4f7749b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -57,6 +57,7 @@
private val activityStarter: ActivityStarter,
// Using a provider to avoid a circular dependency.
private val viewModel: Provider<ModesDialogViewModel>,
+ private val dialogEventLogger: ModesDialogEventLogger,
@Main private val mainCoroutineContext: CoroutineContext,
) : SystemUIDialog.Delegate {
// NOTE: This should only be accessed/written from the main thread.
@@ -102,7 +103,9 @@
)
}
- private fun openSettings(dialog: SystemUIDialog) {
+ @VisibleForTesting
+ fun openSettings(dialog: SystemUIDialog) {
+ dialogEventLogger.logDialogSettings()
val animationController =
dialogTransitionAnimator.createActivityTransitionController(dialog)
if (animationController == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt
new file mode 100644
index 0000000..33ed419
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.notification.modes.ZenMode
+import com.android.systemui.qs.QSModesEvent
+import javax.inject.Inject
+
+class ModesDialogEventLogger
+@Inject
+constructor(
+ private val uiEventLogger: UiEventLogger,
+) {
+
+ fun logModeOn(mode: ZenMode) {
+ val id =
+ if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_ON else QSModesEvent.QS_MODES_MODE_ON
+ uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+ }
+
+ fun logModeOff(mode: ZenMode) {
+ val id =
+ if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_OFF else QSModesEvent.QS_MODES_MODE_OFF
+ uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+ }
+
+ fun logModeSettings(mode: ZenMode) {
+ val id =
+ if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_SETTINGS
+ else QSModesEvent.QS_MODES_MODE_SETTINGS
+ uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+ }
+
+ fun logOpenDurationDialog(mode: ZenMode) {
+ // should only occur for manual Do Not Disturb.
+ if (!mode.isManualDnd) {
+ return
+ }
+ uiEventLogger.log(QSModesEvent.QS_MODES_DURATION_DIALOG)
+ }
+
+ fun logDialogSettings() {
+ uiEventLogger.log(QSModesEvent.QS_MODES_SETTINGS)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index 44b692f..5772099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -49,6 +50,7 @@
zenModeInteractor: ZenModeInteractor,
@Background val bgDispatcher: CoroutineDispatcher,
private val dialogDelegate: ModesDialogDelegate,
+ private val dialogEventLogger: ModesDialogEventLogger,
) {
private val zenDialogMetricsLogger = QSZenModeDialogMetricsLogger(context)
@@ -94,14 +96,17 @@
if (!mode.rule.isEnabled) {
openSettings(mode)
} else if (mode.isActive) {
+ dialogEventLogger.logModeOff(mode)
zenModeInteractor.deactivateMode(mode)
} else {
if (mode.rule.isManualInvocationAllowed) {
if (zenModeInteractor.shouldAskForZenDuration(mode)) {
+ dialogEventLogger.logOpenDurationDialog(mode)
// NOTE: The dialog handles turning on the mode itself.
val dialog = makeZenModeDialog()
dialog.show()
} else {
+ dialogEventLogger.logModeOn(mode)
zenModeInteractor.activateMode(mode)
}
}
@@ -114,6 +119,7 @@
.flowOn(bgDispatcher)
private fun openSettings(mode: ZenMode) {
+ dialogEventLogger.logModeSettings(mode)
val intent: Intent =
Intent(ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, mode.id)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 066bfc5..1522cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -165,6 +165,7 @@
private boolean mShowSafetyWarning;
private long mLastToggledRingerOn;
private boolean mDeviceInteractive = true;
+ boolean mInAudioSharing = false;
private VolumePolicy mVolumePolicy;
@GuardedBy("this")
@@ -295,6 +296,9 @@
mJavaAdapter.alwaysCollectFlow(
mAudioSharingInteractor.getVolume(),
this::handleAudioSharingStreamVolumeChanges);
+ mJavaAdapter.alwaysCollectFlow(
+ mAudioSharingInteractor.isInAudioSharing(),
+ inSharing -> mInAudioSharing = inSharing);
}
}
@@ -510,11 +514,18 @@
// Since their values overlap with DEVICE_OUT_EARPIECE and DEVICE_OUT_SPEAKER.
// Anyway, we can check BLE devices by using just DEVICE_OUT_BLE_HEADSET.
final boolean routedToBluetooth =
- (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
- (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
- AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
- AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
- AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0;
+ // TODO(b/359737651): Need audio support to return broadcast mask.
+ // For now, mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) will return
+ // AudioManager.DEVICE_NONE, so we also need to check if the device is in audio
+ // sharing here.
+ mInAudioSharing
+ || (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC)
+ & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
+ | AudioManager
+ .DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+ | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+ | AudioManager.DEVICE_OUT_BLE_HEADSET))
+ != 0;
changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
} else if (stream == AudioManager.STREAM_VOICE_CALL) {
final boolean routedToBluetooth =
@@ -813,6 +824,7 @@
ss.dynamic = true;
ss.levelMin = mAudioSharingInteractor.getVolumeMin();
ss.levelMax = mAudioSharingInteractor.getVolumeMax();
+ ss.routedToBluetooth = true;
if (ss.level != volume) {
ss.level = volume;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e56f6b3..2468449 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1892,8 +1892,8 @@
.equals(ss.remoteLabel)) {
addRow(
stream,
- R.drawable.ic_volume_media,
- R.drawable.ic_volume_media_mute,
+ R.drawable.ic_volume_media_bt,
+ R.drawable.ic_volume_media_bt_mute,
true,
false,
true);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index 154737c..4f77cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -26,7 +26,6 @@
import com.android.settingslib.media.MediaDevice.MediaDeviceType
import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.volume.data.repository.AudioRepository
-import com.android.settingslib.volume.data.repository.AudioSharingRepository
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -37,7 +36,6 @@
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
@@ -60,7 +58,6 @@
private val bluetoothAdapter: BluetoothAdapter?,
private val deviceIconInteractor: DeviceIconInteractor,
private val mediaOutputInteractor: MediaOutputInteractor,
- audioSharingRepository: AudioSharingRepository,
) {
val currentAudioDevice: StateFlow<AudioOutputDevice> =
@@ -80,9 +77,6 @@
.flowOn(backgroundCoroutineContext)
.stateIn(scope, SharingStarted.Eagerly, AudioOutputDevice.Unknown)
- /** Whether the device is in audio sharing */
- val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
-
private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
if (
BluetoothAdapter.checkBluetoothAddress(address) &&
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
index 2170c36..9aed8ab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
@@ -36,11 +36,15 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
interface AudioSharingInteractor {
+ /** Audio sharing state on the device. */
+ val isInAudioSharing: Flow<Boolean>
+
/** Audio sharing secondary headset volume changes. */
val volume: Flow<Int?>
@@ -76,6 +80,7 @@
private val audioVolumeInteractor: AudioVolumeInteractor,
private val audioSharingRepository: AudioSharingRepository
) : AudioSharingInteractor {
+ override val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
override val volume: Flow<Int?> =
combine(audioSharingRepository.secondaryGroupId, audioSharingRepository.volumeMap) {
@@ -125,13 +130,13 @@
}
private companion object {
- const val TAG = "AudioSharingInteractor"
const val DEFAULT_VOLUME = 20
}
}
@SysUISingleton
class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingInteractor {
+ override val isInAudioSharing: Flow<Boolean> = flowOf(false)
override val volume: Flow<Int?> = emptyFlow()
override val volumeMin: Int = EMPTY_VOLUME
override val volumeMax: Int = EMPTY_VOLUME
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
index ed25129..a270d5f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -18,6 +18,7 @@
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.interactor.AudioSharingInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
@@ -49,11 +50,12 @@
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
audioOutputInteractor: AudioOutputInteractor,
audioModeInteractor: AudioModeInteractor,
- interactor: MediaOutputInteractor,
+ mediaOutputInteractor: MediaOutputInteractor,
+ audioSharingInteractor: AudioSharingInteractor,
) {
private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
- interactor.defaultActiveMediaSession
+ mediaOutputInteractor.defaultActiveMediaSession
.filterData()
.flatMapLatest { session ->
if (session == null) {
@@ -77,7 +79,7 @@
val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
audioModeInteractor.isOngoingCall
.flatMapLatest { isOngoingCall ->
- audioOutputInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
+ audioSharingInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
if (isOngoingCall) {
currentAudioDevice.map {
MediaOutputComponentModel.Calling(it, isInAudioSharing)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 075d8ae..7aa415b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -102,6 +102,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.service.dreams.IDreamManager;
import android.service.trust.TrustAgentService;
import android.telephony.ServiceState;
@@ -111,9 +112,9 @@
import android.testing.TestableLooper;
import android.text.TextUtils;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
@@ -129,6 +130,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
@@ -138,9 +140,13 @@
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -149,6 +155,7 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.After;
@@ -175,8 +182,11 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private static final String PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY =
@@ -277,6 +287,12 @@
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
+ @Mock
+ private AlternateBouncerInteractor mAlternateBouncerInteractor;
+ @Mock
+ private JavaAdapter mJavaAdapter;
+ @Mock
+ private SceneInteractor mSceneInteractor;
@Captor
private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
@@ -301,6 +317,16 @@
mFingerprintAuthenticatorsRegisteredCallback;
private final InstanceId mKeyguardInstanceId = InstanceId.fakeInstanceId(999);
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
+ }
+
+ public KeyguardUpdateMonitorTest(FlagsParameterization flags) {
+ super();
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setup() throws RemoteException {
mKosmos = new KosmosJavaAdapter(this);
@@ -993,7 +1019,7 @@
verifyFingerprintAuthenticateNeverCalled();
// WHEN alternate bouncer is shown
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
- mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
// THEN make sure FP listening begins
verifyFingerprintAuthenticateCall();
@@ -1489,7 +1515,7 @@
@Test
public void testShouldNotListenForUdfps_whenInLockDown() {
// GIVEN a "we should listen for udfps" state
- setKeyguardBouncerVisibility(false /* isVisible */);
+ mKeyguardUpdateMonitor.setPrimaryBouncerVisibility(false /* isVisible */);
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
@@ -2124,7 +2150,7 @@
verifyFingerprintAuthenticateNeverCalled();
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
- mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+ mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
verifyFingerprintAuthenticateCall();
}
@@ -2323,12 +2349,7 @@
}
private void bouncerFullyVisible() {
- setKeyguardBouncerVisibility(true);
- }
-
- private void setKeyguardBouncerVisibility(boolean isVisible) {
- mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
- mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.setPrimaryBouncerVisibility(true);
}
private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
@@ -2434,7 +2455,12 @@
mPackageManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider),
- mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
+ mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager,
+ () -> mAlternateBouncerInteractor,
+ () -> mJavaAdapter,
+ () -> mSceneInteractor);
+ setAlternateBouncerVisibility(false);
+ setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
start();
}
@@ -2458,5 +2484,25 @@
protected int getBiometricLockoutDelay() {
return 0;
}
+
+ private void setPrimaryBouncerVisibility(boolean isVisible) {
+ if (SceneContainerFlag.isEnabled()) {
+ ObservableTransitionState transitionState = new ObservableTransitionState.Idle(
+ isVisible ? Scenes.Bouncer : Scenes.Lockscreen);
+ when(mSceneInteractor.getTransitionState()).thenReturn(
+ MutableStateFlow(transitionState));
+ onTransitionStateChanged(transitionState);
+ } else {
+ sendPrimaryBouncerChanged(isVisible, isVisible);
+ mTestableLooper.processAllMessages();
+ }
+ }
+
+ private void setAlternateBouncerVisibility(boolean isVisible) {
+ if (SceneContainerFlag.isEnabled()) {
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(isVisible);
+ }
+ onAlternateBouncerVisibilityChange(isVisible);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
new file mode 100644
index 0000000..0c716137
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial.ui.viewmodel
+
+import androidx.lifecycle.Lifecycle.Event
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.SavedStateHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
+import com.android.systemui.keyboard.data.repository.keyboardRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import com.android.systemui.touchpad.tutorial.touchpadGesturesInteractor
+import com.android.systemui.util.coroutines.MainDispatcherRule
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val sysUiState = kosmos.sysUiState
+ private val touchpadRepo = PrettyFakeTouchpadRepository()
+ private val keyboardRepo = kosmos.keyboardRepository
+ private var startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+ private val viewModel by lazy { createViewModel(startingPeripheral) }
+
+ // createUnsafe so its methods don't have to be called on Main thread
+ private val lifecycle = LifecycleRegistry.createUnsafe(mock(LifecycleOwner::class.java))
+
+ @get:Rule val mainDispatcherRule = MainDispatcherRule(kosmos.testDispatcher)
+
+ private fun createViewModel(
+ startingPeripheral: String = INTENT_TUTORIAL_TYPE_TOUCHPAD,
+ hasTouchpadTutorialScreens: Boolean = true,
+ ): KeyboardTouchpadTutorialViewModel {
+ val viewModel =
+ KeyboardTouchpadTutorialViewModel(
+ Optional.of(kosmos.touchpadGesturesInteractor),
+ KeyboardTouchpadConnectionInteractor(keyboardRepo, touchpadRepo),
+ hasTouchpadTutorialScreens,
+ SavedStateHandle(mapOf(INTENT_TUTORIAL_TYPE_KEY to startingPeripheral))
+ )
+ lifecycle.addObserver(viewModel)
+ return viewModel
+ }
+
+ @Test
+ fun screensOrder_whenTouchpadAndKeyboardConnected() =
+ testScope.runTest {
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+ peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+ goToNextScreen()
+ goToNextScreen()
+ // reached the last screen
+
+ assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY).inOrder()
+ assertThat(closeActivity).isFalse()
+ }
+
+ @Test
+ fun screensOrder_whenKeyboardDisconnectsDuringTutorial() =
+ testScope.runTest {
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+ peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+ // back gesture screen
+ goToNextScreen()
+ // home gesture screen
+ peripheralsState(keyboardConnected = false, touchpadConnected = true)
+ goToNextScreen()
+ // no action key screen because keyboard disconnected
+
+ assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE).inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun screensOrderUntilFinish_whenTouchpadAndKeyboardConnected() =
+ testScope.runTest {
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+
+ peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+ goToNextScreen()
+ goToNextScreen()
+ // we're at the last screen so "next screen" should be actually closing activity
+ goToNextScreen()
+
+ assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY).inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun screensOrder_whenGoingBackToPreviousScreens() =
+ testScope.runTest {
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+ peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+ // back gesture
+ goToNextScreen()
+ // home gesture
+ goToNextScreen()
+ // action key
+
+ goBack()
+ // home gesture
+ goBack()
+ // back gesture
+ goBack()
+ // finish activity
+
+ assertThat(screens)
+ .containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY, HOME_GESTURE, BACK_GESTURE)
+ .inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun screensOrder_whenGoingBackAndOnlyKeyboardConnected() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+ peripheralsState(keyboardConnected = true, touchpadConnected = false)
+
+ // action key screen
+ goBack()
+ // activity finished
+
+ assertThat(screens).containsExactly(ACTION_KEY).inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun screensOrder_whenTouchpadConnected() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+
+ peripheralsState(keyboardConnected = false, touchpadConnected = true)
+
+ goToNextScreen()
+ goToNextScreen()
+ goToNextScreen()
+
+ assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE).inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun screensOrder_whenKeyboardConnected() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+
+ peripheralsState(keyboardConnected = true)
+
+ goToNextScreen()
+ goToNextScreen()
+
+ assertThat(screens).containsExactly(ACTION_KEY).inOrder()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun touchpadGesturesDisabled_onlyDuringTouchpadTutorial() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+ collectValues(viewModel.screen) // just to initialize viewModel
+ peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+ assertGesturesDisabled()
+ goToNextScreen()
+ goToNextScreen()
+ // end of touchpad tutorial, keyboard tutorial starts
+ assertGesturesNotDisabled()
+ }
+
+ @Test
+ fun activityFinishes_ifTouchpadModuleIsNotPresent() =
+ testScope.runTest {
+ val viewModel =
+ createViewModel(
+ startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD,
+ hasTouchpadTutorialScreens = false
+ )
+ val screens by collectValues(viewModel.screen)
+ val closeActivity by collectLastValue(viewModel.closeActivity)
+ peripheralsState(touchpadConnected = true)
+
+ assertThat(screens).isEmpty()
+ assertThat(closeActivity).isTrue()
+ }
+
+ @Test
+ fun touchpadGesturesDisabled_whenTutorialGoesToForeground() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+ collectValues(viewModel.screen) // just to initialize viewModel
+ peripheralsState(touchpadConnected = true)
+
+ lifecycle.handleLifecycleEvent(Event.ON_START)
+
+ assertGesturesDisabled()
+ }
+
+ @Test
+ fun touchpadGesturesNotDisabled_whenTutorialGoesToBackground() =
+ testScope.runTest {
+ startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+ collectValues(viewModel.screen)
+ peripheralsState(touchpadConnected = true)
+
+ lifecycle.handleLifecycleEvent(Event.ON_START)
+ lifecycle.handleLifecycleEvent(Event.ON_STOP)
+
+ assertGesturesNotDisabled()
+ }
+
+ @Test
+ fun keyboardShortcutsDisabled_onlyDuringKeyboardTutorial() =
+ testScope.runTest {
+ // TODO(b/358587037)
+ }
+
+ private fun TestScope.goToNextScreen() {
+ viewModel.onDoneButtonClicked()
+ runCurrent()
+ }
+
+ private fun TestScope.goBack() {
+ viewModel.onBack()
+ runCurrent()
+ }
+
+ private fun TestScope.peripheralsState(
+ keyboardConnected: Boolean = false,
+ touchpadConnected: Boolean = false
+ ) {
+ keyboardRepo.setIsAnyKeyboardConnected(keyboardConnected)
+ touchpadRepo.setIsAnyTouchpadConnected(touchpadConnected)
+ runCurrent()
+ }
+
+ private fun TestScope.assertGesturesNotDisabled() = assertFlagEnabled(enabled = false)
+
+ private fun TestScope.assertGesturesDisabled() = assertFlagEnabled(enabled = true)
+
+ private fun TestScope.assertFlagEnabled(enabled: Boolean) {
+ // sysui state is changed on background scope so let's make sure it's executed
+ runCurrent()
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED))
+ .isEqualTo(enabled)
+ }
+
+ // replace below when we have better fake
+ internal class PrettyFakeTouchpadRepository : TouchpadRepository {
+
+ private val _isAnyTouchpadConnected = MutableStateFlow(false)
+ override val isAnyTouchpadConnected: Flow<Boolean> = _isAnyTouchpadConnected
+
+ fun setIsAnyTouchpadConnected(connected: Boolean) {
+ _isAnyTouchpadConnected.value = connected
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 206bbbf..4ce2d7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -51,6 +51,7 @@
import androidx.compose.ui.platform.ComposeView;
import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -611,7 +612,8 @@
when(mQSContainerImplController.getView()).thenReturn(mContainer);
when(mQSPanelController.getTileLayout()).thenReturn(mQQsTileLayout);
when(mQuickQSPanelController.getTileLayout()).thenReturn(mQsTileLayout);
- when(mFooterActionsViewModelFactory.create(any())).thenReturn(mFooterActionsViewModel);
+ when(mFooterActionsViewModelFactory.create(any(LifecycleOwner.class)))
+ .thenReturn(mFooterActionsViewModel);
}
private void setUpMedia() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
index 3abdf62..cb92b77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
@@ -91,7 +91,12 @@
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager)
- .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */)
+ .removeNotification(
+ notificationKey,
+ /* releaseImmediately= */ true,
+ /* animate= */ true,
+ /* reason= */ "onIntentStarted(willAnimate=false)"
+ )
verify(onFinishAnimationCallback).run()
}
@@ -109,7 +114,12 @@
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager)
- .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */)
+ .removeNotification(
+ notificationKey,
+ /* releaseImmediately= */ true,
+ /* animate= */ true,
+ /* reason= */ "onLaunchAnimationCancelled()"
+ )
verify(onFinishAnimationCallback).run()
}
@@ -127,7 +137,12 @@
assertFalse(isExpandAnimationRunning!!)
verify(headsUpManager)
- .removeNotification(notificationKey, true /* releaseImmediately */, false /* animate */)
+ .removeNotification(
+ notificationKey,
+ /* releaseImmediately= */ true,
+ /* animate= */ false,
+ /* reason= */ "onLaunchAnimationEnd()"
+ )
verify(onFinishAnimationCallback).run()
}
@@ -161,12 +176,18 @@
controller.onTransitionAnimationEnd(isExpandingFullyAbove = true)
verify(headsUpManager)
- .removeNotification(summary.key, true /* releaseImmediately */, false /* animate */)
+ .removeNotification(
+ summary.key,
+ /* releaseImmediately= */ true,
+ /* animate= */ false,
+ /* reason= */ "onLaunchAnimationEnd()"
+ )
verify(headsUpManager, never())
.removeNotification(
notification.entry.key,
- true /* releaseImmediately */,
- false /* animate */
+ /* releaseImmediately= */ true,
+ /* animate= */ false,
+ /* reason= */ "onLaunchAnimationEnd()"
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 8e9323f..b4f4138 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -108,30 +108,31 @@
private val executor = FakeExecutor(systemClock)
private val huns: ArrayList<NotificationEntry> = ArrayList()
private lateinit var helper: NotificationGroupTestHelper
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
helper = NotificationGroupTestHelper(mContext)
- coordinator = HeadsUpCoordinator(
- logger,
- systemClock,
- headsUpManager,
- headsUpViewBinder,
- visualInterruptionDecisionProvider,
- remoteInputManager,
- launchFullScreenIntentProvider,
- flags,
- headerController,
- executor)
+ coordinator =
+ HeadsUpCoordinator(
+ logger,
+ systemClock,
+ headsUpManager,
+ headsUpViewBinder,
+ visualInterruptionDecisionProvider,
+ remoteInputManager,
+ launchFullScreenIntentProvider,
+ flags,
+ headerController,
+ executor
+ )
coordinator.attach(notifPipeline)
// capture arguments:
collectionListener = withArgCaptor {
verify(notifPipeline).addCollectionListener(capture())
}
- notifPromoter = withArgCaptor {
- verify(notifPipeline).addPromoter(capture())
- }
+ notifPromoter = withArgCaptor { verify(notifPipeline).addPromoter(capture()) }
notifLifetimeExtender = withArgCaptor {
verify(notifPipeline).addNotificationLifetimeExtender(capture())
}
@@ -141,9 +142,7 @@
beforeFinalizeFilterListener = withArgCaptor {
verify(notifPipeline).addOnBeforeFinalizeFilterListener(capture())
}
- onHeadsUpChangedListener = withArgCaptor {
- verify(headsUpManager).addListener(capture())
- }
+ onHeadsUpChangedListener = withArgCaptor { verify(headsUpManager).addListener(capture()) }
actionPressListener = withArgCaptor {
verify(remoteInputManager).addActionPressListener(capture())
}
@@ -187,8 +186,8 @@
assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
executor.advanceClockToLast()
executor.runAllReady()
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(headsUpManager, times(1)).removeNotification(anyString(), eq(true))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false), anyString())
+ verify(headsUpManager, times(1)).removeNotification(anyString(), eq(true), anyString())
}
@Test
@@ -203,8 +202,8 @@
executor.advanceClockToLast()
executor.runAllReady()
assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false), anyString())
+ verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true), anyString())
}
@Test
@@ -217,7 +216,7 @@
notifLifetimeExtender.cancelLifetimeExtension(entry)
executor.advanceClockToLast()
executor.runAllReady()
- verify(headsUpManager, times(0)).removeNotification(anyString(), any())
+ verify(headsUpManager, never()).removeNotification(anyString(), any(), anyString())
}
@Test
@@ -227,14 +226,14 @@
whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason = */ 0))
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason= */ 0))
actionPressListener.accept(entry)
executor.runAllReady()
verify(endLifetimeExtension, times(1)).onEndLifetimeExtension(notifLifetimeExtender, entry)
- collectionListener.onEntryRemoved(entry, /* reason = */ 0)
- verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
+ collectionListener.onEntryRemoved(entry, /* reason= */ 0)
+ verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any(), anyString())
}
@Test
@@ -248,8 +247,8 @@
whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true)
assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
- collectionListener.onEntryRemoved(entry, /* reason = */ 0)
- verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
+ collectionListener.onEntryRemoved(entry, /* reason= */ 0)
+ verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any(), anyString())
}
@Test
@@ -261,8 +260,8 @@
addHUN(entry)
executor.advanceClockToLast()
executor.runAllReady()
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ verify(headsUpManager, never()).removeNotification(anyString(), eq(false), anyString())
+ verify(headsUpManager, never()).removeNotification(anyString(), eq(true), anyString())
}
@Test
@@ -273,8 +272,8 @@
assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
executor.advanceClockToLast()
executor.runAllReady()
- verify(headsUpManager, times(1)).removeNotification(anyString(), eq(false))
- verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ verify(headsUpManager, times(1)).removeNotification(anyString(), eq(false), anyString())
+ verify(headsUpManager, never()).removeNotification(anyString(), eq(true), anyString())
}
@Test
@@ -326,9 +325,8 @@
// THEN only promote the current HUN, mEntry
assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
- assertFalse(notifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
- .setPkg("test-package2")
- .build()))
+ val testPackage2 = NotificationEntryBuilder().setPkg("test-package2").build()
+ assertFalse(notifPromoter.shouldPromoteToTopLevel(testPackage2))
}
@Test
@@ -338,9 +336,9 @@
// THEN only section the current HUN, mEntry
assertTrue(notifSectioner.isInSection(entry))
- assertFalse(notifSectioner.isInSection(NotificationEntryBuilder()
- .setPkg("test-package")
- .build()))
+ assertFalse(
+ notifSectioner.isInSection(NotificationEntryBuilder().setPkg("test-package").build())
+ )
}
@Test
@@ -350,10 +348,12 @@
// THEN only the current HUN, mEntry, should be lifetimeExtended
assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* cancellationReason */ 0))
- assertFalse(notifLifetimeExtender.maybeExtendLifetime(
- NotificationEntryBuilder()
- .setPkg("test-package")
- .build(), /* cancellationReason */ 0))
+ assertFalse(
+ notifLifetimeExtender.maybeExtendLifetime(
+ NotificationEntryBuilder().setPkg("test-package").build(),
+ /* reason= */ 0
+ )
+ )
}
@Test
@@ -366,8 +366,9 @@
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
verify(headsUpManager, never()).showNotification(entry)
withArgCaptor<BindCallback> {
- verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
- }.onBindFinished(entry)
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ }
+ .onBindFinished(entry)
// THEN we tell the HeadsUpManager to show the notification
verify(headsUpManager).showNotification(entry)
@@ -430,7 +431,7 @@
whenever(remoteInputManager.isSpinning(any())).thenReturn(false)
// THEN heads up manager should remove the entry
- verify(headsUpManager).removeNotification(entry.key, false)
+ verify(headsUpManager).removeNotification(eq(entry.key), eq(false), anyString())
}
private fun addHUN(entry: NotificationEntry) {
@@ -545,19 +546,22 @@
collectionListener.onEntryAdded(groupSibling1)
collectionListener.onEntryAdded(groupSibling2)
- val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
- .build()
+ val beforeTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- val afterTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupSibling2))
- .build()
- beforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+ val afterTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
+ .build()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+ listOf(groupPriority, afterTransformGroup)
+ )
verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
finishBind(groupPriority)
@@ -583,19 +587,22 @@
collectionListener.onEntryUpdated(groupSibling1)
collectionListener.onEntryUpdated(groupSibling2)
- val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
- .build()
+ val beforeTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- val afterTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupSibling2))
- .build()
- beforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+ val afterTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
+ .build()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+ listOf(groupPriority, afterTransformGroup)
+ )
verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
finishBind(groupPriority)
@@ -618,19 +625,22 @@
collectionListener.onEntryUpdated(groupSummary)
collectionListener.onEntryUpdated(groupPriority)
- val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
- .build()
+ val beforeTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- val afterTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupSibling2))
- .build()
- beforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+ val afterTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
+ .build()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+ listOf(groupPriority, afterTransformGroup)
+ )
verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
finishBind(groupPriority)
@@ -654,19 +664,22 @@
collectionListener.onEntryUpdated(groupSibling1)
collectionListener.onEntryUpdated(groupSibling2)
- val beforeTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
- .build()
+ val beforeTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
- val afterTransformGroup = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupSibling2))
- .build()
- beforeFinalizeFilterListener
- .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+ val afterTransformGroup =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
+ .build()
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+ listOf(groupPriority, afterTransformGroup)
+ )
finishBind(groupSummary)
verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any())
@@ -688,10 +701,11 @@
collectionListener.onEntryAdded(groupSummary)
collectionListener.onEntryAdded(groupSibling1)
collectionListener.onEntryAdded(groupSibling2)
- val groupEntry = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupSibling1, groupSibling2))
- .build()
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupSibling1, groupSibling2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -708,16 +722,16 @@
@Test
fun testNoTransferTwoChildAlert_withGroupAlertAll() {
setShouldHeadsUp(groupSummary)
- whenever(notifPipeline.allNotifs)
- .thenReturn(listOf(groupSummary, groupChild1, groupChild2))
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupChild1, groupChild2))
collectionListener.onEntryAdded(groupSummary)
collectionListener.onEntryAdded(groupChild1)
collectionListener.onEntryAdded(groupChild2)
- val groupEntry = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupChild1, groupChild2))
- .build()
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupChild1, groupChild2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -742,10 +756,11 @@
collectionListener.onEntryAdded(groupSummary)
collectionListener.onEntryAdded(groupChild1)
collectionListener.onEntryAdded(groupChild2)
- val groupEntry = GroupEntryBuilder()
- .setSummary(groupSummary)
- .setChildren(listOf(groupChild1, groupChild2))
- .build()
+ val groupEntry =
+ GroupEntryBuilder()
+ .setSummary(groupSummary)
+ .setChildren(listOf(groupChild1, groupChild2))
+ .build()
beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -1045,9 +1060,7 @@
.thenReturn(DecisionImpl.of(should))
}
- private fun setDefaultShouldFullScreen(
- originalDecision: FullScreenIntentDecision
- ) {
+ private fun setDefaultShouldFullScreen(originalDecision: FullScreenIntentDecision) {
val provider = visualInterruptionDecisionProvider
whenever(provider.makeUnloggedFullScreenIntentDecision(any())).thenAnswer {
val entry: NotificationEntry = it.getArgument(0)
@@ -1059,11 +1072,8 @@
entry: NotificationEntry,
originalDecision: FullScreenIntentDecision
) {
- whenever(
- visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
- ).thenAnswer {
- FullScreenIntentDecisionImpl(entry, originalDecision)
- }
+ whenever(visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry))
+ .thenAnswer { FullScreenIntentDecisionImpl(entry, originalDecision) }
}
private fun verifyLoggedFullScreenIntentDecision(
@@ -1089,7 +1099,8 @@
private fun finishBind(entry: NotificationEntry) {
verify(headsUpManager, never()).showNotification(entry)
withArgCaptor<BindCallback> {
- verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
- }.onBindFinished(entry)
+ verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+ }
+ .onBindFinished(entry)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 30e7247..70ac31d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -391,6 +391,7 @@
configurationController,
mStatusOverlayHoverListenerFactory,
fakeDarkIconDispatcher,
+ mock(StatusBarContentInsetsProvider::class.java),
)
.create(view)
.also { it.init() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index ed5ec7b2..648ddf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -32,6 +32,7 @@
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT
import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP
import com.android.systemui.Gefingerpoken
import com.android.systemui.SysuiTestCase
@@ -42,6 +43,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -54,21 +56,14 @@
private val systemIconsContainer: View
get() = view.requireViewById(R.id.system_icons)
- private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
private val windowController = mock<StatusBarWindowController>()
@Before
fun setUp() {
- mDependency.injectTestDependency(
- StatusBarContentInsetsProvider::class.java,
- contentInsetsProvider
- )
mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
context.ensureTestableResources()
view = spy(createStatusBarView())
whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(Insets.NONE)
}
@Test
@@ -183,21 +178,40 @@
}
@Test
- fun onAttachedToWindow_updatesWindowHeight() {
+ @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onAttachedToWindow_flagOff_updatesWindowHeight() {
view.onAttachedToWindow()
verify(windowController).refreshStatusBarHeight()
}
@Test
- fun onConfigurationChanged_updatesWindowHeight() {
+ @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onAttachedToWindow_flagOn_doesNotUpdateWindowHeight() {
+ view.onAttachedToWindow()
+
+ verify(windowController, never()).refreshStatusBarHeight()
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onConfigurationChanged_flagOff_updatesWindowHeight() {
view.onConfigurationChanged(Configuration())
verify(windowController).refreshStatusBarHeight()
}
@Test
- fun onConfigurationChanged_multipleCalls_updatesWindowHeightMultipleTimes() {
+ @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onConfigurationChanged_flagOn_doesNotUpdateWindowHeight() {
+ view.onConfigurationChanged(Configuration())
+
+ verify(windowController, never()).refreshStatusBarHeight()
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onConfigurationChanged_multipleCalls_flagOff_updatesWindowHeightMultipleTimes() {
view.onConfigurationChanged(Configuration())
view.onConfigurationChanged(Configuration())
view.onConfigurationChanged(Configuration())
@@ -207,10 +221,20 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+ fun onConfigurationChanged_multipleCalls_flagOn_neverUpdatesWindowHeight() {
+ view.onConfigurationChanged(Configuration())
+ view.onConfigurationChanged(Configuration())
+ view.onConfigurationChanged(Configuration())
+ view.onConfigurationChanged(Configuration())
+
+ verify(windowController, never()).refreshStatusBarHeight()
+ }
+
+ @Test
fun onAttachedToWindow_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(insets)
+ view.setInsetsFetcher { insets }
view.onAttachedToWindow()
@@ -221,10 +245,23 @@
}
@Test
+ fun onAttachedToWindow_noInsetsFetcher_noCrash() {
+ // Don't call `PhoneStatusBarView.setInsetsFetcher`
+
+ // WHEN the view is attached
+ view.onAttachedToWindow()
+
+ // THEN there's no crash, and the padding stays as it was
+ assertThat(view.paddingLeft).isEqualTo(0)
+ assertThat(view.paddingTop).isEqualTo(0)
+ assertThat(view.paddingRight).isEqualTo(0)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
fun onConfigurationChanged_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(insets)
+ view.setInsetsFetcher { insets }
view.onConfigurationChanged(Configuration())
@@ -235,17 +272,31 @@
}
@Test
+ fun onConfigurationChanged_noInsetsFetcher_noCrash() {
+ // Don't call `PhoneStatusBarView.setInsetsFetcher`
+
+ // WHEN the view is attached
+ view.onConfigurationChanged(Configuration())
+
+ // THEN there's no crash, and the padding stays as it was
+ assertThat(view.paddingLeft).isEqualTo(0)
+ assertThat(view.paddingTop).isEqualTo(0)
+ assertThat(view.paddingRight).isEqualTo(0)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
fun onConfigurationChanged_noRelevantChange_doesNotUpdateInsets() {
val previousInsets =
Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(previousInsets)
+ view.setInsetsFetcher { previousInsets }
+
context.orCreateTestableResources.overrideConfiguration(Configuration())
view.onAttachedToWindow()
val newInsets = Insets.NONE
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ view.setInsetsFetcher { newInsets }
+
view.onConfigurationChanged(Configuration())
assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
@@ -258,16 +309,14 @@
fun onConfigurationChanged_densityChanged_updatesInsets() {
val previousInsets =
Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(previousInsets)
+ view.setInsetsFetcher { previousInsets }
val configuration = Configuration()
configuration.densityDpi = 123
context.orCreateTestableResources.overrideConfiguration(configuration)
view.onAttachedToWindow()
val newInsets = Insets.NONE
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ view.setInsetsFetcher { newInsets }
configuration.densityDpi = 456
view.onConfigurationChanged(configuration)
@@ -281,16 +330,14 @@
fun onConfigurationChanged_fontScaleChanged_updatesInsets() {
val previousInsets =
Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(previousInsets)
+ view.setInsetsFetcher { previousInsets }
val configuration = Configuration()
configuration.fontScale = 1f
context.orCreateTestableResources.overrideConfiguration(configuration)
view.onAttachedToWindow()
val newInsets = Insets.NONE
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ view.setInsetsFetcher { newInsets }
configuration.fontScale = 2f
view.onConfigurationChanged(configuration)
@@ -316,8 +363,7 @@
@Test
fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left= */ 90, /* top= */ 10, /* right= */ 45, /* bottom= */ 50)
- whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(insets)
+ view.setInsetsFetcher { insets }
view.onApplyWindowInsets(WindowInsets(Rect()))
@@ -358,7 +404,7 @@
/* typeVisibilityMap = */ booleanArrayOf(),
/* isRound = */ false,
/* forceConsumingTypes = */ 0,
- /* forceConsumingCaptionBar = */ false,
+ /* forceConsumingOpaqueCaptionBar = */ false,
/* suppressScrimTypes = */ 0,
/* displayCutout = */ DisplayCutout.NO_CUTOUT,
/* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 9fa392f..7a34e94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -434,7 +434,11 @@
// Then
verify(mBubblesManager).onUserChangedBubble(entry, false);
- verify(mHeadsUpManager).removeNotification(entry.getKey(), true);
+ verify(mHeadsUpManager).removeNotification(
+ entry.getKey(),
+ /* releaseImmediately= */ true,
+ /* reason= */ "onNotificationBubbleIconClicked"
+ );
verifyNoMoreInteractions(mContentIntent);
verifyNoMoreInteractions(mShadeController);
@@ -456,7 +460,11 @@
// Then
verify(mBubblesManager).onUserChangedBubble(entry, true);
- verify(mHeadsUpManager).removeNotification(entry.getKey(), true);
+ verify(mHeadsUpManager).removeNotification(
+ entry.getKey(),
+ /* releaseImmediately= */ true,
+ /* reason= */ "onNotificationBubbleIconClicked"
+ );
verify(mContentIntent, atLeastOnce()).isActivity();
verifyNoMoreInteractions(mContentIntent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
index bf0a39b..06b3b57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -57,6 +57,7 @@
private val activityStarter = kosmos.activityStarter
private val mockDialogTransitionAnimator = kosmos.mockDialogTransitionAnimator
private val mockAnimationController = kosmos.mockActivityTransitionAnimatorController
+ private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger
private lateinit var underTest: ModesDialogDelegate
@Before
@@ -75,6 +76,7 @@
mockDialogTransitionAnimator,
activityStarter,
{ kosmos.modesDialogViewModel },
+ mockDialogEventLogger,
kosmos.mainCoroutineContext,
)
}
@@ -121,4 +123,12 @@
assertThat(underTest.currentDialog).isNull()
}
+
+ @Test
+ fun openSettings_logsEvent() =
+ testScope.runTest {
+ val dialog: SystemUIDialog = mock()
+ underTest.openSettings(dialog)
+ verify(mockDialogEventLogger).logDialogSettings()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 8b7d921..4ea1a0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -229,6 +229,32 @@
}
@Test
+ public void testVolumeChangeW_inAudioSharing_doStateChanged() {
+ ArgumentCaptor<VolumeDialogController.State> stateCaptor =
+ ArgumentCaptor.forClass(VolumeDialogController.State.class);
+ mVolumeController.setDeviceInteractive(false);
+ when(mWakefullnessLifcycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ // For now, mAudioManager.getDevicesForStream returns DEVICE_NONE during audio sharing
+ when(mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC))
+ .thenReturn(AudioManager.DEVICE_NONE);
+
+ mVolumeController.mInAudioSharing = true;
+ mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+ verify(mCallback).onStateChanged(stateCaptor.capture());
+ assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
+ assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
+ .isTrue();
+
+ mVolumeController.mInAudioSharing = false;
+ mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+ verify(mCallback, times(2)).onStateChanged(stateCaptor.capture());
+ assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
+ assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
+ .isFalse();
+ }
+
+ @Test
public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
index ee48c10..2ab8221 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.domain.interactor
import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.log.communalSceneLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -24,6 +25,7 @@
Kosmos.Fixture {
CommunalSceneInteractor(
applicationScope = applicationCoroutineScope,
- communalSceneRepository = communalSceneRepository,
+ repository = communalSceneRepository,
+ logger = communalSceneLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt
new file mode 100644
index 0000000..b560ee8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.log
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+
+val Kosmos.communalSceneLogger: CommunalSceneLogger by
+ Kosmos.Fixture { CommunalSceneLogger(logcatLogBuffer("CommunalSceneLogger")) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index f162594..64ae051 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -38,6 +39,7 @@
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
glanceableHubTransitions = glanceableHubTransitions,
+ communalSceneInteractor = communalSceneInteractor,
communalSettingsInteractor = communalSettingsInteractor,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
index 450dcc2..d06bab2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
@@ -19,13 +19,11 @@
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
val Kosmos.dreamingToLockscreenTransitionViewModel by Fixture {
DreamingToLockscreenTransitionViewModel(
- fromDreamingTransitionInteractor = mock(),
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
index 9ff7dd5..ffe6918 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
@@ -27,6 +27,9 @@
numRunningPackages: Int = 0,
) : FgsManagerController {
+ var initialized = false
+ private set
+
override var numRunningPackages = numRunningPackages
set(value) {
if (value != field) {
@@ -53,7 +56,9 @@
dialogDismissedListeners.forEach { it.onDialogDismissed() }
}
- override fun init() {}
+ override fun init() {
+ initialized = true
+ }
override fun showDialog(expandable: Expandable?) {}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
new file mode 100644
index 0000000..d37d8f3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment.viewmodel
+
+import android.content.res.mainResources
+import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.footerActionsController
+import com.android.systemui.qs.footerActionsViewModelFactory
+import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModel
+import com.android.systemui.shade.largeScreenHeaderHelper
+import com.android.systemui.shade.transition.largeScreenShadeInterpolator
+import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.phone.keyguardBypassController
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+
+val Kosmos.qsFragmentComposeViewModelFactory by
+ Kosmos.Fixture {
+ object : QSFragmentComposeViewModel.Factory {
+ override fun create(
+ lifecycleScope: LifecycleCoroutineScope
+ ): QSFragmentComposeViewModel {
+ return QSFragmentComposeViewModel(
+ quickSettingsContainerViewModel,
+ mainResources,
+ footerActionsViewModelFactory,
+ footerActionsController,
+ sysuiStatusBarStateController,
+ keyguardBypassController,
+ disableFlagsRepository,
+ largeScreenShadeInterpolator,
+ configurationInteractor,
+ largeScreenHeaderHelper,
+ lifecycleScope,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
index 99bb479..932e768 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -33,6 +33,7 @@
dialogTransitionAnimator,
activityStarter,
{ modesDialogViewModel },
+ modesDialogEventLogger,
mainCoroutineContext,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt
new file mode 100644
index 0000000..24e7a87
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+var Kosmos.modesDialogEventLogger by Kosmos.Fixture { ModesDialogEventLogger(uiEventLogger) }
+var Kosmos.mockModesDialogEventLogger by Kosmos.Fixture { mock<ModesDialogEventLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
new file mode 100644
index 0000000..5146f77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSModesEvent
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class ModesDialogEventLoggerTest : SysuiTestCase() {
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private val underTest = ModesDialogEventLogger(uiEventLogger)
+
+ @Test
+ fun testLogModeOn_manual() {
+ underTest.logModeOn(TestModeBuilder.MANUAL_DND_INACTIVE)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_ON, "android")
+ }
+
+ @Test
+ fun testLogModeOff_manual() {
+ underTest.logModeOff(TestModeBuilder.MANUAL_DND_ACTIVE)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_OFF, "android")
+ }
+
+ @Test
+ fun testLogModeSettings_manual() {
+ underTest.logModeSettings(TestModeBuilder.MANUAL_DND_ACTIVE)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_SETTINGS, "android")
+ }
+
+ @Test
+ fun testLogModeOn_automatic() {
+ underTest.logModeOn(TestModeBuilder().setActive(true).setPackage("pkg1").build())
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_ON, "pkg1")
+ }
+
+ @Test
+ fun testLogModeOff_automatic() {
+ underTest.logModeOff(TestModeBuilder().setActive(false).setPackage("pkg2").build())
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_OFF, "pkg2")
+ }
+
+ @Test
+ fun testLogModeSettings_automatic() {
+ underTest.logModeSettings(TestModeBuilder().setPackage("pkg3").build())
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_SETTINGS, "pkg3")
+ }
+
+ @Test
+ fun testLogOpenDurationDialog_manual() {
+ underTest.logOpenDurationDialog(TestModeBuilder.MANUAL_DND_INACTIVE)
+
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ // package not logged for duration dialog as it only applies to manual mode
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_DURATION_DIALOG, null)
+ }
+
+ @Test
+ fun testLogOpenDurationDialog_automatic_doesNotLog() {
+ underTest.logOpenDurationDialog(
+ TestModeBuilder().setActive(false).setPackage("mypkg").build()
+ )
+
+ // ignore calls to open dialog on something other than the manual rule (shouldn't happen)
+ assertThat(uiEventLogger.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun testLogDialogSettings() {
+ underTest.logDialogSettings()
+ assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+ uiEventLogger[0].match(QSModesEvent.QS_MODES_SETTINGS, null)
+ }
+
+ private fun UiEventLoggerFake.FakeUiEvent.match(event: QSModesEvent, modePackage: String?) {
+ assertThat(eventId).isEqualTo(event.id)
+ assertThat(packageName).isEqualTo(modePackage)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
index 00020f8..3571a73 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
import javax.inject.Provider
val Kosmos.modesDialogViewModel: ModesDialogViewModel by
@@ -30,5 +31,6 @@
zenModeInteractor,
testDispatcher,
Provider { modesDialogDelegate }.get(),
+ modesDialogEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt
new file mode 100644
index 0000000..f502df0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+
+var Kosmos.touchpadGesturesInteractor: TouchpadGesturesInteractor by
+ Kosmos.Fixture {
+ TouchpadGesturesInteractor(sysUiState, displayTracker, testScope.backgroundScope)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.kt
new file mode 100644
index 0000000..5776203
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.coroutines
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * Overrides main dispatcher to passed testDispatcher. You probably want to use it when using
+ * viewModelScope which has hardcoded main dispatcher.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class MainDispatcherRule(val testDispatcher: TestDispatcher) : TestWatcher() {
+ override fun starting(description: Description) {
+ Dispatchers.setMain(testDispatcher)
+ }
+
+ override fun finished(description: Description) {
+ Dispatchers.resetMain()
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 295e150..2e1ecfd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -219,7 +219,7 @@
*
* NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
*/
-@Deprecated("Replace with mockito-kotlin", level = WARNING)
+// TODO(359670968): rewrite this to use mockito-kotlin
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
kotlinArgumentCaptor<T>().apply { block() }.value
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS
new file mode 100644
index 0000000..1f07df9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/volume/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index e2d414e..3ac565a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
@@ -22,7 +22,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.data.repository.audioSharingRepository
import com.android.systemui.volume.mediaOutputInteractor
val Kosmos.audioOutputInteractor by
@@ -37,6 +36,5 @@
bluetoothAdapter,
deviceIconInteractor,
mediaOutputInteractor,
- audioSharingRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
index 9f11822..63a1325 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.volume.domain.interactor.audioModeInteractor
import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputInteractor
@@ -31,5 +32,6 @@
audioOutputInteractor,
audioModeInteractor,
mediaOutputInteractor,
+ audioSharingInteractor,
)
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 2c4bc7c..531fa45 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -78,8 +78,8 @@
import android.util.Size;
import android.view.Surface;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index cc9b70e..639ebab 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -311,6 +311,7 @@
com.android.internal.util.QuickSelect
com.android.internal.util.RingBuffer
com.android.internal.util.SizedInputStream
+com.android.internal.util.RateLimitingCache
com.android.internal.util.StringPool
com.android.internal.util.TokenBucket
com.android.internal.util.XmlPullParserWrapper
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index b052d23..5c6f99a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -16,8 +16,6 @@
package com.android.server.accessibility.magnification;
-import static android.view.InputDevice.SOURCE_MOUSE;
-import static android.view.InputDevice.SOURCE_STYLUS;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
@@ -342,8 +340,12 @@
cancelFling();
}
handleTouchEventWith(mCurrentState, event, rawEvent, policyFlags);
- } else if (Flags.enableMagnificationFollowsMouse()
- && (event.getSource() == SOURCE_MOUSE || event.getSource() == SOURCE_STYLUS)) {
+ }
+ }
+
+ @Override
+ void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (Flags.enableMagnificationFollowsMouse()) {
if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
// TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
// over, rather than only interacting with the current display.
@@ -351,8 +353,6 @@
// Send through the mouse/stylus event handler.
mMouseEventHandler.onEvent(event, mDisplayId);
}
- // Dispatch to normal event handling flow.
- dispatchTransformedEvent(event, rawEvent, policyFlags);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 08411c2..446123f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -127,49 +127,41 @@
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugInputEventHistory, event);
}
- if (shouldDispatchTransformedEvent(event)) {
- dispatchTransformedEvent(event, rawEvent, policyFlags);
- } else {
- onMotionEventInternal(event, rawEvent, policyFlags);
+ switch (event.getSource()) {
+ case SOURCE_TOUCHSCREEN: {
+ if (magnificationShortcutExists()) {
+ // Observe touchscreen events while magnification activation is detected.
+ onMotionEventInternal(event, rawEvent, policyFlags);
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_DOWN) {
- mCallback.onTouchInteractionStart(mDisplayId, getMode());
- } else if (action == ACTION_UP || action == ACTION_CANCEL) {
- mCallback.onTouchInteractionEnd(mDisplayId, getMode());
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mCallback.onTouchInteractionStart(mDisplayId, getMode());
+ } else if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mCallback.onTouchInteractionEnd(mDisplayId, getMode());
+ }
+ // Return early: Do not dispatch event through normal eventing
+ // flow, it has been fully consumed by the magnifier.
+ return;
+ }
+ } break;
+ case SOURCE_MOUSE:
+ case SOURCE_STYLUS: {
+ if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) {
+ handleMouseOrStylusEvent(event, rawEvent, policyFlags);
+ }
}
+ break;
+ default:
+ break;
}
+ // Dispatch event through normal eventing flow.
+ dispatchTransformedEvent(event, rawEvent, policyFlags);
}
- /**
- * Some touchscreen, mouse and stylus events may modify magnifier state. Checks for whether the
- * event should not be dispatched to the magnifier.
- *
- * @param event The event to check.
- * @return `true` if the event should be sent through the normal event flow or `false` if it
- * should be observed by magnifier.
- */
- private boolean shouldDispatchTransformedEvent(MotionEvent event) {
- if (event.getSource() == SOURCE_TOUCHSCREEN) {
- if (mDetectSingleFingerTripleTap
- || mDetectTwoFingerTripleTap
- || mDetectShortcutTrigger) {
- // Observe touchscreen events while magnification activation is detected.
- return false;
- }
- }
- if (Flags.enableMagnificationFollowsMouse()) {
- if (event.isFromSource(SOURCE_MOUSE) || event.isFromSource(SOURCE_STYLUS)) {
- // Note that mouse events include other mouse-like pointing devices
- // such as touchpads and pointing sticks.
- // Observe any mouse or stylus movement.
- // We observe all movement to ensure that events continue to come in order,
- // even though only some movement types actually move the viewport.
- return false;
- }
- }
- // Magnification dispatches (ignores) all other events
- return true;
+ private boolean magnificationShortcutExists() {
+ return (mDetectSingleFingerTripleTap
+ || mDetectTwoFingerTripleTap
+ || mDetectShortcutTrigger);
}
final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
@@ -202,6 +194,13 @@
abstract void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags);
/**
+ * Called when this MagnificationGestureHandler should handle a mouse or stylus motion event,
+ * but not re-dispatch it when completed.
+ */
+ abstract void handleMouseOrStylusEvent(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags);
+
+ /**
* Called when the shortcut target is magnification.
*/
public void notifyShortcutTriggered() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 1818cdd..a841404 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -147,9 +147,13 @@
}
@Override
+ void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ // Window Magnification viewport doesn't move with mouse events (yet).
+ }
+
+ @Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (event.getSource() != SOURCE_TOUCHSCREEN) {
- // Window Magnification viewport doesn't move with mouse events (yet).
return;
}
// To keep InputEventConsistencyVerifiers within GestureDetectors happy.
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 930af5e..5044e93 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -71,6 +71,7 @@
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
import com.android.internal.util.FrameworkStatsLog;
@@ -244,10 +245,18 @@
Slog.e(TAG, "Failed to start new event because already have active event.");
return;
}
+ Slog.d(TAG, "Started new PresentationStatsEvent");
mEventInternal = Optional.of(new PresentationStatsEventInternal());
}
/**
+ * Test use only, returns a copy of the events object
+ */
+ Optional<PresentationStatsEventInternal> getInternalEvent() {
+ return mEventInternal;
+ }
+
+ /**
* Set request_id
*/
public void maybeSetRequestId(int requestId) {
@@ -339,10 +348,16 @@
});
}
- public void maybeSetCountShown(int datasets) {
+ /**
+ * This is called when a dataset is shown to the user. Will set the count shown,
+ * related timestamps and presentation reason.
+ */
+ public void logWhenDatasetShown(int datasets) {
mEventInternal.ifPresent(
event -> {
+ maybeSetSuggestionPresentedTimestampMs();
event.mCountShown = datasets;
+ event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
});
}
@@ -405,7 +420,12 @@
public void maybeSetDisplayPresentationType(@UiType int uiType) {
mEventInternal.ifPresent(event -> {
- event.mDisplayPresentationType = getDisplayPresentationType(uiType);
+ // There are cases in which another UI type will show up after selects a dataset
+ // such as with Inline after Fill Dialog. Set as the first presentation type only.
+ if (event.mDisplayPresentationType
+ == AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE) {
+ event.mDisplayPresentationType = getDisplayPresentationType(uiType);
+ }
});
}
@@ -430,9 +450,12 @@
}
public void maybeSetSuggestionSentTimestampMs(int timestamp) {
- mEventInternal.ifPresent(event -> {
- event.mSuggestionSentTimestampMs = timestamp;
- });
+ mEventInternal.ifPresent(
+ event -> {
+ if (event.mSuggestionSentTimestampMs == DEFAULT_VALUE_INT) {
+ event.mSuggestionSentTimestampMs = timestamp;
+ }
+ });
}
public void maybeSetSuggestionSentTimestampMs() {
@@ -481,8 +504,6 @@
public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
mEventInternal.ifPresent(event -> {
- event.mDisplayPresentationType =
- AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, userId);
if (TextUtils.isEmpty(imeString)) {
@@ -602,40 +623,56 @@
}
/**
+ * Sets the field length whenever the text changes. Will keep track of the first
+ * and last modification lengths.
+ */
+ public void updateTextFieldLength(AutofillValue value) {
+ mEventInternal.ifPresent(event -> {
+ if (value == null || !value.isText()) {
+ return;
+ }
+
+ int length = value.getTextValue().length();
+
+ if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
+ event.mFieldFirstLength = length;
+ }
+ event.mFieldLastLength = length;
+ });
+ }
+
+ /**
* Set various timestamps whenever the ViewState is modified
*
* <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms
* else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms
*/
- public void onFieldTextUpdated(ViewState state, int length) {
+ public void onFieldTextUpdated(ViewState state, AutofillValue value) {
mEventInternal.ifPresent(event -> {
- int timestamp = getElapsedTime();
- // Focused id should be set before this is called
- if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
- // if these don't match, the currently field different than before
- Slog.w(
- TAG,
- "Bad view state for: " + event.mFocusedId);
- return;
- }
+ int timestamp = getElapsedTime();
+ // Focused id should be set before this is called
+ if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
+ // if these don't match, the currently field different than before
+ Slog.w(
+ TAG,
+ "Bad view state for: " + event.mFocusedId + ", state: " + state);
+ return;
+ }
- // Text changed because filling into form, just log Autofill timestamp
- if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
- event.mAutofilledTimestampMs = timestamp;
- return;
- }
+ updateTextFieldLength(value);
- // Set length variables
- if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
- event.mFieldFirstLength = length;
- }
- event.mFieldLastLength = length;
+ // Text changed because filling into form, just log Autofill timestamp
+ if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
+ event.mAutofilledTimestampMs = timestamp;
+ return;
+ }
- // Set timestamp variables
- if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
- event.mFieldModifiedFirstTimestampMs = timestamp;
- }
- event.mFieldModifiedLastTimestampMs = timestamp;
+
+ // Set timestamp variables
+ if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
+ event.mFieldModifiedFirstTimestampMs = timestamp;
+ }
+ event.mFieldModifiedLastTimestampMs = timestamp;
});
}
@@ -796,7 +833,10 @@
});
}
- public void logAndEndEvent() {
+ /**
+ * Finish and log the event.
+ */
+ public void logAndEndEvent(String caller) {
if (!mEventInternal.isPresent()) {
Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
+ "event");
@@ -804,7 +844,8 @@
}
PresentationStatsEventInternal event = mEventInternal.get();
if (sVerbose) {
- Slog.v(TAG, "Log AutofillPresentationEventReported:"
+ Slog.v(TAG, "(" + caller + ") "
+ + "Log AutofillPresentationEventReported:"
+ " requestId=" + event.mRequestId
+ " sessionId=" + mSessionId
+ " mNoPresentationEventReason=" + event.mNoPresentationReason
@@ -926,7 +967,7 @@
mEventInternal = Optional.empty();
}
- private static final class PresentationStatsEventInternal {
+ static final class PresentationStatsEventInternal {
int mRequestId;
@NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
boolean mIsDatasetAvailable;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6dea8b0..c75fd0b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -28,7 +28,6 @@
import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
-import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
@@ -67,10 +66,10 @@
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SUCCESS;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TIMEOUT;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TRANSACTION_TOO_LARGE;
+import static com.android.server.autofill.Helper.SaveInfoStats;
import static com.android.server.autofill.Helper.containsCharsInOrder;
import static com.android.server.autofill.Helper.createSanitizers;
import static com.android.server.autofill.Helper.getNumericValue;
-import static com.android.server.autofill.Helper.SaveInfoStats;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
@@ -78,6 +77,7 @@
import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_SUCCESS;
import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_DATASET_AUTHENTICATION;
import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_FULL_AUTHENTICATION;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
@@ -1288,11 +1288,11 @@
* Clears the existing response for the partition, reads a new structure, and then requests a
* new fill response from the fill service.
*
- * <p> Also asks the IME to make an inline suggestions request if it's enabled.
+ * <p>Also asks the IME to make an inline suggestions request if it's enabled.
*/
@GuardedBy("mLock")
- private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
- int flags) {
+ private Optional<Integer> requestNewFillResponseLocked(
+ @NonNull ViewState viewState, int newState, int flags) {
boolean isSecondary = shouldRequestSecondaryProvider(flags);
final FillResponse existingResponse = isSecondary
? viewState.getSecondaryResponse() : viewState.getResponse();
@@ -1333,7 +1333,7 @@
mFillRequestEventLogger.maybeSetIsAugmented(true);
mFillRequestEventLogger.logAndEndEvent();
triggerAugmentedAutofillLocked(flags);
- return;
+ return Optional.empty();
}
viewState.setState(newState);
@@ -1353,11 +1353,6 @@
+ ", flags=" + flags);
}
boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
- mPresentationStatsEventLogger.maybeSetRequestId(requestId);
- mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
- mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
- mFieldClassificationIdSnapshot);
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
mFillRequestEventLogger.maybeSetRequestId(requestId);
mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
mSaveEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -1417,6 +1412,8 @@
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
+
+ return Optional.of(requestId);
}
private boolean isRequestSupportFillDialog(int flags) {
@@ -1662,6 +1659,7 @@
final LogMaker requestLog;
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetRequestId(requestId);
// Start a new FillResponse logger for the success case.
mFillResponseEventLogger.startLogForNewResponse();
mFillResponseEventLogger.maybeSetRequestId(requestId);
@@ -2419,7 +2417,7 @@
NOT_SHOWN_REASON_REQUEST_FAILED);
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_FAILURE);
}
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("fill request failure");
mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
mFillResponseEventLogger.logAndEndEvent();
}
@@ -2642,6 +2640,8 @@
public void onShown(int uiType, int numDatasetsShown) {
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetDisplayPresentationType(uiType);
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_ANY_SHOWN);
if (uiType == UI_TYPE_INLINE) {
// Inline Suggestions are inflated one at a time
@@ -2657,7 +2657,7 @@
}
mLoggedInlineDatasetShown = true;
} else {
- mPresentationStatsEventLogger.maybeSetCountShown(numDatasetsShown);
+ mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
// Explicitly sets maybeSetSuggestionPresentedTimestampMs
mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
mService.logDatasetShown(this.id, mClientState, uiType);
@@ -2800,7 +2800,10 @@
if (mCurrentViewId == null) {
return;
}
+ mPresentationStatsEventLogger.logAndEndEvent("fallback from fill dialog");
+ startNewEventForPresentationStatsEventLogger();
final ViewState currentView = mViewStates.get(mCurrentViewId);
+ logPresentationStatsOnViewEnteredLocked(currentView.getResponse(), false);
currentView.maybeCallOnFillReady(mFlags);
}
}
@@ -2850,7 +2853,7 @@
if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
// Augmented autofill is not logged.
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("authentication - augmented");
return;
}
if (mResponses == null) {
@@ -2859,7 +2862,7 @@
Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_FAILURE);
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("authentication - no response");
removeFromService();
return;
}
@@ -2870,7 +2873,7 @@
Slog.w(TAG, "no authenticated response");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_FAILURE);
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("authentication - bad response");
removeFromService();
return;
}
@@ -2885,7 +2888,7 @@
Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_FAILURE);
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("authentication - no datasets");
removeFromService();
return;
}
@@ -3330,7 +3333,7 @@
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("Context committed");
final int flags = lastResponse.getFlags();
if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
@@ -4299,6 +4302,7 @@
* Starts (if necessary) a new fill request upon entering a view.
*
* <p>A new request will be started in 2 scenarios:
+ *
* <ol>
* <li>If the user manually requested autofill.
* <li>If the view is part of a new partition.
@@ -4307,18 +4311,17 @@
* @param id The id of the view that is entered.
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
- *
* @return {@code true} if a new fill response is requested.
*/
@GuardedBy("mLock")
- private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
- @NonNull ViewState viewState, int flags) {
+ private Optional<Integer> requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+ @NonNull AutofillId id, @NonNull ViewState viewState, int flags) {
// Force new response for manual request
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
mSessionFlags.mAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
- requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
- return true;
+ return requestNewFillResponseLocked(
+ viewState, ViewState.STATE_RESTARTED_SESSION, flags);
}
// If it's not, then check if it should start a partition.
@@ -4331,15 +4334,15 @@
// Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as
// augmentedOnly, but other fields are still fillable by standard autofill.
mSessionFlags.mAugmentedAutofillOnly = false;
- requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
- return true;
+ return requestNewFillResponseLocked(
+ viewState, ViewState.STATE_STARTED_PARTITION, flags);
}
if (sVerbose) {
Slog.v(TAG, "Not starting new partition for view " + id + ": "
+ viewState.getStateAsString());
}
- return false;
+ return Optional.empty();
}
/**
@@ -4428,31 +4431,32 @@
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
int flags) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#updateLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(TAG, "updateLocked(" + id + "): rejected - session: destroyed");
return;
}
if (action == ACTION_RESPONSE_EXPIRED) {
mSessionFlags.mExpiredResponse = true;
if (sDebug) {
- Slog.d(TAG, "Set the response has expired.");
+ Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired.");
}
mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
NOT_SHOWN_REASON_VIEW_CHANGED);
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED");
return;
}
id.setSessionId(this.id);
- if (sVerbose) {
- Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
- + actionAsString(action) + ", flags=" + flags);
- }
ViewState viewState = mViewStates.get(id);
if (sVerbose) {
- Slog.v(TAG, "updateLocked(" + this.id + "): mCurrentViewId=" + mCurrentViewId
- + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
- + ", viewState=" + viewState);
+ Slog.v(
+ TAG,
+ "updateLocked(" + id + "): "
+ + "id=" + this.id
+ + ", action=" + actionAsString(action)
+ + ", flags=" + flags
+ + ", mCurrentViewId=" + mCurrentViewId
+ + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
+ + ", viewState=" + viewState);
}
if (viewState == null) {
@@ -4505,14 +4509,14 @@
mSessionFlags.mFillDialogDisabled = true;
mPreviouslyFillDialogPotentiallyStarted = false;
} else {
- // Set the default reason for now if the user doesn't trigger any focus event
- // on the autofillable view. This can be changed downstream when more
- // information is available or session is committed.
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_NO_FOCUS);
mPreviouslyFillDialogPotentiallyStarted = true;
}
- requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
+ Optional<Integer> maybeRequestId =
+ requestNewFillResponseLocked(
+ viewState, ViewState.STATE_STARTED_SESSION, flags);
+ if (maybeRequestId.isPresent()) {
+ mPresentationStatsEventLogger.maybeSetRequestId(maybeRequestId.get());
+ }
break;
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
@@ -4577,8 +4581,10 @@
}
boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
if (shouldRequestSecondaryProvider(flags)) {
- if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
- id, viewState, flags)) {
+ Optional<Integer> maybeRequestIdCred =
+ requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+ id, viewState, flags);
+ if (maybeRequestIdCred.isPresent()) {
Slog.v(TAG, "Started a new fill request for secondary provider.");
return;
}
@@ -4622,17 +4628,7 @@
mLogViewEntered = true;
}
- // Previously, fill request will only start whenever a view is entered.
- // With Fill Dialog, request starts prior to view getting entered. So, we can't end
- // the event at this moment, otherwise we will be wrongly attributing fill dialog
- // event as concluded.
- if (!wasPreviouslyFillDialog && !isSameViewAgain) {
- // TODO(b/319872477): Re-consider this logic below
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
- mPresentationStatsEventLogger.logAndEndEvent();
- }
-
+ // Trigger augmented autofill if applicable
if ((flags & FLAG_MANUAL_REQUEST) == 0) {
// Not a manual request
if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
@@ -4658,26 +4654,25 @@
return;
}
}
- // If previous request was FillDialog request, a logger event was already started
- if (!wasPreviouslyFillDialog) {
+
+ Optional<Integer> maybeNewRequestId =
+ requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
+
+ // Previously, fill request will only start whenever a view is entered.
+ // With Fill Dialog, request starts prior to view getting entered. So, we can't end
+ // the event at this moment, otherwise we will be wrongly attributing fill dialog
+ // event as concluded.
+ if (!wasPreviouslyFillDialog
+ && (!isSameViewEntered || maybeNewRequestId.isPresent())) {
startNewEventForPresentationStatsEventLogger();
- }
- if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
- // If a new request was issued even if previously it was fill dialog request,
- // we should end the log event, and start a new one. However, it leaves us
- // susceptible to race condition. But since mPresentationStatsEventLogger is
- // lock guarded, we should be safe.
- if (wasPreviouslyFillDialog) {
- mPresentationStatsEventLogger.logAndEndEvent();
- startNewEventForPresentationStatsEventLogger();
+ if (maybeNewRequestId.isPresent()) {
+ mPresentationStatsEventLogger.maybeSetRequestId(maybeNewRequestId.get());
}
- return;
}
- FillResponse response = viewState.getResponse();
- if (response != null) {
- logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
- }
+ logPresentationStatsOnViewEnteredLocked(
+ viewState.getResponse(), isCredmanRequested);
+ mPresentationStatsEventLogger.updateTextFieldLength(value);
if (isSameViewEntered) {
setFillDialogDisabledAndStartInput();
@@ -4719,13 +4714,17 @@
@GuardedBy("mLock")
private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
boolean isCredmanRequested) {
- mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
mFieldClassificationIdSnapshot);
- mPresentationStatsEventLogger.maybeSetAvailableCount(
- response.getDatasets(), mCurrentViewId);
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
mPresentationStatsEventLogger.maybeSetFocusedId(mCurrentViewId);
+
+ if (response != null) {
+ mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetAvailableCount(
+ response.getDatasets(), mCurrentViewId);
+ }
}
@GuardedBy("mLock")
@@ -4796,8 +4795,12 @@
viewState.setCurrentValue(value);
final String filterText = textValue;
-
final AutofillValue filledValue = viewState.getAutofilledValue();
+
+ if (textValue != null) {
+ mPresentationStatsEventLogger.onFieldTextUpdated(viewState, value);
+ }
+
if (filledValue != null) {
if (filledValue.equals(value)) {
// When the update is caused by autofilling the view, just update the
@@ -4821,9 +4824,6 @@
currentView.maybeCallOnFillReady(flags);
}
}
- if (textValue != null) {
- mPresentationStatsEventLogger.onFieldTextUpdated(viewState, textValue.length());
- }
if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
@@ -4888,7 +4888,7 @@
mSaveEventLogger.logAndEndEvent();
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("on fill ready");
return;
}
}
@@ -4920,7 +4920,6 @@
synchronized (mLock) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
- mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_DIALOG);
}
// Just show fill dialog once, so disabled after shown.
// Note: Cannot disable before requestShowFillDialog() because the method
@@ -6086,6 +6085,11 @@
private void startNewEventForPresentationStatsEventLogger() {
synchronized (mLock) {
mPresentationStatsEventLogger.startNewEvent();
+ // Set the default reason for now if the user doesn't trigger any focus event
+ // on the autofillable view. This can be changed downstream when more
+ // information is available or session is committed.
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_NO_FOCUS);
mPresentationStatsEventLogger.maybeSetDetectionPreference(
getDetectionPreferenceForLogging());
mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -6724,7 +6728,7 @@
SystemClock.elapsedRealtime() - mStartTime);
mFillRequestEventLogger.logAndEndEvent();
mFillResponseEventLogger.logAndEndEvent();
- mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent("log all events");
mSaveEventLogger.logAndEndEvent();
mSessionCommittedEventLogger.logAndEndEvent();
}
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 a10039f..2446a6d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -461,9 +461,9 @@
}
@Override
- public void onShown() {
+ public void onShown(int datasetsShown) {
if (mCallback != null) {
- mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
+ mCallback.onShown(UI_TYPE_DIALOG, datasetsShown);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index 5a71b89..c7b6be6 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -85,7 +85,7 @@
void onDatasetPicked(@NonNull Dataset dataset);
void onDismissed();
void onCanceled();
- void onShown();
+ void onShown(int datasetsShown);
void startIntentSender(IntentSender intentSender);
}
@@ -155,7 +155,8 @@
mDialog.setContentView(decor);
setDialogParamsAsBottomSheet();
mDialog.setOnCancelListener((d) -> mCallback.onCanceled());
- mDialog.setOnShowListener((d) -> mCallback.onShown());
+ int datasetsShown = (mAdapter != null) ? mAdapter.getCount() : 0;
+ mDialog.setOnShowListener((d) -> mCallback.onShown(datasetsShown));
show();
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a2bbff0..37dddc6 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -10,9 +10,6 @@
# Zram writeback
per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
-# Userspace reboot
-per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com
-
# ServiceWatcher
per-file ServiceWatcher.java = sooniln@google.com
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
deleted file mode 100644
index 89327b5..0000000
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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 com.android.server;
-
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED;
-
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.util.concurrent.Executor;
-
-/**
- * Utility class to help abstract logging {@code UserspaceRebootReported} atom.
- */
-public final class UserspaceRebootLogger {
-
- private static final String TAG = "UserspaceRebootLogger";
-
- private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY =
- "persist.sys.userspace_reboot.log.should_log";
- private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY =
- "sys.userspace_reboot.log.last_started";
- private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
- "sys.userspace_reboot.log.last_finished";
- private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last";
-
- private UserspaceRebootLogger() {}
-
- /**
- * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
- * logged on the next successful boot.
- *
- * <p>This call should only be made on devices supporting userspace reboot.
- */
- public static void noteUserspaceRebootWasRequested() {
- if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
- Slog.wtf(TAG, "noteUserspaceRebootWasRequested: Userspace reboot is not supported.");
- return;
- }
-
- SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
- SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
- String.valueOf(SystemClock.elapsedRealtime()));
- }
-
- /**
- * Updates internal state on boot after successful userspace reboot.
- *
- * <p>Should be called right before framework sets {@code sys.boot_completed} property.
- *
- * <p>This call should only be made on devices supporting userspace reboot.
- */
- public static void noteUserspaceRebootSuccess() {
- if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
- Slog.wtf(TAG, "noteUserspaceRebootSuccess: Userspace reboot is not supported.");
- return;
- }
-
- SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
- String.valueOf(SystemClock.elapsedRealtime()));
- }
-
- /**
- * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
- *
- * <p>On devices that do not support userspace reboot this method will always return {@code
- * false}.
- */
- public static boolean shouldLogUserspaceRebootEvent() {
- if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
- return false;
- }
-
- return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
- }
-
- /**
- * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}.
- *
- * <p>Should be called in the end of {@link
- * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have
- * tried to proactivelly unlock storage of the primary user.
- *
- * <p>This call should only be made on devices supporting userspace reboot.
- */
- public static void logEventAsync(boolean userUnlocked, Executor executor) {
- if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
- Slog.wtf(TAG, "logEventAsync: Userspace reboot is not supported.");
- return;
- }
-
- final int outcome = computeOutcome();
- final long durationMillis;
- if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
- durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0)
- - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0);
- } else {
- durationMillis = 0;
- }
- final int encryptionState =
- userUnlocked
- ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED
- : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
- executor.execute(
- () -> {
- Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome
- + " durationMillis: " + durationMillis + " encryptionState: "
- + encryptionState + " }");
- FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome,
- durationMillis, encryptionState);
- SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "");
- });
- }
-
- private static int computeOutcome() {
- if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
- }
- String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, ""));
- if (reason.startsWith("reboot,")) {
- reason = reason.substring("reboot".length());
- }
- if (reason.startsWith("userspace_failed,watchdog_fork")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
- }
- if (reason.startsWith("userspace_failed,shutdown_aborted")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
- }
- if (reason.startsWith("mount_userdata_failed")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
- }
- if (reason.startsWith("userspace_failed,init_user0")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
- }
- if (reason.startsWith("userspace_failed,enablefilecrypto")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
- }
- if (reason.startsWith("userspace_failed,watchdog_triggered")) {
- return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
- }
- return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
- }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4a18cb1..91b549c9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -372,7 +372,6 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.ServerProtoEnums;
-import android.sysprop.InitProperties;
import android.system.Os;
import android.system.OsConstants;
import android.telephony.TelephonyManager;
@@ -451,7 +450,6 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
-import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.ComponentAliasResolver.Resolution;
import com.android.server.am.LowMemDetector.MemFactor;
@@ -2360,20 +2358,6 @@
}
}
- private void maybeLogUserspaceRebootEvent() {
- if (!UserspaceRebootLogger.shouldLogUserspaceRebootEvent()) {
- return;
- }
- final int userId = mUserController.getCurrentUserId();
- if (userId != UserHandle.USER_SYSTEM) {
- // Only log for user0.
- return;
- }
- // TODO(b/148767783): should we check all profiles under user0?
- UserspaceRebootLogger.logEventAsync(StorageManager.isCeStorageUnlocked(userId),
- BackgroundThread.getExecutor());
- }
-
/**
* Encapsulates global settings related to hidden API enforcement behaviour, including tracking
* the latest value via a content observer.
@@ -5323,12 +5307,6 @@
// Start looking for apps that are abusing wake locks.
Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
- // Check if we are performing userspace reboot before setting sys.boot_completed to
- // avoid race with init reseting sys.init.userspace_reboot.in_progress once sys
- // .boot_completed is 1.
- if (InitProperties.userspace_reboot_in_progress().orElse(false)) {
- UserspaceRebootLogger.noteUserspaceRebootSuccess();
- }
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
@@ -5352,7 +5330,6 @@
}, mConstants.FULL_PSS_MIN_INTERVAL);
}
});
- maybeLogUserspaceRebootEvent();
mUserController.scheduleStartProfiles();
}
// UART is on if init's console service is running, send a warning notification.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index bd27f47..ab79713 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -492,6 +492,11 @@
// Used to scale the brightness in doze mode
private float mDozeScaleFactor;
+ // Used to keep track of the display state from the latest request to override the doze screen
+ // state.
+ @GuardedBy("mLock")
+ private int mPendingOverrideDozeScreenStateLocked;
+
/**
* Creates the display power controller.
*/
@@ -803,15 +808,28 @@
@Override
public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) {
Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState));
- mHandler.postAtTime(() -> {
- if (mDisplayOffloadSession == null
- || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
- || displayState == Display.STATE_UNKNOWN)) {
- return;
+ if (mDisplayOffloadSession != null
+ && (DisplayOffloadSession.isSupportedOffloadState(displayState)
+ || displayState == Display.STATE_UNKNOWN)) {
+ if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+ mWakelockController.acquireWakelock(
+ WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
}
- mDisplayStateController.overrideDozeScreenState(displayState, reason);
- updatePowerState();
- }, mClock.uptimeMillis());
+ synchronized (mLock) {
+ mPendingOverrideDozeScreenStateLocked = displayState;
+ }
+ mHandler.postAtTime(() -> {
+ synchronized (mLock) {
+ mDisplayStateController
+ .overrideDozeScreenState(mPendingOverrideDozeScreenStateLocked, reason);
+ }
+ updatePowerState();
+ if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+ mWakelockController.releaseWakelock(
+ WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
+ }
+ }, mClock.uptimeMillis());
+ }
}
@Override
@@ -1338,30 +1356,6 @@
initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
}
- if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
- // Sometimes, a display-state change can come without an associated PowerRequest,
- // as with DisplayOffload. For those cases, we have to make sure to also mark the
- // display as "not ready" so that we can inform power-manager when the state-change is
- // complete.
- if (mPowerState.getScreenState() != state) {
- final boolean wasReady;
- synchronized (mLock) {
- wasReady = mDisplayReadyLocked;
- mDisplayReadyLocked = false;
- mustNotify = true;
- }
-
- if (wasReady) {
- // If we went from ready to not-ready from the state-change (instead of a
- // PowerRequest) there's a good chance that nothing is keeping PowerManager
- // from suspending. Grab the unfinished business suspend blocker to keep the
- // device awake until the display-state change goes into effect.
- mWakelockController.acquireWakelock(
- WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
- }
- }
- }
-
// Animate the screen state change unless already animating.
// The transition may be deferred, so after this point we will use the
// actual state instead of the desired one.
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 7bc7971..5b0229c 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -20,6 +20,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.utils.DebugUtils;
@@ -37,7 +38,8 @@
public static final int WAKE_LOCK_PROXIMITY_NEGATIVE = 2;
public static final int WAKE_LOCK_PROXIMITY_DEBOUNCE = 3;
public static final int WAKE_LOCK_STATE_CHANGED = 4;
- public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5;
+ public static final int WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE = 5;
+ public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 6;
@VisibleForTesting
static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
@@ -53,18 +55,23 @@
WAKE_LOCK_PROXIMITY_NEGATIVE,
WAKE_LOCK_PROXIMITY_DEBOUNCE,
WAKE_LOCK_STATE_CHANGED,
+ WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
WAKE_LOCK_UNFINISHED_BUSINESS
})
@Retention(RetentionPolicy.SOURCE)
public @interface WAKE_LOCK_TYPE {
}
+ private final Object mLock = new Object();
+
// Asynchronous callbacks into the power manager service.
// Only invoked from the handler thread while no locks are held.
private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks;
// Identifiers for suspend blocker acquisition requests
private final String mSuspendBlockerIdUnfinishedBusiness;
+ @GuardedBy("mLock")
+ private final String mSuspendBlockerOverrideDozeScreenState;
private final String mSuspendBlockerIdOnStateChanged;
private final String mSuspendBlockerIdProxPositive;
private final String mSuspendBlockerIdProxNegative;
@@ -73,6 +80,10 @@
// True if we have unfinished business and are holding a suspend-blocker.
private boolean mUnfinishedBusiness;
+ // True if we have are holding a suspend-blocker to override the doze screen state.
+ @GuardedBy("mLock")
+ private boolean mIsOverrideDozeScreenStateAcquired;
+
// True if we have have debounced the proximity change impact and are holding a suspend-blocker.
private boolean mHasProximityDebounced;
@@ -108,6 +119,7 @@
mTag = TAG + "[" + mDisplayId + "]";
mDisplayPowerCallbacks = callbacks;
mSuspendBlockerIdUnfinishedBusiness = "[" + displayId + "]unfinished business";
+ mSuspendBlockerOverrideDozeScreenState = "[" + displayId + "]override doze screen state";
mSuspendBlockerIdOnStateChanged = "[" + displayId + "]on state changed";
mSuspendBlockerIdProxPositive = "[" + displayId + "]prox positive";
mSuspendBlockerIdProxNegative = "[" + displayId + "]prox negative";
@@ -154,6 +166,10 @@
return acquireProxDebounceSuspendBlocker();
case WAKE_LOCK_STATE_CHANGED:
return acquireStateChangedSuspendBlocker();
+ case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+ synchronized (mLock) {
+ return acquireOverrideDozeScreenStateSuspendBlockerLocked();
+ }
case WAKE_LOCK_UNFINISHED_BUSINESS:
return acquireUnfinishedBusinessSuspendBlocker();
default:
@@ -171,6 +187,10 @@
return releaseProxDebounceSuspendBlocker();
case WAKE_LOCK_STATE_CHANGED:
return releaseStateChangedSuspendBlocker();
+ case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+ synchronized (mLock) {
+ return releaseOverrideDozeScreenStateSuspendBlockerLocked();
+ }
case WAKE_LOCK_UNFINISHED_BUSINESS:
return releaseUnfinishedBusinessSuspendBlocker();
default:
@@ -220,6 +240,42 @@
}
/**
+ * Acquires the suspend blocker to override the doze screen state and notifies the
+ * PowerManagerService about the changes. Note that this utility is syncronized because a
+ * request to override the doze screen state can come from a non-power thread.
+ */
+ @GuardedBy("mLock")
+ private boolean acquireOverrideDozeScreenStateSuspendBlockerLocked() {
+ // Grab a wake lock if we have unfinished business.
+ if (!mIsOverrideDozeScreenStateAcquired) {
+ if (DEBUG) {
+ Slog.d(mTag, "Acquiring suspend blocker to override the doze screen state...");
+ }
+ mDisplayPowerCallbacks.acquireSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+ mIsOverrideDozeScreenStateAcquired = true;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Releases the override doze screen state suspend blocker and notifies the PowerManagerService
+ * about the changes.
+ */
+ @GuardedBy("mLock")
+ private boolean releaseOverrideDozeScreenStateSuspendBlockerLocked() {
+ if (mIsOverrideDozeScreenStateAcquired) {
+ if (DEBUG) {
+ Slog.d(mTag, "Finished overriding doze screen state...");
+ }
+ mDisplayPowerCallbacks.releaseSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+ mIsOverrideDozeScreenStateAcquired = false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Acquires the unfinished business wakelock and notifies the PowerManagerService about the
* changes.
*/
@@ -366,6 +422,7 @@
pw.println(" mOnStateChangePending=" + isOnStateChangedPending());
pw.println(" mOnProximityPositiveMessages=" + isProximityPositiveAcquired());
pw.println(" mOnProximityNegativeMessages=" + isProximityNegativeAcquired());
+ pw.println(" mIsOverrideDozeScreenStateAcquired=" + isOverrideDozeScreenStateAcquired());
}
@VisibleForTesting
@@ -394,6 +451,13 @@
}
@VisibleForTesting
+ String getSuspendBlockerOverrideDozeScreenState() {
+ synchronized (mLock) {
+ return mSuspendBlockerOverrideDozeScreenState;
+ }
+ }
+
+ @VisibleForTesting
boolean hasUnfinishedBusiness() {
return mUnfinishedBusiness;
}
@@ -417,4 +481,11 @@
boolean hasProximitySensorDebounced() {
return mHasProximityDebounced;
}
+
+ @VisibleForTesting
+ boolean isOverrideDozeScreenStateAcquired() {
+ synchronized (mLock) {
+ return mIsOverrideDozeScreenStateAcquired;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 1938642..e2889fa 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -29,8 +29,8 @@
import android.media.AudioAttributes;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Slog;
+
import com.android.internal.compat.IPlatformCompat;
/**
@@ -79,6 +79,11 @@
if (restrictAudioAttributesCall() || restrictAudioAttributesAlarm()
|| restrictAudioAttributesMedia()) {
AudioAttributes attributes = record.getChannel().getAudioAttributes();
+ if (attributes == null) {
+ if (DBG) Slog.d(TAG, "missing AudioAttributes");
+ return null;
+ }
+
boolean updateAttributes = false;
if (restrictAudioAttributesCall()
&& !record.getNotification().isStyle(Notification.CallStyle.class)
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 22b4d5d..5105fd3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1399,7 +1399,29 @@
"Package " + pkgName + " is a persistent app. "
+ "Persistent apps are not updateable.");
}
+ // When updating an sdk library, make sure that the versionMajor is
+ // changed if the targetSdkVersion and minSdkVersion have changed
+ if (parsedPackage.isSdkLibrary() && ps.getPkg() != null
+ && ps.getPkg().isSdkLibrary()) {
+ final int oldMinSdk = ps.getPkg().getMinSdkVersion();
+ final int newMinSdk = parsedPackage.getMinSdkVersion();
+ if (oldTargetSdk != newTargetSdk || oldMinSdk != newMinSdk) {
+ final int oldVersionMajor = ps.getPkg().getSdkLibVersionMajor();
+ final int newVersionMajor = parsedPackage.getSdkLibVersionMajor();
+ if (oldVersionMajor == newVersionMajor) {
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Failure updating " + pkgName + " as it updates"
+ + " an sdk library <"
+ + parsedPackage.getSdkLibraryName() + ">"
+ + " without changing the versionMajor, but the"
+ + " targetSdkVersion or minSdkVersion has changed."
+ );
+ }
+ }
+ }
}
+
}
PackageSetting signatureCheckPs = ps;
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index c95d88e..2c13bd0 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -165,7 +165,55 @@
"name": "CtsUpdateOwnershipEnforcementTestCases"
},
{
- "name": "CtsPackageInstallerCUJTestCases",
+ "name": "CtsPackageInstallerCUJInstallationTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUninstallationTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options":[
+ {
+ "exclude-annotation":"androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation":"org.junit.Ignore"
+ }
+ ]
+ },
+ {
+ "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
"file_patterns": [
"core/java/.*Install.*",
"services/core/.*Install.*",
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b22b726..27024a7 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -101,7 +101,6 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
-import android.sysprop.InitProperties;
import android.sysprop.PowerProperties;
import android.util.ArrayMap;
import android.util.IntArray;
@@ -132,7 +131,6 @@
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.UiThread;
-import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.feature.DeviceConfigParameterProvider;
@@ -1274,8 +1272,7 @@
mHalInteractiveModeEnabled = true;
mWakefulnessRaw = WAKEFULNESS_AWAKE;
- sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1")
- || InitProperties.userspace_reboot_in_progress().orElse(false);
+ sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
mNativeWrapper.nativeInit(this);
mNativeWrapper.nativeSetAutoSuspend(false);
@@ -4032,7 +4029,6 @@
throw new UnsupportedOperationException(
"Attempted userspace reboot on a device that doesn't support it");
}
- UserspaceRebootLogger.noteUserspaceRebootWasRequested();
}
if (mHandler == null || !mSystemReady) {
if (RescueParty.isRecoveryTriggeredReboot()) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 8e37527..0962319 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -67,19 +67,19 @@
/**
* The handle of the primary frontend resource
*/
- private int mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ private long mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
/**
* List of the frontend handles that are used by the current client.
*/
- private Set<Integer> mUsingFrontendHandles = new HashSet<>();
+ private Set<Long> mUsingFrontendHandles = new HashSet<>();
/**
* List of the client ids that share frontend with the current client.
*/
private Set<Integer> mShareFeClientIds = new HashSet<>();
- private Set<Integer> mUsingDemuxHandles = new HashSet<>();
+ private Set<Long> mUsingDemuxHandles = new HashSet<>();
/**
* Client id sharee that has shared frontend with the current client.
@@ -89,7 +89,7 @@
/**
* List of the Lnb handles that are used by the current client.
*/
- private Set<Integer> mUsingLnbHandles = new HashSet<>();
+ private Set<Long> mUsingLnbHandles = new HashSet<>();
/**
* List of the Cas system ids that are used by the current client.
@@ -184,7 +184,7 @@
*
* @param frontendHandle being used.
*/
- public void useFrontend(int frontendHandle) {
+ public void useFrontend(long frontendHandle) {
mUsingFrontendHandles.add(frontendHandle);
}
@@ -193,14 +193,14 @@
*
* @param frontendHandle being used.
*/
- public void setPrimaryFrontend(int frontendHandle) {
+ public void setPrimaryFrontend(long frontendHandle) {
mPrimaryUsingFrontendHandle = frontendHandle;
}
/**
* Get the primary frontend used by the client
*/
- public int getPrimaryFrontend() {
+ public long getPrimaryFrontend() {
return mPrimaryUsingFrontendHandle;
}
@@ -222,7 +222,7 @@
mShareFeClientIds.remove(clientId);
}
- public Set<Integer> getInUseFrontendHandles() {
+ public Set<Long> getInUseFrontendHandles() {
return mUsingFrontendHandles;
}
@@ -253,14 +253,14 @@
*
* @param demuxHandle the demux being used.
*/
- public void useDemux(int demuxHandle) {
+ public void useDemux(long demuxHandle) {
mUsingDemuxHandles.add(demuxHandle);
}
/**
* Get the set of demux handles in use.
*/
- public Set<Integer> getInUseDemuxHandles() {
+ public Set<Long> getInUseDemuxHandles() {
return mUsingDemuxHandles;
}
@@ -269,7 +269,7 @@
*
* @param demuxHandle the demux handl being released.
*/
- public void releaseDemux(int demuxHandle) {
+ public void releaseDemux(long demuxHandle) {
mUsingDemuxHandles.remove(demuxHandle);
}
@@ -278,11 +278,11 @@
*
* @param lnbHandle being used.
*/
- public void useLnb(int lnbHandle) {
+ public void useLnb(long lnbHandle) {
mUsingLnbHandles.add(lnbHandle);
}
- public Set<Integer> getInUseLnbHandles() {
+ public Set<Long> getInUseLnbHandles() {
return mUsingLnbHandles;
}
@@ -291,7 +291,7 @@
*
* @param lnbHandle being released.
*/
- public void releaseLnb(int lnbHandle) {
+ public void releaseLnb(long lnbHandle) {
mUsingLnbHandles.remove(lnbHandle);
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
index df73565..14bc216 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
@@ -69,7 +69,7 @@
public static class Builder extends TunerResourceBasic.Builder {
private int mFilterTypes;
- Builder(int handle) {
+ Builder(long handle) {
super(handle);
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
index 7ef75e3..953d974 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -42,7 +42,7 @@
/**
* An array to save all the FE handles under the same exclisive group.
*/
- private Set<Integer> mExclusiveGroupMemberHandles = new HashSet<>();
+ private Set<Long> mExclusiveGroupMemberHandles = new HashSet<>();
private FrontendResource(Builder builder) {
super(builder);
@@ -58,7 +58,7 @@
return mExclusiveGroupId;
}
- public Set<Integer> getExclusiveGroupMemberFeHandles() {
+ public Set<Long> getExclusiveGroupMemberFeHandles() {
return mExclusiveGroupMemberHandles;
}
@@ -67,7 +67,7 @@
*
* @param handle the handle to be added.
*/
- public void addExclusiveGroupMemberFeHandle(int handle) {
+ public void addExclusiveGroupMemberFeHandle(long handle) {
mExclusiveGroupMemberHandles.add(handle);
}
@@ -76,7 +76,7 @@
*
* @param handles the handle collection to be added.
*/
- public void addExclusiveGroupMemberFeHandles(Collection<Integer> handles) {
+ public void addExclusiveGroupMemberFeHandles(Collection<Long> handles) {
mExclusiveGroupMemberHandles.addAll(handles);
}
@@ -85,7 +85,7 @@
*
* @param id the id to be removed.
*/
- public void removeExclusiveGroupMemberFeId(int handle) {
+ public void removeExclusiveGroupMemberFeId(long handle) {
mExclusiveGroupMemberHandles.remove(handle);
}
@@ -104,7 +104,7 @@
@Type private int mType;
private int mExclusiveGroupId;
- Builder(int handle) {
+ Builder(long handle) {
super(handle);
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
index 41cacea..ab28371 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
@@ -37,8 +37,7 @@
* Builder class for {@link LnbResource}.
*/
public static class Builder extends TunerResourceBasic.Builder {
-
- Builder(int handle) {
+ Builder(long handle) {
super(handle);
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
index 07853fc..d2ff8fa 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
@@ -28,7 +28,7 @@
* Handle of the current resource. Should not be changed and should be aligned with the driver
* level implementation.
*/
- final int mHandle;
+ final long mHandle;
/**
* If the current resource is in use.
@@ -44,7 +44,7 @@
this.mHandle = builder.mHandle;
}
- public int getHandle() {
+ public long getHandle() {
return mHandle;
}
@@ -78,9 +78,9 @@
* Builder class for {@link TunerResourceBasic}.
*/
public static class Builder {
- private final int mHandle;
+ private final long mHandle;
- Builder(int handle) {
+ Builder(long handle) {
this.mHandle = handle;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 0afb049..45a40ed 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -78,12 +78,18 @@
private static final int INVALID_FE_COUNT = -1;
+ private static final int RESOURCE_ID_SHIFT = 24;
+ private static final int RESOURCE_TYPE_SHIFT = 56;
+ private static final long RESOURCE_COUNT_MASK = 0xffffff;
+ private static final long RESOURCE_ID_MASK = 0xffffffff;
+ private static final long RESOURCE_TYPE_MASK = 0xff;
+
// Map of the registered client profiles
private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>();
private int mNextUnusedClientId = 0;
// Map of the current available frontend resources
- private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>();
+ private Map<Long, FrontendResource> mFrontendResources = new HashMap<>();
// SparseIntArray of the max usable number for each frontend resource type
private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray();
// SparseIntArray of the currently used number for each frontend resource type
@@ -93,15 +99,15 @@
// Backups for the frontend resource maps for enabling testing with custom resource maps
// such as TunerTest.testHasUnusedFrontend1()
- private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>();
+ private Map<Long, FrontendResource> mFrontendResourcesBackup = new HashMap<>();
private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray();
private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray();
private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray();
// Map of the current available demux resources
- private Map<Integer, DemuxResource> mDemuxResources = new HashMap<>();
+ private Map<Long, DemuxResource> mDemuxResources = new HashMap<>();
// Map of the current available lnb resources
- private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
+ private Map<Long, LnbResource> mLnbResources = new HashMap<>();
// Map of the current available Cas resources
private Map<Integer, CasResource> mCasResources = new HashMap<>();
// Map of the current available CiCam resources
@@ -272,7 +278,7 @@
}
@Override
- public void setLnbInfoList(int[] lnbHandles) throws RemoteException {
+ public void setLnbInfoList(long[] lnbHandles) throws RemoteException {
enforceTrmAccessPermission("setLnbInfoList");
if (lnbHandles == null) {
throw new RemoteException("Lnb handle list can't be null");
@@ -283,8 +289,8 @@
}
@Override
- public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendHandle) {
+ public boolean requestFrontend(
+ @NonNull TunerFrontendRequest request, @NonNull long[] frontendHandle) {
enforceTunerAccessPermission("requestFrontend");
enforceTrmAccessPermission("requestFrontend");
if (frontendHandle == null) {
@@ -369,8 +375,8 @@
}
@Override
- public boolean requestDemux(@NonNull TunerDemuxRequest request,
- @NonNull int[] demuxHandle) throws RemoteException {
+ public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle)
+ throws RemoteException {
enforceTunerAccessPermission("requestDemux");
enforceTrmAccessPermission("requestDemux");
if (demuxHandle == null) {
@@ -387,7 +393,7 @@
@Override
public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
- @NonNull int[] descramblerHandle) throws RemoteException {
+ @NonNull long[] descramblerHandle) throws RemoteException {
enforceDescramblerAccessPermission("requestDescrambler");
enforceTrmAccessPermission("requestDescrambler");
if (descramblerHandle == null) {
@@ -404,7 +410,7 @@
@Override
public boolean requestCasSession(@NonNull CasSessionRequest request,
- @NonNull int[] casSessionHandle) throws RemoteException {
+ @NonNull long[] casSessionHandle) throws RemoteException {
enforceTrmAccessPermission("requestCasSession");
if (casSessionHandle == null) {
throw new RemoteException("casSessionHandle can't be null");
@@ -419,8 +425,8 @@
}
@Override
- public boolean requestCiCam(@NonNull TunerCiCamRequest request,
- @NonNull int[] ciCamHandle) throws RemoteException {
+ public boolean requestCiCam(@NonNull TunerCiCamRequest request, @NonNull long[] ciCamHandle)
+ throws RemoteException {
enforceTrmAccessPermission("requestCiCam");
if (ciCamHandle == null) {
throw new RemoteException("ciCamHandle can't be null");
@@ -435,7 +441,7 @@
}
@Override
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle)
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle)
throws RemoteException {
enforceTunerAccessPermission("requestLnb");
enforceTrmAccessPermission("requestLnb");
@@ -452,7 +458,7 @@
}
@Override
- public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
+ public void releaseFrontend(long frontendHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseFrontend");
enforceTrmAccessPermission("releaseFrontend");
if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
@@ -481,7 +487,7 @@
}
@Override
- public void releaseDemux(int demuxHandle, int clientId) throws RemoteException {
+ public void releaseDemux(long demuxHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseDemux");
enforceTrmAccessPermission("releaseDemux");
if (DEBUG) {
@@ -512,7 +518,7 @@
}
@Override
- public void releaseDescrambler(int descramblerHandle, int clientId) {
+ public void releaseDescrambler(long descramblerHandle, int clientId) {
enforceTunerAccessPermission("releaseDescrambler");
enforceTrmAccessPermission("releaseDescrambler");
if (DEBUG) {
@@ -521,7 +527,7 @@
}
@Override
- public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException {
+ public void releaseCasSession(long casSessionHandle, int clientId) throws RemoteException {
enforceTrmAccessPermission("releaseCasSession");
if (!validateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) {
@@ -545,7 +551,7 @@
}
@Override
- public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException {
+ public void releaseCiCam(long ciCamHandle, int clientId) throws RemoteException {
enforceTrmAccessPermission("releaseCiCam");
if (!validateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) {
@@ -573,7 +579,7 @@
}
@Override
- public void releaseLnb(int lnbHandle, int clientId) throws RemoteException {
+ public void releaseLnb(long lnbHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseLnb");
enforceTrmAccessPermission("releaseLnb");
if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
@@ -871,7 +877,7 @@
// A set to record the frontends pending on updating. Ids will be removed
// from this set once its updating finished. Any frontend left in this set when all
// the updates are done will be removed from mFrontendResources.
- Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet());
+ Set<Long> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet());
// Update frontendResources map and other mappings accordingly
for (int i = 0; i < infos.length; i++) {
@@ -890,7 +896,7 @@
}
}
- for (int removingHandle : updatingFrontendHandles) {
+ for (long removingHandle : updatingFrontendHandles) {
// update the exclusive group id member list
removeFrontendResource(removingHandle);
}
@@ -908,7 +914,7 @@
// A set to record the demuxes pending on updating. Ids will be removed
// from this set once its updating finished. Any demux left in this set when all
// the updates are done will be removed from mDemuxResources.
- Set<Integer> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet());
+ Set<Long> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet());
// Update demuxResources map and other mappings accordingly
for (int i = 0; i < infos.length; i++) {
@@ -926,13 +932,13 @@
}
}
- for (int removingHandle : updatingDemuxHandles) {
+ for (long removingHandle : updatingDemuxHandles) {
// update the exclusive group id member list
removeDemuxResource(removingHandle);
}
}
@VisibleForTesting
- protected void setLnbInfoListInternal(int[] lnbHandles) {
+ protected void setLnbInfoListInternal(long[] lnbHandles) {
if (DEBUG) {
for (int i = 0; i < lnbHandles.length; i++) {
Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")");
@@ -942,7 +948,7 @@
// A set to record the Lnbs pending on updating. Handles will be removed
// from this set once its updating finished. Any lnb left in this set when all
// the updates are done will be removed from mLnbResources.
- Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet());
+ Set<Long> updatingLnbHandles = new HashSet<>(getLnbResources().keySet());
// Update lnbResources map and other mappings accordingly
for (int i = 0; i < lnbHandles.length; i++) {
@@ -958,7 +964,7 @@
}
}
- for (int removingHandle : updatingLnbHandles) {
+ for (long removingHandle : updatingLnbHandles) {
removeLnbResource(removingHandle);
}
}
@@ -1003,7 +1009,7 @@
}
@VisibleForTesting
- protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) {
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, long[] frontendHandle) {
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
@@ -1015,8 +1021,8 @@
return false;
}
clientPriorityUpdateOnRequest(requestClient);
- int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
boolean isRequestFromSameProcess = false;
@@ -1050,7 +1056,7 @@
// we need to check the max used num if the target frontend type is not
// currently in primary use (and simply blocked due to exclusive group)
ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId());
- int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+ long primaryFeId = targetOwnerProfile.getPrimaryFrontend();
FrontendResource primaryFe = getFrontendResource(primaryFeId);
if (fr.getType() != primaryFe.getType()
&& isFrontendMaxNumUseReached(fr.getType())) {
@@ -1102,7 +1108,7 @@
getClientProfile(shareeFeClientId).stopSharingFrontend(selfClientId);
getClientProfile(selfClientId).releaseFrontend();
}
- for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) {
+ for (long feId : getClientProfile(targetClientId).getInUseFrontendHandles()) {
getClientProfile(selfClientId).useFrontend(feId);
}
getClientProfile(selfClientId).setShareeFeClientId(targetClientId);
@@ -1117,14 +1123,14 @@
currentOwnerProfile.stopSharingFrontend(newOwnerId);
newOwnerProfile.setShareeFeClientId(ClientProfile.INVALID_RESOURCE_ID);
currentOwnerProfile.setShareeFeClientId(newOwnerId);
- for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
+ for (long inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
getFrontendResource(inUseHandle).setOwner(newOwnerId);
}
// change the primary frontend
newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend());
currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE);
// double check there is no other resources tied to the previous owner
- for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
+ for (long inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
int ownerId = getFrontendResource(inUseHandle).getOwnerClientId();
if (ownerId != newOwnerId) {
Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle
@@ -1156,8 +1162,8 @@
ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
- Set<Integer> inUseLnbHandles = new HashSet<>();
- for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
+ Set<Long> inUseLnbHandles = new HashSet<>();
+ for (Long lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
// link lnb handle to the new profile
newOwnerProfile.useLnb(lnbHandle);
@@ -1169,7 +1175,7 @@
}
// unlink lnb handles from the original owner
- for (Integer lnbHandle : inUseLnbHandles) {
+ for (Long lnbHandle : inUseLnbHandles) {
currentOwnerProfile.releaseLnb(lnbHandle);
}
@@ -1192,7 +1198,7 @@
}
@VisibleForTesting
- protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
+ protected boolean requestLnbInternal(TunerLnbRequest request, long[] lnbHandle) {
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
@@ -1200,8 +1206,8 @@
lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
ClientProfile requestClient = getClientProfile(request.clientId);
clientPriorityUpdateOnRequest(requestClient);
- int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
boolean isRequestFromSameProcess = false;
@@ -1248,7 +1254,8 @@
}
@VisibleForTesting
- protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) {
+ protected boolean requestCasSessionInternal(
+ CasSessionRequest request, long[] casSessionHandle) {
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
@@ -1301,7 +1308,7 @@
}
@VisibleForTesting
- protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) {
+ protected boolean requestCiCamInternal(TunerCiCamRequest request, long[] ciCamHandle) {
if (DEBUG) {
Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
}
@@ -1324,6 +1331,7 @@
ciCamHandle[0] = generateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ Slog.e(TAG, "requestCiCamInternal(ciCamHandle=" + ciCamHandle[0] + ")");
return true;
}
for (int ownerId : ciCam.getOwnerClientIds()) {
@@ -1349,6 +1357,7 @@
ciCamHandle[0] = generateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ Slog.e(TAG, "requestCiCamInternal(ciCamHandle=" + ciCamHandle[0] + ")");
return true;
}
return false;
@@ -1432,7 +1441,7 @@
}
@VisibleForTesting
- protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ protected boolean requestDemuxInternal(TunerDemuxRequest request, long[] demuxHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
@@ -1455,8 +1464,8 @@
}
clientPriorityUpdateOnRequest(requestClient);
- int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ long inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
boolean isRequestFromSameProcess = false;
@@ -1550,7 +1559,7 @@
@VisibleForTesting
protected boolean requestDescramblerInternal(
- TunerDescramblerRequest request, int[] descramblerHandle) {
+ TunerDescramblerRequest request, long[] descramblerHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDescrambler(request=" + request + ")");
}
@@ -1869,20 +1878,20 @@
return false;
}
- private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+ private void updateFrontendClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
FrontendResource grantingFrontend = getFrontendResource(grantingHandle);
ClientProfile ownerProfile = getClientProfile(ownerClientId);
grantingFrontend.setOwner(ownerClientId);
increFrontendNum(mFrontendUsedNums, grantingFrontend.getType());
ownerProfile.useFrontend(grantingHandle);
- for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
+ for (long exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
ownerProfile.useFrontend(exclusiveGroupMember);
}
ownerProfile.setPrimaryFrontend(grantingHandle);
}
- private void updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+ private void updateDemuxClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
DemuxResource grantingDemux = getDemuxResource(grantingHandle);
if (grantingDemux != null) {
ClientProfile ownerProfile = getClientProfile(ownerClientId);
@@ -1897,7 +1906,7 @@
ownerProfile.releaseDemux(releasingDemux.getHandle());
}
- private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+ private void updateLnbClientMappingOnNewGrant(long grantingHandle, int ownerClientId) {
LnbResource grantingLnb = getLnbResource(grantingHandle);
ClientProfile ownerProfile = getClientProfile(ownerClientId);
grantingLnb.setOwner(ownerClientId);
@@ -1981,23 +1990,23 @@
@VisibleForTesting
@Nullable
- protected FrontendResource getFrontendResource(int frontendHandle) {
+ protected FrontendResource getFrontendResource(long frontendHandle) {
return mFrontendResources.get(frontendHandle);
}
@VisibleForTesting
- protected Map<Integer, FrontendResource> getFrontendResources() {
+ protected Map<Long, FrontendResource> getFrontendResources() {
return mFrontendResources;
}
@VisibleForTesting
@Nullable
- protected DemuxResource getDemuxResource(int demuxHandle) {
+ protected DemuxResource getDemuxResource(long demuxHandle) {
return mDemuxResources.get(demuxHandle);
}
@VisibleForTesting
- protected Map<Integer, DemuxResource> getDemuxResources() {
+ protected Map<Long, DemuxResource> getDemuxResources() {
return mDemuxResources;
}
@@ -2056,8 +2065,8 @@
}
}
- private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer,
- FrontendResource> dstMap) {
+ private void replaceFeResourceMap(
+ Map<Long, FrontendResource> srcMap, Map<Long, FrontendResource> dstMap) {
if (dstMap != null) {
dstMap.clear();
if (srcMap != null && srcMap.size() > 0) {
@@ -2110,7 +2119,7 @@
if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
newFe.addExclusiveGroupMemberFeHandle(fe.getHandle());
newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles());
- for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+ for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
getFrontendResource(excGroupmemberFeHandle)
.addExclusiveGroupMemberFeHandle(newFe.getHandle());
}
@@ -2128,7 +2137,7 @@
mDemuxResources.put(newDemux.getHandle(), newDemux);
}
- private void removeFrontendResource(int removingHandle) {
+ private void removeFrontendResource(long removingHandle) {
FrontendResource fe = getFrontendResource(removingHandle);
if (fe == null) {
return;
@@ -2140,7 +2149,7 @@
}
clearFrontendAndClientMapping(ownerClient);
}
- for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+ for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
getFrontendResource(excGroupmemberFeHandle)
.removeExclusiveGroupMemberFeId(fe.getHandle());
}
@@ -2148,7 +2157,7 @@
mFrontendResources.remove(removingHandle);
}
- private void removeDemuxResource(int removingHandle) {
+ private void removeDemuxResource(long removingHandle) {
DemuxResource demux = getDemuxResource(removingHandle);
if (demux == null) {
return;
@@ -2161,12 +2170,12 @@
@VisibleForTesting
@Nullable
- protected LnbResource getLnbResource(int lnbHandle) {
+ protected LnbResource getLnbResource(long lnbHandle) {
return mLnbResources.get(lnbHandle);
}
@VisibleForTesting
- protected Map<Integer, LnbResource> getLnbResources() {
+ protected Map<Long, LnbResource> getLnbResources() {
return mLnbResources;
}
@@ -2175,7 +2184,7 @@
mLnbResources.put(newLnb.getHandle(), newLnb);
}
- private void removeLnbResource(int removingHandle) {
+ private void removeLnbResource(long removingHandle) {
LnbResource lnb = getLnbResource(removingHandle);
if (lnb == null) {
return;
@@ -2279,7 +2288,7 @@
if (profile == null) {
return;
}
- for (Integer feId : profile.getInUseFrontendHandles()) {
+ for (Long feId : profile.getInUseFrontendHandles()) {
FrontendResource fe = getFrontendResource(feId);
int ownerClientId = fe.getOwnerClientId();
if (ownerClientId == profile.getId()) {
@@ -2290,10 +2299,9 @@
if (ownerClientProfile != null) {
ownerClientProfile.stopSharingFrontend(profile.getId());
}
-
}
- int primaryFeId = profile.getPrimaryFrontend();
+ long primaryFeId = profile.getPrimaryFrontend();
if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
FrontendResource primaryFe = getFrontendResource(primaryFeId);
if (primaryFe != null) {
@@ -2310,7 +2318,7 @@
return;
}
// Clear Lnb
- for (Integer lnbHandle : profile.getInUseLnbHandles()) {
+ for (Long lnbHandle : profile.getInUseLnbHandles()) {
getLnbResource(lnbHandle).removeOwner();
}
// Clear Cas
@@ -2322,7 +2330,7 @@
getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId());
}
// Clear Demux
- for (Integer demuxHandle : profile.getInUseDemuxHandles()) {
+ for (Long demuxHandle : profile.getInUseDemuxHandles()) {
getDemuxResource(demuxHandle).removeOwner();
}
// Clear Frontend
@@ -2335,24 +2343,31 @@
return mClientProfiles.keySet().contains(clientId);
}
- private int generateResourceHandle(
+ /**
+ * Generate resource handle for resourceType and resourceId
+ * Resource Handle Allotment : 64 bits (long)
+ * 8 bits - resourceType
+ * 32 bits - resourceId
+ * 24 bits - resourceRequestCount
+ */
+ private long generateResourceHandle(
@TunerResourceManager.TunerResourceType int resourceType, int resourceId) {
- return (resourceType & 0x000000ff) << 24
- | (resourceId << 16)
- | (mResourceRequestCount++ & 0xffff);
+ return (resourceType & RESOURCE_TYPE_MASK) << RESOURCE_TYPE_SHIFT
+ | (resourceId & RESOURCE_ID_MASK) << RESOURCE_ID_SHIFT
+ | (mResourceRequestCount++ & RESOURCE_COUNT_MASK);
}
@VisibleForTesting
- protected int getResourceIdFromHandle(int resourceHandle) {
+ protected int getResourceIdFromHandle(long resourceHandle) {
if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- return resourceHandle;
+ return (int) resourceHandle;
}
- return (resourceHandle & 0x00ff0000) >> 16;
+ return (int) ((resourceHandle >> RESOURCE_ID_SHIFT) & RESOURCE_ID_MASK);
}
- private boolean validateResourceHandle(int resourceType, int resourceHandle) {
+ private boolean validateResourceHandle(int resourceType, long resourceHandle) {
if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE
- || ((resourceHandle & 0xff000000) >> 24) != resourceType) {
+ || ((resourceHandle >> RESOURCE_TYPE_SHIFT) & RESOURCE_TYPE_MASK) != resourceType) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index eccbffb..0761087 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -364,10 +364,7 @@
if ((privFlags & HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS) == 0) {
return TOUCH_VIBRATION_ATTRIBUTES;
}
- return new VibrationAttributes.Builder(IME_FEEDBACK_VIBRATION_ATTRIBUTES)
- // TODO(b/332661766): Remove CATEGORY_KEYBOARD logic
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
- .build();
+ return IME_FEEDBACK_VIBRATION_ATTRIBUTES;
}
@Nullable
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 5c567da..aa4b9f3 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -282,12 +282,6 @@
VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale,
Long.toBinaryString(mCallerInfo.attrs.getFlags()),
mCallerInfo.attrs.usageToString());
- // Optional, most vibrations have category unknown so skip them to simplify the logs
- String categoryStr =
- mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN
- ? " | category=" + VibrationAttributes.categoryToString(
- mCallerInfo.attrs.getCategory())
- : "";
// Optional, most vibrations should not be defined via AudioAttributes
// so skip them to simplify the logs
String audioUsageStr =
@@ -302,7 +296,7 @@
" | played: %s | original: %s",
mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
mOriginalEffect == null ? null : mOriginalEffect.toDebugString());
- pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr);
+ pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr);
}
/** Write this info into given {@link PrintWriter}. */
@@ -335,7 +329,6 @@
final VibrationAttributes attrs = mCallerInfo.attrs;
proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
- proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory());
proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
proto.end(attrsToken);
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index a74c4e0..b3862cc 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -134,7 +134,8 @@
return effect.resolve(mDefaultVibrationAmplitude)
.applyEffectStrength(newEffectStrength)
.scale(scaleFactor)
- .scaleLinearly(adaptiveScale);
+ // Make sure this is the last one so it is applied on top of the settings scaling.
+ .applyAdaptiveScale(adaptiveScale);
}
/**
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 8cc157c..4fc0b74 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -279,8 +279,8 @@
vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
vendorData.setDataPosition(0);
long duration = mNativeWrapper.performVendorEffect(vendorData,
- vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(),
- vibrationId);
+ vendorEffect.getEffectStrength(), vendorEffect.getScale(),
+ vendorEffect.getAdaptiveScale(), vibrationId);
if (duration > 0) {
mCurrentAmplitude = -1;
notifyListenerOnVibrating(true);
@@ -459,7 +459,7 @@
long vibrationId);
private static native long performVendorEffect(long nativePtr, Parcel vendorData,
- long strength, float scale, long vibrationId);
+ long strength, float scale, float adaptiveScale, long vibrationId);
private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
long vibrationId);
@@ -518,8 +518,9 @@
/** Turns vibrator on to perform a vendor-specific effect. */
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- long vibrationId) {
- return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId);
+ float adaptiveScale, long vibrationId) {
+ return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
+ vibrationId);
}
/** Turns vibrator on to perform effect composed of give primitives effect. */
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index fbf09fe..10ce8c2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -2450,7 +2450,7 @@
long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
synchronized (mService.mGlobalLock) {
- mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
+ mService.dumpDebugLocked(os, WindowTracingLogLevel.ALL);
}
os.end(tokenInner);
os.write(CPU_STATS, printCpuStats(reportedTimeStampNanos));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3ac91b3..0bd8441 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7538,6 +7538,10 @@
if (mStartingWindow == win) {
// This could only happen when the window is removed from hierarchy. So do not keep its
// reference anymore.
+ if (mStartingSurface != null) {
+ // Ensure the reference in client side can be removed.
+ mStartingSurface.remove(false /* animate */, false /* hasImeSurface */);
+ }
mStartingWindow = null;
mStartingData = null;
mStartingSurface = null;
@@ -10328,7 +10332,7 @@
* Write all fields to an {@code ActivityRecordProto}. This assumes the
* {@code ActivityRecordProto} is the outer-most proto data.
*/
- void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+ void dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
writeNameToProto(proto, NAME);
super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
@@ -10406,9 +10410,9 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
// Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 121ab2c..3d58082 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6785,7 +6785,7 @@
synchronized (mGlobalLock) {
// The output proto of "activity --proto activities"
mRootWindowContainer.dumpDebug(
- proto, ROOT_WINDOW_CONTAINER, WindowTraceLogLevel.ALL);
+ proto, ROOT_WINDOW_CONTAINER, WindowTracingLogLevel.ALL);
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index d2f3d1d..4427605 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -263,6 +263,13 @@
&& cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord));
}
+ /**
+ * Returns the value of the user aspect ratio override property. If unset, return {@code true}.
+ */
+ boolean getAllowUserAspectRatioOverridePropertyValue() {
+ return !mAllowUserAspectRatioOverrideOptProp.isFalse();
+ }
+
@VisibleForTesting
int getUserMinAspectRatioOverrideCode() {
try {
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 906ee20..3c3b773 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -39,6 +39,8 @@
@NonNull
private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
@NonNull
+ private final DesktopAppCompatAspectRatioPolicy mDesktopAppCompatAspectRatioPolicy;
+ @NonNull
private final AppCompatOverrides mAppCompatOverrides;
@NonNull
private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
@@ -63,6 +65,8 @@
wmService.mAppCompatConfiguration);
mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
wmService.mAppCompatConfiguration);
+ mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
+ mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
}
@NonNull
@@ -81,6 +85,11 @@
}
@NonNull
+ DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+ return mDesktopAppCompatAspectRatioPolicy;
+ }
+
+ @NonNull
AppCompatOverrides getAppCompatOverrides() {
return mAppCompatOverrides;
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 02c8a49..20c5f02 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -1506,10 +1506,13 @@
PackageManager pm = mService.mContext.getPackageManager();
ApplicationInfo applicationInfo;
+ final int sourceUserId = UserHandle.getUserId(sourceUid);
try {
- applicationInfo = pm.getApplicationInfo(packageName, 0);
+ applicationInfo = pm.getApplicationInfoAsUser(packageName, /* flags= */ 0,
+ sourceUserId);
} catch (PackageManager.NameNotFoundException e) {
- Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+ Slog.wtf(TAG, "Package name: " + packageName + " not found for user "
+ + sourceUserId);
return bas.optedIn(ar);
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 3ebaf03..9be3f43 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -255,8 +255,18 @@
inOutConfig.windowConfiguration.setAppBounds(
newParentConfiguration.windowConfiguration.getBounds());
outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- outAppBounds.inset(displayContent.getDisplayPolicy()
- .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+ if (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ final DisplayPolicy.DecorInsets.Info decor =
+ displayContent.getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
+ if (outAppBounds.contains(decor.mOverrideNonDecorFrame)) {
+ outAppBounds.intersect(decor.mOverrideNonDecorFrame);
+ }
+ } else {
+ // TODO(b/358509380): Handle other windowing mode like split screen and freeform
+ // cases correctly.
+ outAppBounds.inset(displayContent.getDisplayPolicy()
+ .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+ }
}
float density = inOutConfig.densityDpi;
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
@@ -807,23 +817,23 @@
*/
@CallSuper
protected void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
final long token = proto.start(fieldId);
- if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
+ if (logLevel == WindowTracingLogLevel.ALL || mHasOverrideConfiguration) {
mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
- logLevel == WindowTraceLogLevel.CRITICAL);
+ logLevel == WindowTracingLogLevel.CRITICAL);
}
// Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
// required to mitigate performance overhead
- if (logLevel == WindowTraceLogLevel.ALL) {
+ if (logLevel == WindowTracingLogLevel.ALL) {
mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
false /* critical */);
}
- if (logLevel == WindowTraceLogLevel.TRIM) {
+ if (logLevel == WindowTracingLogLevel.TRIM) {
// Required for Fass to automatically detect pip transitions in Winscope traces
dumpDebugWindowingMode(proto);
}
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
new file mode 100644
index 0000000..8477c6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+
+import android.annotation.NonNull;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+// TODO(b/359217664): Consider refactoring into AppCompatAspectRatioPolicy.
+/**
+ * Encapsulate app compat aspect ratio policy logic specific for desktop windowing initial bounds
+ * calculation.
+ */
+public class DesktopAppCompatAspectRatioPolicy {
+
+ @NonNull
+ private final AppCompatOverrides mAppCompatOverrides;
+ @NonNull
+ private final AppCompatConfiguration mAppCompatConfiguration;
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final TransparentPolicy mTransparentPolicy;
+
+ DesktopAppCompatAspectRatioPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatOverrides appCompatOverrides,
+ @NonNull TransparentPolicy transparentPolicy,
+ @NonNull AppCompatConfiguration appCompatConfiguration) {
+ mActivityRecord = activityRecord;
+ mAppCompatOverrides = appCompatOverrides;
+ mTransparentPolicy = transparentPolicy;
+ mAppCompatConfiguration = appCompatConfiguration;
+ }
+
+ /**
+ * Calculates the final aspect ratio of an launching activity based on the task it will be
+ * launched in. Takes into account any min or max aspect ratio constraints.
+ */
+ float calculateAspectRatio(@NonNull Task task) {
+ final float maxAspectRatio = getMaxAspectRatio();
+ final float minAspectRatio = getMinAspectRatio(task);
+ float desiredAspectRatio = 0;
+ desiredAspectRatio = getDesiredAspectRatio(task);
+ if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+ desiredAspectRatio = maxAspectRatio;
+ } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+ desiredAspectRatio = minAspectRatio;
+ }
+ return desiredAspectRatio;
+ }
+
+ /**
+ * Returns the aspect ratio desired by the system for current activity, not taking into account
+ * any min or max aspect ratio constraints.
+ */
+ @VisibleForTesting
+ float getDesiredAspectRatio(@NonNull Task task) {
+ final float letterboxAspectRatioOverride = getFixedOrientationLetterboxAspectRatio(task);
+ // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
+ // be respected in #calculateAspectRatio.
+ if (isDefaultMultiWindowLetterboxAspectRatioDesired(task)) {
+ return DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+ } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+ return letterboxAspectRatioOverride;
+ }
+ return AppCompatUtils.computeAspectRatio(task.getDisplayArea().getBounds());
+ }
+
+ /**
+ * Determines the letterbox aspect ratio for an application based on its orientation and
+ * resizability.
+ */
+ private float getFixedOrientationLetterboxAspectRatio(@NonNull Task task) {
+ return mActivityRecord.shouldCreateCompatDisplayInsets()
+ ? getDefaultMinAspectRatioForUnresizableApps(task)
+ : getDefaultMinAspectRatio(task);
+ }
+
+ /**
+ * Calculates the aspect ratio of the available display area when an app enters split-screen on
+ * a given device, taking into account any dividers and insets.
+ */
+ private float getSplitScreenAspectRatio(@NonNull Task task) {
+ // Getting the same aspect ratio that apps get in split screen.
+ final DisplayArea displayArea = task.getDisplayArea();
+ final int dividerWindowWidth =
+ mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_thickness);
+ final int dividerInsets =
+ mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+ R.dimen.docked_stack_divider_insets);
+ final int dividerSize = dividerWindowWidth - dividerInsets * 2;
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ if (bounds.width() >= bounds.height()) {
+ bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
+ bounds.right = bounds.centerX();
+ } else {
+ bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
+ bounds.bottom = bounds.centerY();
+ }
+ return AppCompatUtils.computeAspectRatio(bounds);
+ }
+
+
+ /**
+ * Returns the minimum aspect ratio for unresizable apps as determined by the system.
+ */
+ private float getDefaultMinAspectRatioForUnresizableApps(@NonNull Task task) {
+ if (!mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()) {
+ return mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ : getDefaultMinAspectRatio(task);
+ }
+
+ return getSplitScreenAspectRatio(task);
+ }
+
+ /**
+ * Returns the default minimum aspect ratio for apps as determined by the system.
+ */
+ private float getDefaultMinAspectRatio(@NonNull Task task) {
+ if (!mAppCompatConfiguration.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+ return mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+ return getDisplayAreaMinAspectRatio(task);
+ }
+
+ /**
+ * Calculates the aspect ratio of the available display area.
+ */
+ private float getDisplayAreaMinAspectRatio(@NonNull Task task) {
+ final DisplayArea displayArea = task.getDisplayArea();
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ return AppCompatUtils.computeAspectRatio(bounds);
+ }
+
+ /**
+ * Returns {@code true} if the default aspect ratio for a letterboxed app in multi-window mode
+ * should be used.
+ */
+ private boolean isDefaultMultiWindowLetterboxAspectRatioDesired(@NonNull Task task) {
+ final DisplayContent dc = task.mDisplayContent;
+ final int windowingMode = task.getDisplayArea().getWindowingMode();
+ return WindowConfiguration.inMultiWindowMode(windowingMode)
+ && !dc.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Returns the min aspect ratio of this activity.
+ */
+ private float getMinAspectRatio(@NonNull Task task) {
+ if (mTransparentPolicy.isRunning()) {
+ return mTransparentPolicy.getInheritedMinAspectRatio();
+ }
+
+ final ActivityInfo info = mActivityRecord.info;
+ if (info.applicationInfo == null) {
+ return info.getMinAspectRatio();
+ }
+
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+ if (shouldApplyUserMinAspectRatioOverride(task)) {
+ return aspectRatioOverrides.getUserMinAspectRatio();
+ }
+
+ if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
+ && !mAppCompatOverrides.getAppCompatCameraOverrides()
+ .shouldOverrideMinAspectRatioForCamera()) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !ActivityInfo.isFixedOrientationPortrait(
+ mActivityRecord.getOverrideOrientation())) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
+ && isFullscreenPortrait(task)) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) {
+ return Math.max(aspectRatioOverrides.getSplitScreenAspectRatio(),
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_SMALL)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ info.getMinAspectRatio());
+ }
+ return info.getMinAspectRatio();
+ }
+
+ /**
+ * Returns the max aspect ratio of this activity.
+ */
+ private float getMaxAspectRatio() {
+ if (mTransparentPolicy.isRunning()) {
+ return mTransparentPolicy.getInheritedMaxAspectRatio();
+ }
+ return mActivityRecord.info.getMaxAspectRatio();
+ }
+
+ /**
+ * Whether an applications minimum aspect ratio has been overridden.
+ */
+ boolean hasMinAspectRatioOverride(@NonNull Task task) {
+ return mActivityRecord.info.getMinAspectRatio() < getMinAspectRatio(task);
+ }
+
+ /**
+ * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+ * current app.
+ */
+ private boolean shouldApplyUserMinAspectRatioOverride(@NonNull Task task) {
+ if (!shouldEnableUserAspectRatioSettings(task)) {
+ return false;
+ }
+
+ final int userAspectRatioCode = mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ .getUserMinAspectRatioOverrideCode();
+
+ return userAspectRatioCode != USER_MIN_ASPECT_RATIO_UNSET
+ && userAspectRatioCode != USER_MIN_ASPECT_RATIO_APP_DEFAULT
+ && userAspectRatioCode != USER_MIN_ASPECT_RATIO_FULLSCREEN;
+ }
+
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ private boolean shouldEnableUserAspectRatioSettings(@NonNull Task task) {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+ .getAllowUserAspectRatioOverridePropertyValue()
+ && mAppCompatConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && task.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Returns {@code true} if the task window is portrait and fullscreen.
+ */
+ private boolean isFullscreenPortrait(@NonNull Task task) {
+ return task.getConfiguration().orientation == ORIENTATION_PORTRAIT
+ && task.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 86f69cd..ca5485e 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -356,7 +356,7 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c44e1b1..648f6bd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3565,9 +3565,9 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
// Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 2f0ee17..f40f2617 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
@@ -183,7 +184,7 @@
final DisplayInfo displayInfo = dc.getDisplayInfo();
final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
if (settings.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
- if (dc.isPrivate()) {
+ if (dc.isPrivate() || dc.getDisplay().getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT) {
// For private displays by default content is destroyed on removal.
return REMOVE_CONTENT_MODE_DESTROY;
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index c66d659..169a76f 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -403,7 +403,7 @@
@Override
public void dumpProto(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
final long token = proto.start(fieldId);
final long token2 = proto.start(IDENTIFIER);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3a5f9b7..6b916ef 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -726,7 +726,7 @@
}
@Override
- void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+ void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, INSETS_SOURCE_PROVIDER, logLevel);
final WindowState imeRequesterWindow =
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index baf0db2..0c0b794 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -65,6 +65,6 @@
InsetsControlTarget getImeControlTarget();
void dumpProto(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel);
+ @WindowTracingLogLevel int logLevel);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f5c92f6..b66b8bc 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -725,7 +725,7 @@
}
}
- void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+ void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel) {
final long token = proto.start(fieldId);
mSource.dumpDebug(proto, SOURCE);
mTmpRect.dumpDebug(proto, FRAME);
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 9c2a8de..098a691 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -462,7 +462,7 @@
}
}
- void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+ void dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
for (int i = mProviders.size() - 1; i >= 0; i--) {
final InsetsSourceProvider provider = mProviders.valueAt(i);
provider.dumpDebug(proto,
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 60454fc..781023c 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -30,3 +30,4 @@
# Files related to tracing
per-file *TransitionTracer.java = file:platform/development:/tools/winscope/OWNERS
+per-file *WindowTracing* = file:platform/development:/tools/winscope/OWNERS
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6427c32..b528e20 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1189,8 +1189,8 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ @WindowTracingLogLevel int logLevel) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4340771..efa9c53 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6271,8 +6271,8 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ @WindowTracingLogLevel int logLevel) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 329d11b..2fbabc5 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1780,11 +1780,6 @@
if (resuming != null) {
// We do not want to trigger auto-PiP upon launch of a translucent activity.
final boolean resumingOccludesParent = resuming.occludesParent();
- // Resuming the new resume activity only if the previous activity can't go into Pip
- // since we want to give Pip activities a chance to enter Pip before resuming the
- // next activity.
- final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
- "shouldAutoPipWhilePausing", userLeaving);
if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
// If a new task is being launched, then mark the existing top activity as
@@ -1794,6 +1789,12 @@
Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
resuming, resuming.getOptions());
}
+
+ // Resuming the new resume activity only if the previous activity can't go into Pip
+ // since we want to give Pip activities a chance to enter Pip before resuming the
+ // next activity.
+ final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
+ "shouldAutoPipWhilePausing", userLeaving);
if (prev.supportsEnterPipOnTaskSwitch && userLeaving
&& resumingOccludesParent && lastResumedCanPip
&& prev.pictureInPictureArgs.isAutoEnterEnabled()) {
@@ -3334,8 +3335,8 @@
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ @WindowTracingLogLevel int logLevel) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 15d67eb..9ae881b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2784,9 +2784,9 @@
@CallSuper
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
boolean isVisible = isVisible();
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 243ab3a..bdb1d43 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6858,7 +6858,7 @@
* @param proto Stream to write the WindowContainer object to.
* @param logLevel Determines the amount of data to be written to the Protobuf.
*/
- void dumpDebugLocked(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+ void dumpDebugLocked(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
mPolicy.dumpDebug(proto, POLICY);
mRoot.dumpDebug(proto, ROOT_WINDOW_CONTAINER, logLevel);
final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
@@ -7217,7 +7217,7 @@
if (useProto) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mGlobalLock) {
- dumpDebugLocked(proto, WindowTraceLogLevel.ALL);
+ dumpDebugLocked(proto, WindowTracingLogLevel.ALL);
}
proto.flush();
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b6e8977..923ad4b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4072,9 +4072,9 @@
@CallSuper
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
boolean isVisible = isVisible();
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible) {
return;
}
@@ -6140,7 +6140,7 @@
@Override
public void dumpProto(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
+ @WindowTracingLogLevel int logLevel) {
dumpDebug(proto, fieldId, logLevel);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 11ef2cd..67bd5cb 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -698,8 +698,8 @@
@CallSuper
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ @WindowTracingLogLevel int logLevel) {
+ if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 04d5c03..fe26726 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -112,7 +112,7 @@
saveForBugreportInternal(pw);
}
- abstract void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw);
+ abstract void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw);
abstract void setLogFrequency(boolean onFrame, PrintWriter pw);
abstract void setBufferCapacity(int capacity, PrintWriter pw);
abstract boolean isEnabled();
@@ -158,7 +158,7 @@
* @param where Logging point descriptor
* @param elapsedRealtimeNanos Timestamp
*/
- protected void dumpToProto(ProtoOutputStream os, @WindowTraceLogLevel int logLevel,
+ protected void dumpToProto(ProtoOutputStream os, @WindowTracingLogLevel int logLevel,
String where, long elapsedRealtimeNanos) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
try {
diff --git a/services/core/java/com/android/server/wm/WindowTracingDataSource.java b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
index 3d2c0d3..6984f0d 100644
--- a/services/core/java/com/android/server/wm/WindowTracingDataSource.java
+++ b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
@@ -50,12 +50,14 @@
}
public static class Config {
- public final @WindowTraceLogLevel int mLogLevel;
- public final boolean mLogOnFrame;
+ public final @WindowTracingLogLevel int mLogLevel;
+ public final @WindowTracingLogFrequency int mLogFrequency;
- private Config(@WindowTraceLogLevel int logLevel, boolean logOnFrame) {
+ private Config(
+ @WindowTracingLogLevel int logLevel,
+ @WindowTracingLogFrequency int logFrequency) {
mLogLevel = logLevel;
- mLogOnFrame = logOnFrame;
+ mLogFrequency = logFrequency;
}
}
@@ -68,7 +70,8 @@
}
}
- private static final Config CONFIG_DEFAULT = new Config(WindowTraceLogLevel.TRIM, true);
+ private static final Config CONFIG_DEFAULT =
+ new Config(WindowTracingLogLevel.TRIM, WindowTracingLogFrequency.FRAME);
private static final int CONFIG_VALUE_UNSPECIFIED = 0;
private static final String TAG = "WindowTracingDataSource";
@@ -160,45 +163,48 @@
throw new RuntimeException("Failed to parse WindowManagerConfig", e);
}
- @WindowTraceLogLevel int logLevel;
+ @WindowTracingLogLevel int logLevel;
switch(parsedLogLevel) {
case CONFIG_VALUE_UNSPECIFIED:
Log.w(TAG, "Unspecified log level. Defaulting to TRIM");
- logLevel = WindowTraceLogLevel.TRIM;
+ logLevel = WindowTracingLogLevel.TRIM;
break;
case WindowManagerConfig.LOG_LEVEL_VERBOSE:
- logLevel = WindowTraceLogLevel.ALL;
+ logLevel = WindowTracingLogLevel.ALL;
break;
case WindowManagerConfig.LOG_LEVEL_DEBUG:
- logLevel = WindowTraceLogLevel.TRIM;
+ logLevel = WindowTracingLogLevel.TRIM;
break;
case WindowManagerConfig.LOG_LEVEL_CRITICAL:
- logLevel = WindowTraceLogLevel.CRITICAL;
+ logLevel = WindowTracingLogLevel.CRITICAL;
break;
default:
Log.w(TAG, "Unrecognized log level. Defaulting to TRIM");
- logLevel = WindowTraceLogLevel.TRIM;
+ logLevel = WindowTracingLogLevel.TRIM;
break;
}
- boolean logOnFrame;
+ @WindowTracingLogFrequency int logFrequency;
switch(parsedLogFrequency) {
case CONFIG_VALUE_UNSPECIFIED:
- Log.w(TAG, "Unspecified log frequency. Defaulting to 'log on frame'");
- logOnFrame = true;
+ Log.w(TAG, "Unspecified log frequency. Defaulting to 'frame'");
+ logFrequency = WindowTracingLogFrequency.FRAME;
break;
case WindowManagerConfig.LOG_FREQUENCY_FRAME:
- logOnFrame = true;
+ logFrequency = WindowTracingLogFrequency.FRAME;
break;
case WindowManagerConfig.LOG_FREQUENCY_TRANSACTION:
- logOnFrame = false;
+ logFrequency = WindowTracingLogFrequency.TRANSACTION;
+ break;
+ case WindowManagerConfig.LOG_FREQUENCY_SINGLE_DUMP:
+ logFrequency = WindowTracingLogFrequency.SINGLE_DUMP;
break;
default:
- Log.w(TAG, "Unrecognized log frequency. Defaulting to 'log on frame'");
- logOnFrame = true;
+ Log.w(TAG, "Unrecognized log frequency. Defaulting to 'frame'");
+ logFrequency = WindowTracingLogFrequency.FRAME;
break;
}
- return new Config(logLevel, logOnFrame);
+ return new Config(logLevel, logFrequency);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowTracingLegacy.java b/services/core/java/com/android/server/wm/WindowTracingLegacy.java
index 7a36707..34fd088 100644
--- a/services/core/java/com/android/server/wm/WindowTracingLegacy.java
+++ b/services/core/java/com/android/server/wm/WindowTracingLegacy.java
@@ -30,6 +30,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.TraceBuffer;
import java.io.File;
@@ -58,7 +59,7 @@
private boolean mEnabled;
private volatile boolean mEnabledLockFree;
- protected @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+ protected @WindowTracingLogLevel int mLogLevel = WindowTracingLogLevel.TRIM;
protected boolean mLogOnFrame = false;
WindowTracingLegacy(WindowManagerService service, Choreographer choreographer) {
@@ -66,6 +67,7 @@
service.mGlobalLock, BUFFER_CAPACITY_TRIM);
}
+ @VisibleForTesting
WindowTracingLegacy(File traceFile, WindowManagerService service, Choreographer choreographer,
WindowManagerGlobalLock globalLock, int bufferSize) {
super(service, choreographer, globalLock);
@@ -74,20 +76,20 @@
}
@Override
- void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+ void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw) {
logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
mLogLevel = logLevel;
switch (logLevel) {
- case WindowTraceLogLevel.ALL: {
+ case WindowTracingLogLevel.ALL: {
setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
break;
}
- case WindowTraceLogLevel.TRIM: {
+ case WindowTracingLogLevel.TRIM: {
setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
break;
}
- case WindowTraceLogLevel.CRITICAL: {
+ case WindowTracingLogLevel.CRITICAL: {
setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
break;
}
@@ -141,19 +143,19 @@
String logLevelStr = shell.getNextArgRequired().toLowerCase();
switch (logLevelStr) {
case "all": {
- setLogLevel(WindowTraceLogLevel.ALL, pw);
+ setLogLevel(WindowTracingLogLevel.ALL, pw);
break;
}
case "trim": {
- setLogLevel(WindowTraceLogLevel.TRIM, pw);
+ setLogLevel(WindowTracingLogLevel.TRIM, pw);
break;
}
case "critical": {
- setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+ setLogLevel(WindowTracingLogLevel.CRITICAL, pw);
break;
}
default: {
- setLogLevel(WindowTraceLogLevel.TRIM, pw);
+ setLogLevel(WindowTracingLogLevel.TRIM, pw);
break;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java b/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java
new file mode 100644
index 0000000..8e2c308
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({
+ WindowTracingLogFrequency.FRAME,
+ WindowTracingLogFrequency.TRANSACTION,
+ WindowTracingLogFrequency.SINGLE_DUMP,
+})
+@Retention(RetentionPolicy.SOURCE)
+@interface WindowTracingLogFrequency {
+ /**
+ * Trace state snapshots when a frame is committed.
+ */
+ int FRAME = 0;
+ /**
+ * Trace state snapshots when a transaction is committed.
+ */
+ int TRANSACTION = 1;
+ /**
+ * Trace single state snapshots when the Perfetto data source is started.
+ */
+ int SINGLE_DUMP = 2;
+}
diff --git a/services/core/java/com/android/server/wm/WindowTraceLogLevel.java b/services/core/java/com/android/server/wm/WindowTracingLogLevel.java
similarity index 90%
rename from services/core/java/com/android/server/wm/WindowTraceLogLevel.java
rename to services/core/java/com/android/server/wm/WindowTracingLogLevel.java
index 2165c66..4f901c6 100644
--- a/services/core/java/com/android/server/wm/WindowTraceLogLevel.java
+++ b/services/core/java/com/android/server/wm/WindowTracingLogLevel.java
@@ -22,12 +22,12 @@
import java.lang.annotation.RetentionPolicy;
@IntDef({
- WindowTraceLogLevel.ALL,
- WindowTraceLogLevel.TRIM,
- WindowTraceLogLevel.CRITICAL,
+ WindowTracingLogLevel.ALL,
+ WindowTracingLogLevel.TRIM,
+ WindowTracingLogLevel.CRITICAL,
})
@Retention(RetentionPolicy.SOURCE)
-@interface WindowTraceLogLevel{
+@interface WindowTracingLogLevel {
/**
* Logs all elements with maximum amount of information.
*
diff --git a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
index 653b6da..cf948ca 100644
--- a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
+++ b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
@@ -25,6 +25,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -37,11 +39,17 @@
this::onStart, this::onStop);
WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) {
- super(service, choreographer, service.mGlobalLock);
+ this(service, choreographer, service.mGlobalLock);
+ }
+
+ @VisibleForTesting
+ WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer,
+ WindowManagerGlobalLock globalLock) {
+ super(service, choreographer, globalLock);
}
@Override
- void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+ void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw) {
logAndPrintln(pw, "Log level must be configured through perfetto");
}
@@ -110,7 +118,15 @@
if (!isDataSourceStarting) {
return;
}
- } else if (isOnFrameLogEvent != dataSourceConfig.mLogOnFrame) {
+ } else if (isOnFrameLogEvent) {
+ boolean isDataSourceLoggingOnFrame =
+ dataSourceConfig.mLogFrequency == WindowTracingLogFrequency.FRAME;
+ if (!isDataSourceLoggingOnFrame) {
+ return;
+ }
+ } else if (dataSourceConfig.mLogFrequency
+ == WindowTracingLogFrequency.SINGLE_DUMP) {
+ // If it is a dump, write only the start log event and skip the following ones
return;
}
@@ -141,21 +157,21 @@
}
private void onStart(WindowTracingDataSource.Config config) {
- if (config.mLogOnFrame) {
+ if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
mCountSessionsOnFrame.incrementAndGet();
- } else {
+ } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
mCountSessionsOnTransaction.incrementAndGet();
}
Log.i(TAG, "Started with logLevel: " + config.mLogLevel
- + " logOnFrame: " + config.mLogOnFrame);
+ + " logFrequency: " + config.mLogFrequency);
log(WHERE_START_TRACING);
}
private void onStop(WindowTracingDataSource.Config config) {
- if (config.mLogOnFrame) {
+ if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
mCountSessionsOnFrame.decrementAndGet();
- } else {
+ } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
mCountSessionsOnTransaction.decrementAndGet();
}
Log.i(TAG, "Stopped");
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index f12930a..5c5ac28 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -198,7 +198,8 @@
}
static Aidl::VendorEffect vendorEffectFromJavaParcel(JNIEnv* env, jobject vendorData,
- jlong strength, jfloat scale) {
+ jlong strength, jfloat scale,
+ jfloat adaptiveScale) {
PersistableBundle bundle;
if (AParcel* parcel = AParcel_fromJavaParcel(env, vendorData); parcel != nullptr) {
if (binder_status_t status = bundle.readFromParcel(parcel); status == STATUS_OK) {
@@ -217,6 +218,7 @@
effect.vendorData = bundle;
effect.strength = static_cast<Aidl::EffectStrength>(strength);
effect.scale = static_cast<float>(scale);
+ effect.vendorScale = static_cast<float>(adaptiveScale);
return effect;
}
@@ -319,13 +321,14 @@
static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobject vendorData, jlong strength, jfloat scale,
- jlong vibrationId) {
+ jfloat adaptiveScale, jlong vibrationId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
return -1;
}
- Aidl::VendorEffect effect = vendorEffectFromJavaParcel(env, vendorData, strength, scale);
+ Aidl::VendorEffect effect =
+ vendorEffectFromJavaParcel(env, vendorData, strength, scale, adaptiveScale);
auto callback = wrapper->createCallback(vibrationId);
auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
return hal->performVendorEffect(effect, callback);
@@ -511,7 +514,7 @@
{"off", "(J)V", (void*)vibratorOff},
{"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
{"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
- {"performVendorEffect", "(JLandroid/os/Parcel;JFJ)J", (void*)vibratorPerformVendorEffect},
+ {"performVendorEffect", "(JLandroid/os/Parcel;JFFJ)J", (void*)vibratorPerformVendorEffect},
{"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
(void*)vibratorPerformComposedEffect},
{"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 0bcc572..5840cb9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1621,16 +1621,21 @@
advanceTime(1); // Run updatePowerState
reset(mHolder.wakelockController);
+ when(mHolder.wakelockController
+ .acquireWakelock(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE))
+ .thenReturn(true);
mHolder.dpc.overrideDozeScreenState(
supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
- advanceTime(1); // Run updatePowerState
// Should get a wakelock to notify powermanager
- verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock(
- eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS));
+ verify(mHolder.wakelockController).acquireWakelock(
+ eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
+ advanceTime(1); // Run updatePowerState
verify(mHolder.displayPowerState)
.setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
+ verify(mHolder.wakelockController).releaseWakelock(
+ eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index c23d4b1..019b70e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -64,6 +64,8 @@
"[" + DISPLAY_ID + "]prox negative");
assertEquals(mWakelockController.getSuspendBlockerProxDebounceId(),
"[" + DISPLAY_ID + "]prox debounce");
+ assertEquals(mWakelockController.getSuspendBlockerOverrideDozeScreenState(),
+ "[" + DISPLAY_ID + "]override doze screen state");
}
@Test
@@ -162,6 +164,28 @@
}
@Test
+ public void acquireOverrideDozeScreenStateSuspendBlocker() throws Exception {
+ // Acquire the suspend blocker
+ verifyWakelockAcquisitionAndReaquisition(WakelockController
+ .WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+ () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+ // Verify acquire happened only once
+ verify(mDisplayPowerCallbacks, times(1))
+ .acquireSuspendBlocker(mWakelockController
+ .getSuspendBlockerOverrideDozeScreenState());
+
+ // Release the suspend blocker
+ verifyWakelockReleaseAndRerelease(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+ () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+ // Verify suspend blocker was released only once
+ verify(mDisplayPowerCallbacks, times(1))
+ .releaseSuspendBlocker(mWakelockController
+ .getSuspendBlockerOverrideDozeScreenState());
+ }
+
+ @Test
public void proximityPositiveRunnableWorksAsExpected() {
// Acquire the suspend blocker twice
assertTrue(mWakelockController.acquireWakelock(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 3931580..d80a1f0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -18,13 +18,20 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
import static android.view.MotionEvent.ACTION_UP;
+import static junit.framework.Assert.assertFalse;
+
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.testng.AssertJUnit.assertTrue;
import android.annotation.NonNull;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -32,8 +39,10 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -45,6 +54,9 @@
@RunWith(AndroidJUnit4.class)
public class MagnificationGestureHandlerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private TestMagnificationGestureHandler mMgh;
private static final int DISPLAY_0 = 0;
private static final int FULLSCREEN_MODE =
@@ -81,6 +93,66 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
+ final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+ mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
+
+ mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
+
+ try {
+ assertTrue(mMgh.mIsHandleMouseOrStylusEventCalled);
+ } finally {
+ mouseEvent.recycle();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
+ final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+ stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
+
+ mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
+
+ try {
+ assertTrue(mMgh.mIsHandleMouseOrStylusEventCalled);
+ } finally {
+ stylusEvent.recycle();
+ }
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
+ final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+ mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
+
+ mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
+
+ try {
+ assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
+ } finally {
+ mouseEvent.recycle();
+ }
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
+ final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+ stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
+
+ mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
+
+ try {
+ assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
+ } finally {
+ stylusEvent.recycle();
+ }
+ }
+
+ @Test
public void onMotionEvent_downEvent_handleInteractionStart() {
final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
@@ -125,6 +197,7 @@
private static class TestMagnificationGestureHandler extends MagnificationGestureHandler {
boolean mIsInternalMethodCalled = false;
+ boolean mIsHandleMouseOrStylusEventCalled = false;
TestMagnificationGestureHandler(int displayId, boolean detectSingleFingerTripleTap,
boolean detectTwoFingerTripleTap,
@@ -135,6 +208,11 @@
}
@Override
+ void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ mIsHandleMouseOrStylusEventCalled = true;
+ }
+
+ @Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mIsInternalMethodCalled = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
new file mode 100644
index 0000000..75258f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PresentationEventLoggerTest {
+
+ @Test
+ public void testViewEntered() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ AutofillId id = new AutofillId(13);
+ AutofillValue initialValue = AutofillValue.forText("hello");
+ AutofillValue lastValue = AutofillValue.forText("hello world");
+ ViewState vState = new ViewState(id, null, 0, false);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetFocusedId(id);
+ pEventLogger.onFieldTextUpdated(vState, initialValue);
+ pEventLogger.onFieldTextUpdated(vState, lastValue);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mFieldFirstLength).isEqualTo(initialValue.getTextValue().length());
+ assertThat(event.mFieldLastLength).isEqualTo(lastValue.getTextValue().length());
+ assertThat(event.mFieldModifiedFirstTimestampMs).isNotEqualTo(-1);
+ assertThat(event.mFieldModifiedLastTimestampMs).isNotEqualTo(-1);
+ }
+
+ @Test
+ public void testViewAutofilled() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ String newTextValue = "hello";
+ AutofillValue value = AutofillValue.forText(newTextValue);
+ AutofillId id = new AutofillId(13);
+ ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetFocusedId(id);
+ pEventLogger.onFieldTextUpdated(vState, value);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mFieldFirstLength).isEqualTo(newTextValue.length());
+ assertThat(event.mFieldLastLength).isEqualTo(newTextValue.length());
+ assertThat(event.mAutofilledTimestampMs).isNotEqualTo(-1);
+ assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
+ assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
+ }
+
+ @Test
+ public void testModifiedOnDifferentView() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ String newTextValue = "hello";
+ AutofillValue value = AutofillValue.forText(newTextValue);
+ AutofillId id = new AutofillId(13);
+ ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.onFieldTextUpdated(vState, value);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mFieldFirstLength).isEqualTo(-1);
+ assertThat(event.mFieldLastLength).isEqualTo(-1);
+ assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
+ assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
+ assertThat(event.mAutofilledTimestampMs).isEqualTo(-1);
+ }
+
+ @Test
+ public void testSetCountShown() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.logWhenDatasetShown(7);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mCountShown).isEqualTo(7);
+ assertThat(event.mNoPresentationReason)
+ .isEqualTo(PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN);
+ }
+
+ @Test
+ public void testFillDialogShownThenInline() {
+ PresentationStatsEventLogger pEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+ pEventLogger.startNewEvent();
+ pEventLogger.maybeSetDisplayPresentationType(3);
+ pEventLogger.maybeSetDisplayPresentationType(2);
+
+ PresentationStatsEventLogger.PresentationStatsEventInternal event =
+ pEventLogger.getInternalEvent().get();
+ assertThat(event).isNotNull();
+ assertThat(event.mDisplayPresentationType).isEqualTo(3);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 963b27e..8e36335 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -119,8 +119,7 @@
tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- Map<Integer, FrontendResource> resources =
- mTunerResourceManagerService.getFrontendResources();
+ Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos.length; id++) {
assertThat(resources.get(infos[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -147,15 +146,14 @@
tunerFrontendInfo(3 /*handle*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- Map<Integer, FrontendResource> resources =
- mTunerResourceManagerService.getFrontendResources();
+ Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
.containsExactlyElementsIn(Arrays.asList(infos));
- assertThat(resources.get(0).getExclusiveGroupMemberFeHandles()).isEmpty();
- assertThat(resources.get(1).getExclusiveGroupMemberFeHandles()).containsExactly(2, 3);
- assertThat(resources.get(2).getExclusiveGroupMemberFeHandles()).containsExactly(1, 3);
- assertThat(resources.get(3).getExclusiveGroupMemberFeHandles()).containsExactly(1, 2);
+ assertThat(resources.get(0L).getExclusiveGroupMemberFeHandles()).isEmpty();
+ assertThat(resources.get(1L).getExclusiveGroupMemberFeHandles()).containsExactly(2L, 3L);
+ assertThat(resources.get(2L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 3L);
+ assertThat(resources.get(3L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 2L);
}
@Test
@@ -168,11 +166,11 @@
tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- Map<Integer, FrontendResource> resources0 =
+ Map<Long, FrontendResource> resources0 =
mTunerResourceManagerService.getFrontendResources();
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- Map<Integer, FrontendResource> resources1 =
+ Map<Long, FrontendResource> resources1 =
mTunerResourceManagerService.getFrontendResources();
assertThat(resources0).isEqualTo(resources1);
@@ -195,8 +193,7 @@
tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
- Map<Integer, FrontendResource> resources =
- mTunerResourceManagerService.getFrontendResources();
+ Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos1.length; id++) {
assertThat(resources.get(infos1[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -222,8 +219,7 @@
tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
- Map<Integer, FrontendResource> resources =
- mTunerResourceManagerService.getFrontendResources();
+ Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos1.length; id++) {
assertThat(resources.get(infos1[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
@@ -240,7 +236,7 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
TunerFrontendRequest request =
tunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
@@ -263,7 +259,7 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
@@ -296,7 +292,7 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(0);
@@ -333,7 +329,7 @@
1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
TunerFrontendRequest request =
tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
@@ -385,7 +381,7 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -435,13 +431,13 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle, infos[1].handle)));
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
request =
tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
@@ -480,7 +476,7 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
@@ -525,7 +521,7 @@
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/);
- int[] casSessionHandle = new int[1];
+ long[] casSessionHandle = new long[1];
// Request for 2 cas sessions.
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
@@ -581,7 +577,7 @@
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/);
- int[] ciCamHandle = new int[1];
+ long[] ciCamHandle = new long[1];
// Request for 2 ciCam sessions.
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
@@ -625,7 +621,7 @@
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/);
- int[] casSessionHandle = new int[1];
+ long[] casSessionHandle = new long[1];
// Request for 1 cas sessions.
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
@@ -662,7 +658,7 @@
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/);
- int[] ciCamHandle = new int[1];
+ long[] ciCamHandle = new long[1];
// Request for 1 ciCam sessions.
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
@@ -708,17 +704,17 @@
clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init lnb resources.
- int[] lnbHandles = {1};
+ long[] lnbHandles = {1};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
request.clientId = clientId0[0];
- int[] lnbHandle = new int[1];
+ long[] lnbHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
+ .isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0])));
request = new TunerLnbRequest();
request.clientId = clientId1[0];
@@ -747,12 +743,12 @@
assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
// Init lnb resources.
- int[] lnbHandles = {0};
+ long[] lnbHandles = {0};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
request.clientId = clientId[0];
- int[] lnbHandle = new int[1];
+ long[] lnbHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
@@ -786,7 +782,7 @@
TunerFrontendRequest request =
tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
@@ -823,12 +819,12 @@
infos[2] = tunerDemuxInfo(2 /* handle */, Filter.TYPE_TS);
mTunerResourceManagerService.setDemuxInfoListInternal(infos);
- int[] demuxHandle0 = new int[1];
+ long[] demuxHandle0 = new long[1];
// first with undefined type (should be the first one with least # of caps)
TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
- assertThat(demuxHandle0[0]).isEqualTo(1);
+ assertThat(demuxHandle0[0]).isEqualTo(1L);
DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
@@ -837,20 +833,20 @@
demuxHandle0[0] = -1;
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isFalse();
- assertThat(demuxHandle0[0]).isEqualTo(-1);
+ assertThat(demuxHandle0[0]).isEqualTo(-1L);
// now with TS (should be the one with least # of caps that supports TS)
request.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
- assertThat(demuxHandle0[0]).isEqualTo(2);
+ assertThat(demuxHandle0[0]).isEqualTo(2L);
// request for another TS
int[] clientId1 = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
profile1, null /*listener*/, clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- int[] demuxHandle1 = new int[1];
+ long[] demuxHandle1 = new long[1];
TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
.isTrue();
@@ -899,14 +895,14 @@
// let clientId0(prio:100) request for IP - should succeed
TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP);
- int[] demuxHandle0 = new int[1];
+ long[] demuxHandle0 = new long[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
assertThat(demuxHandle0[0]).isEqualTo(0);
// let clientId1(prio:50) request for IP - should fail
TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP);
- int[] demuxHandle1 = new int[1];
+ long[] demuxHandle1 = new long[1];
demuxHandle1[0] = -1;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isFalse();
@@ -926,7 +922,7 @@
// let clientId2(prio:50) request for TS - should succeed
TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS);
- int[] demuxHandle2 = new int[1];
+ long[] demuxHandle2 = new long[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request2, demuxHandle2)).isTrue();
assertThat(demuxHandle2[0]).isEqualTo(0);
@@ -951,7 +947,7 @@
profile, null /*listener*/, clientId);
assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- int[] desHandle = new int[1];
+ long[] desHandle = new long[1];
TunerDescramblerRequest request = new TunerDescramblerRequest();
request.clientId = clientId[0];
assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
@@ -1061,7 +1057,7 @@
1 /*exclusiveGroupId*/);
/**** Init Lnb Resources ****/
- int[] lnbHandles = {1};
+ long[] lnbHandles = {1};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
// Update frontend list in TRM
@@ -1070,7 +1066,7 @@
/**** Request Frontend ****/
// Predefined frontend request and array to save returned frontend handle
- int[] frontendHandle = new int[1];
+ long[] frontendHandle = new long[1];
TunerFrontendRequest request = tunerFrontendRequest(
ownerClientId0[0] /*clientId*/,
FrontendSettings.TYPE_DVBT);
@@ -1080,12 +1076,9 @@
.requestFrontendInternal(request, frontendHandle))
.isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
/**** Share Frontend ****/
@@ -1113,24 +1106,15 @@
shareClientId0[0],
shareClientId1[0])));
// Verify in use frontend list in all the primary owner and share owner clients
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId1[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(shareClientId0[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(shareClientId1[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
/**** Remove Frontend Share Owner ****/
@@ -1142,18 +1126,12 @@
.getShareFeClientIds())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
shareClientId0[0])));
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(shareClientId0[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
/**** Request Shared Frontend with Higher Priority Client ****/
@@ -1173,12 +1151,9 @@
.getOwnerClientId()).isEqualTo(ownerClientId1[0]);
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.getOwnerClientId()).isEqualTo(ownerClientId1[0]);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getInUseFrontendHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle,
- infos[1].handle)));
+ assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId1[0])
+ .getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle)));
assertThat(mTunerResourceManagerService
.getClientProfile(ownerClientId0[0])
.getInUseFrontendHandles()
@@ -1235,7 +1210,7 @@
// Predefined Lnb request and handle array
TunerLnbRequest requestLnb = new TunerLnbRequest();
requestLnb.clientId = shareClientId0[0];
- int[] lnbHandle = new int[1];
+ long[] lnbHandle = new long[1];
// Request for an Lnb
assertThat(mTunerResourceManagerService
@@ -1264,15 +1239,13 @@
.getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseLnbHandles())
- .isEqualTo(new HashSet<Integer>(Arrays.asList(
- lnbHandles[0])));
+ assertThat(mTunerResourceManagerService.getClientProfile(shareClientId0[0])
+ .getInUseLnbHandles())
+ .isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0])));
}
private TunerFrontendInfo tunerFrontendInfo(
- int handle, int frontendType, int exclusiveGroupId) {
+ long handle, int frontendType, int exclusiveGroupId) {
TunerFrontendInfo info = new TunerFrontendInfo();
info.handle = handle;
info.type = frontendType;
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 240bd1e..8797e63 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,8 +16,6 @@
package com.android.server.vibrator;
-import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
-import static android.os.VibrationAttributes.CATEGORY_UNKNOWN;
import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
@@ -255,7 +253,7 @@
}
@Test
- public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOn_keyboardVibrationReturned() {
+ public void testKeyboardHaptic_fixAmplitude_keyboardVibrationReturned() {
mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
@@ -330,7 +328,7 @@
}
@Test
- public void testVibrationAttribute_keyboardCategoryOn_notIme_useTouchUsage() {
+ public void testVibrationAttribute_notIme_useTouchUsage() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
@@ -338,13 +336,11 @@
effectId, /* flags */ 0, /* privFlags */ 0);
assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
.that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
- assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
- .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
}
}
@Test
- public void testVibrationAttribute_keyboardCategoryOn_isIme_useImeFeedbackUsage() {
+ public void testVibrationAttribute_isIme_useImeFeedbackUsage() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
@@ -353,8 +349,6 @@
HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
assertWithMessage("Expected USAGE_IME_FEEDBACK for effect " + effectId)
.that(attrs.getUsage()).isEqualTo(USAGE_IME_FEEDBACK);
- assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
- .that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
}
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 4704691..9681d74 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -281,8 +281,8 @@
@Test
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
- public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() {
- setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
+ public void scale_withVendorEffect_setsEffectStrengthAndScaleBasedOnSettings() {
+ setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
PersistableBundle vendorData = new PersistableBundle();
vendorData.putString("key", "value");
@@ -291,20 +291,27 @@
VibrationEffect.VendorEffect scaled =
(VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+ // Notification scales up.
+ assertTrue(scaled.getScale() > 1);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
VIBRATION_INTENSITY_MEDIUM);
scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ // Notification does not scale.
+ assertEquals(1, scaled.getScale(), TOLERANCE);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+ // Notification scales down.
+ assertTrue(scaled.getScale() < 1);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
// Vibration setting being bypassed will use default setting.
- assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+ assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ assertEquals(1, scaled.getScale(), TOLERANCE);
}
@Test
@@ -348,7 +355,7 @@
scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128),
USAGE_TOUCH));
// Haptic feedback does not scale.
- assertEquals(128f / 255, scaled.getAmplitude(), 1e-5);
+ assertEquals(128f / 255, scaled.getAmplitude(), TOLERANCE);
}
@Test
@@ -373,7 +380,7 @@
scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH));
// Haptic feedback does not scale.
- assertEquals(0.5, scaled.getScale(), 1e-5);
+ assertEquals(0.5, scaled.getScale(), TOLERANCE);
}
@Test
@@ -446,7 +453,7 @@
android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
})
- public void scale_adaptiveHapticsOnVendorEffect_setsLinearScaleParameter() {
+ public void scale_adaptiveHapticsOnVendorEffect_setsAdaptiveScaleParameter() {
setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f);
@@ -457,12 +464,12 @@
VibrationEffect.VendorEffect scaled =
(VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
- assertEquals(scaled.getLinearScale(), 0.5f);
+ assertEquals(scaled.getAdaptiveScale(), 0.5f);
mVibrationScaler.removeAdaptiveHapticsScale(USAGE_RINGTONE);
scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
- assertEquals(scaled.getLinearScale(), 1.0f);
+ assertEquals(scaled.getAdaptiveScale(), 1.0f);
}
private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 6f06050..38cd49d 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -610,7 +610,6 @@
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -619,7 +618,6 @@
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
.build());
}
@@ -637,7 +635,6 @@
assertVibrationNotIgnoredForAttributes(
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build());
}
@@ -654,7 +651,6 @@
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
- .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
.build(),
Vibration.Status.IGNORED_FOR_SETTINGS);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 0fbdce4..bfdaa78 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -34,13 +34,15 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
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.ContentResolver;
+import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
@@ -52,6 +54,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -66,11 +69,14 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
import org.junit.After;
@@ -105,10 +111,12 @@
private static final int TEST_DEFAULT_AMPLITUDE = 255;
private static final float TEST_DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
- @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private PackageManagerInternal mPackageManagerInternalMock;
@Mock private VibrationThread.VibratorManagerHooks mManagerHooks;
@@ -117,6 +125,7 @@
@Mock private VibrationConfig mVibrationConfigMock;
@Mock private VibratorFrameworkStatsLogger mStatsLoggerMock;
+ private ContextWrapper mContextSpy;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
private VibrationSettings mVibrationSettings;
private VibrationScaler mVibrationScaler;
@@ -149,14 +158,16 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
- Context context = InstrumentationRegistry.getContext();
- mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
- mVibrationConfigMock);
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+ when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ mVibrationSettings = new VibrationSettings(mContextSpy,
+ new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
mockVibrators(VIBRATOR_ID);
- PowerManager.WakeLock wakeLock = context.getSystemService(
+ PowerManager.WakeLock wakeLock = mContextSpy.getSystemService(
PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mThread = new VibrationThread(wakeLock, mManagerHooks);
mThread.start();
@@ -254,6 +265,9 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
+ // No user settings scale.
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibrationEffect effect = VibrationEffect.createWaveform(
@@ -277,6 +291,9 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
+ // No user settings scale.
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{5, 5, 5}, new int[]{1, 1, 1}, -1);
@@ -1864,6 +1881,13 @@
}
}
+ private void setUserSetting(String settingName, int value) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+ mVibrationSettings.mSettingObserver.onChange(false);
+ }
+
private long startThreadAndDispatcher(VibrationEffect effect) {
return startThreadAndDispatcher(CombinedVibration.createParallel(effect));
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index f009229..4013587 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1622,7 +1622,12 @@
vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS);
- assertThat(fakeVibrator.getAllVendorEffects()).containsExactly(vendorEffect);
+ // Compare vendor data only, ignore scale applied by device settings in this test.
+ assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
+ assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().keySet())
+ .containsExactly("key");
+ assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().getString("key"))
+ .isEqualTo("value");
}
@Test
@@ -1765,7 +1770,8 @@
assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
VibrationEffect.VendorEffect scaled = fakeVibrator.getAllVendorEffects().get(0);
assertThat(scaled.getEffectStrength()).isEqualTo(VibrationEffect.EFFECT_STRENGTH_LIGHT);
- assertThat(scaled.getLinearScale()).isEqualTo(0.4f);
+ assertThat(scaled.getScale()).isAtMost(1); // Scale down or none if default is LOW
+ assertThat(scaled.getAdaptiveScale()).isEqualTo(0.4f);
}
@Test
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 96c3e97..031d1c2 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -140,13 +140,13 @@
@Override
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- long vibrationId) {
+ float adaptiveScale, long vibrationId) {
if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
return 0;
}
PersistableBundle bundle = PersistableBundle.CREATOR.createFromParcel(vendorData);
recordVendorEffect(vibrationId,
- new VibrationEffect.VendorEffect(bundle, (int) strength, scale));
+ new VibrationEffect.VendorEffect(bundle, (int) strength, scale, adaptiveScale));
applyLatency(mOnLatency);
scheduleListener(mVendorEffectDuration, vibrationId);
// HAL has unknown duration for vendor effects.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 6ba2c70..604869c 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,6 +57,7 @@
"service-permission.stubs.system_server",
"androidx.test.runner",
"androidx.test.rules",
+ "flickerlib",
"junit-params",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 1fa6868..fee9c6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -31,10 +31,10 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.UserMinAspectRatio;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.Surface;
@@ -176,10 +176,14 @@
return mActivityStack.getFromTop(fromTop);
}
- void setTaskWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+ void setTaskWindowingMode(@WindowingMode int windowingMode) {
mTaskStack.top().setWindowingMode(windowingMode);
}
+ void setTaskDisplayAreaWindowingMode(@WindowingMode int windowingMode) {
+ mTaskStack.top().getDisplayArea().setWindowingMode(windowingMode);
+ }
+
void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
doReturn(enabled).when(mActivityStack.top().mAppCompatController
.getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
@@ -222,10 +226,14 @@
.getAppCompatAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
}
- void setGetUserMinAspectRatioOverrideCode(@PackageManager.UserMinAspectRatio int orientation) {
- doReturn(orientation).when(mActivityStack.top()
- .mAppCompatController.getAppCompatAspectRatioOverrides())
- .getUserMinAspectRatioOverrideCode();
+ void setGetUserMinAspectRatioOverrideCode(@UserMinAspectRatio int overrideCode) {
+ doReturn(overrideCode).when(mActivityStack.top().mAppCompatController
+ .getAppCompatAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
+ }
+
+ void setGetUserMinAspectRatioOverrideValue(float overrideValue) {
+ doReturn(overrideValue).when(mActivityStack.top().mAppCompatController
+ .getAppCompatAspectRatioOverrides()).getUserMinAspectRatio();
}
void setIgnoreOrientationRequest(boolean enabled) {
@@ -233,8 +241,7 @@
}
void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
- doReturn(inMultiWindowMode).when(mTaskStack.top())
- .inMultiWindowMode();
+ doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode();
}
void setTopActivityAsEmbedded(boolean embedded) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 40a5347..7760051 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -52,6 +52,11 @@
doReturn(enabled).when(mAppCompatConfiguration).isCameraCompatTreatmentEnabled();
}
+ void enableSplitScreenAspectRatioForUnresizableApps(boolean enabled) {
+ doReturn(enabled).when(mAppCompatConfiguration)
+ .getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ }
+
void enableCameraCompatTreatmentAtBuildTime(boolean enabled) {
doReturn(enabled).when(mAppCompatConfiguration)
.isCameraCompatTreatmentEnabledAtBuildTime();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
new file mode 100644
index 0000000..f4e1d49
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link DesktopAppCompatAspectRatioPolicy}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:DesktopAppCompatAspectRatioPolicyTests
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ private static final float FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.33f;
+
+ @Test
+ public void testHasMinAspectRatioOverride_userAspectRatioEnabled_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideDisabled_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideEnabled_propertyFalse_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideDisabled_propertyTrue_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+ public void testHasMinAspectRatioOverride_overrideEnabled_nonPortraitActivity_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testHasMinAspectRatioOverride_splitScreenAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testHasMinAspectRatioOverride_largeMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testHasMinAspectRatioOverride_mediumMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testHasMinAspectRatioOverride_smallMinAspectRatioOverride_returnTrue() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkHasMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testCalculateAspectRatio_splitScreenAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testCalculateAspectRatio_largeMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioLargeAspectRatioOverride();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testCalculateAspectRatio_mediumMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioMediumAspectRatioOverride();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testCalculateAspectRatio_smallMinAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSmallAspectRatioOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_defaultMultiWindowLetterboxAspectRatio() {
+ runTestScenario((robot)-> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ false);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.setTaskDisplayAreaWindowingMode(WINDOWING_MODE_FREEFORM);
+ });
+
+ robot.checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_displayAspectRatioEnabledForFixedOrientationLetterbox() {
+ runTestScenario((robot)-> {
+ robot.conf().enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+ });
+
+ robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_defaultMinAspectRatio_fixedOrientationAspectRatio() {
+ runTestScenario((robot)-> {
+ robot.applyOnConf((c) -> {
+ c.enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
+ c.setFixedOrientationLetterboxAspectRatio(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+ });
+
+ robot.checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_splitScreenForUnresizeableEnabled() {
+ runTestScenario((robot) -> {
+ robot.conf().enableSplitScreenAspectRatioForUnresizableApps(/* isEnabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ });
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user3By2AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser3By2AspectRatiOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user4By3AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(4 / 3f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_4_3);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser4By3AspectRatiOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_user16By9AspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(16 / 9f);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_16_9);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioUser16By9AspectRatioOverride();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_userSplitScreenAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(robot.getSplitScreenAspectRatio());
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+ });
+ }
+
+ @Test
+ public void testCalculateAspectRatio_userDisplayAreaAspectRatioOverride() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(/* enabled */ true);
+ a.setGetUserMinAspectRatioOverrideValue(robot.getDisplayAreaAspectRatio());
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ });
+ robot.setDesiredAspectRatio(1f);
+
+ robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<DesktopAppCompatAspectRatioPolicyRobotTest> consumer) {
+ final DesktopAppCompatAspectRatioPolicyRobotTest robot =
+ new DesktopAppCompatAspectRatioPolicyRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class DesktopAppCompatAspectRatioPolicyRobotTest extends AppCompatRobotBase {
+ DesktopAppCompatAspectRatioPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ }
+
+ @Override
+ void onPostActivityCreation(@NonNull ActivityRecord activity) {
+ super.onPostActivityCreation(activity);
+ spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+ spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+ }
+
+ void setDesiredAspectRatio(float aspectRatio) {
+ doReturn(aspectRatio).when(getDesktopAppCompatAspectRatioPolicy())
+ .getDesiredAspectRatio(any());
+ }
+
+ DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+ return getTopActivity().mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+ }
+
+ float calculateAspectRatio() {
+ return getDesktopAppCompatAspectRatioPolicy().calculateAspectRatio(
+ getTopActivity().getTask());
+ }
+
+ ActivityRecord getTopActivity() {
+ return this.activity().top();
+ }
+
+ float getSplitScreenAspectRatio() {
+ return getTopActivity().mAppCompatController.getAppCompatAspectRatioOverrides()
+ .getSplitScreenAspectRatio();
+ }
+
+ float getDisplayAreaAspectRatio() {
+ final Rect appBounds = getTopActivity().getDisplayArea().getWindowConfiguration()
+ .getAppBounds();
+ return AppCompatUtils.computeAspectRatio(appBounds);
+ }
+
+ void checkHasMinAspectRatioOverride(boolean expected) {
+ assertEquals(expected, this.activity().top().mAppCompatController
+ .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(
+ this.activity().top().getTask()));
+ }
+
+ void checkCalculateAspectRatioSplitScreenAspectRatio() {
+ assertEquals(getSplitScreenAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioLargeAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioMediumAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioSmallAspectRatioOverride() {
+ assertEquals(OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow() {
+ assertEquals(DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDisplayAreaAspectRatio() {
+ assertEquals(getDisplayAreaAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio() {
+ assertEquals(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO, calculateAspectRatio(),
+ FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser3By2AspectRatiOverride() {
+ assertEquals(3 / 2f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser4By3AspectRatiOverride() {
+ assertEquals(4 / 3f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+
+ void checkCalculateAspectRatioUser16By9AspectRatioOverride() {
+ assertEquals(16 / 9f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index 48a8d55..a1d35a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -125,7 +125,7 @@
public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
mWindowTracing.startTrace(mock(PrintWriter.class));
mWindowTracing.logState("where");
- verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTraceLogLevel.TRIM));
+ verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.TRIM));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
new file mode 100644
index 0000000..1d567b1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+import static java.io.File.createTempFile;
+import static java.nio.file.Files.createTempDirectory;
+
+import android.platform.test.annotations.Presubmit;
+import android.tools.ScenarioBuilder;
+import android.tools.traces.io.ResultWriter;
+import android.tools.traces.monitors.PerfettoTraceMonitor;
+import android.view.Choreographer;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency;
+
+/**
+ * Test class for {@link WindowTracingPerfetto}.
+ */
+@SmallTest
+@Presubmit
+public class WindowTracingPerfettoTest {
+ @Mock
+ private WindowManagerService mWmMock;
+ @Mock
+ private Choreographer mChoreographer;
+ private WindowTracing mWindowTracing;
+ private PerfettoTraceMonitor mTraceMonitor;
+ private ResultWriter mWriter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer,
+ new WindowManagerGlobalLock());
+
+ mWriter = new ResultWriter()
+ .forScenario(new ScenarioBuilder()
+ .forClass(createTempFile("temp", "").getName()).build())
+ .withOutputDir(createTempDirectory("temp").toFile())
+ .setRunComplete();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ stopTracing();
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() {
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() {
+ startTracing(false);
+ assertTrue(mWindowTracing.isEnabled());
+
+ stopTracing();
+ assertFalse(mWindowTracing.isEnabled());
+ }
+
+ @Test
+ public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
+ mWindowTracing.logState("where");
+ verifyZeroInteractions(mWmMock);
+ }
+
+ @Test
+ public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception {
+ startTracing(false);
+ verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+ }
+
+ @Test
+ public void trace_writesStateSnapshot_onLogStateCall() throws Exception {
+ startTracing(false);
+ mWindowTracing.logState("where");
+ verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+ }
+
+ @Test
+ public void dump_writesOneSingleStateSnapshot() throws Exception {
+ startTracing(true);
+ mWindowTracing.logState("where");
+ verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+ }
+
+ private void startTracing(boolean isDump) {
+ if (isDump) {
+ mTraceMonitor = PerfettoTraceMonitor
+ .newBuilder()
+ .enableWindowManagerDump()
+ .build();
+ } else {
+ mTraceMonitor = PerfettoTraceMonitor
+ .newBuilder()
+ .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION)
+ .build();
+ }
+ mTraceMonitor.start();
+ }
+
+ private void stopTracing() {
+ if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) {
+ return;
+ }
+ mTraceMonitor.stop(mWriter);
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 0c98327..6ef953c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -234,7 +234,7 @@
/**
* Bundle key to get the response from
- * {@link #requestProvisionSubscriberIds(Executor, OutcomeReceiver)}.
+ * {@link #requestSatelliteSubscriberProvisionStatus(Executor, OutcomeReceiver)}.
* @hide
*/
public static final String KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN =
@@ -242,13 +242,6 @@
/**
* Bundle key to get the response from
- * {@link #requestIsProvisioned(String, Executor, OutcomeReceiver)}.
- * @hide
- */
- public static final String KEY_IS_SATELLITE_PROVISIONED = "request_is_satellite_provisioned";
-
- /**
- * Bundle key to get the response from
* {@link #provisionSatellite(List, Executor, OutcomeReceiver)}.
* @hide
*/
@@ -2651,8 +2644,10 @@
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public void requestProvisionSubscriberIds(@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<List<SatelliteSubscriberInfo>, SatelliteException> callback) {
+ public void requestSatelliteSubscriberProvisionStatus(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<List<SatelliteSubscriberProvisionStatus>,
+ SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -2664,10 +2659,10 @@
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SATELLITE_RESULT_SUCCESS) {
if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
- List<SatelliteSubscriberInfo> list =
+ List<SatelliteSubscriberProvisionStatus> list =
resultData.getParcelableArrayList(
KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
- SatelliteSubscriberInfo.class);
+ SatelliteSubscriberProvisionStatus.class);
executor.execute(() -> Binder.withCleanCallingIdentity(() ->
callback.onResult(list)));
} else {
@@ -2682,70 +2677,14 @@
}
}
};
- telephony.requestProvisionSubscriberIds(receiver);
+ telephony.requestSatelliteSubscriberProvisionStatus(receiver);
} else {
- loge("requestProvisionSubscriberIds() invalid telephony");
+ loge("requestSatelliteSubscriberProvisionStatus() invalid telephony");
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
} catch (RemoteException ex) {
- loge("requestProvisionSubscriberIds() RemoteException: " + ex);
- executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
- new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
- }
- }
-
- /**
- * Request to get provisioned status for given a satellite subscriber id.
- *
- * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
- * @param executor The executor on which the callback will be called.
- * @param callback callback.
- *
- * @throws SecurityException if the caller doesn't have required permission.
- * @hide
- */
- @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public void requestIsProvisioned(@NonNull String satelliteSubscriberId,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
- Objects.requireNonNull(satelliteSubscriberId);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
-
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- ResultReceiver receiver = new ResultReceiver(null) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == SATELLITE_RESULT_SUCCESS) {
- if (resultData.containsKey(KEY_IS_SATELLITE_PROVISIONED)) {
- boolean isIsProvisioned =
- resultData.getBoolean(KEY_IS_SATELLITE_PROVISIONED);
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onResult(isIsProvisioned)));
- } else {
- loge("KEY_IS_SATELLITE_PROVISIONED does not exist.");
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onError(new SatelliteException(
- SATELLITE_RESULT_REQUEST_FAILED))));
- }
- } else {
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onError(new SatelliteException(resultCode))));
- }
- }
- };
- telephony.requestIsProvisioned(satelliteSubscriberId, receiver);
- } else {
- loge("requestIsSatelliteProvisioned() invalid telephony");
- executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
- new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
- }
- } catch (RemoteException ex) {
- loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
+ loge("requestSatelliteSubscriberProvisionStatus() RemoteException: " + ex);
executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index f26219b..50ed627 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -17,12 +17,15 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.telephony.flags.Flags;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -39,27 +42,117 @@
/** provision subscriberId */
@NonNull
private String mSubscriberId;
-
/** carrier id */
private int mCarrierId;
/** apn */
private String mNiddApn;
+ private int mSubId;
- /**
- * @hide
- */
- public SatelliteSubscriberInfo(@NonNull String subscriberId, @NonNull int carrierId,
- @NonNull String niddApn) {
- this.mCarrierId = carrierId;
- this.mSubscriberId = subscriberId;
- this.mNiddApn = niddApn;
- }
+ /** SubscriberId format is the ICCID. */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static final int ICCID = 0;
+ /** SubscriberId format is the 6 digit of IMSI + MSISDN. */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static final int IMSI_MSISDN = 1;
+
+ /** Type of subscriber id */
+ @SubscriberIdType private int mSubscriberIdType;
+ /** @hide */
+ @IntDef(prefix = "SubscriberId_Type_", value = {
+ ICCID,
+ IMSI_MSISDN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SubscriberIdType {}
private SatelliteSubscriberInfo(Parcel in) {
readFromParcel(in);
}
+ public SatelliteSubscriberInfo(@NonNull Builder builder) {
+ this.mSubscriberId = builder.mSubscriberId;
+ this.mCarrierId = builder.mCarrierId;
+ this.mNiddApn = builder.mNiddApn;
+ this.mSubId = builder.mSubId;
+ this.mSubscriberIdType = builder.mSubscriberIdType;
+ }
+
+ /**
+ * Builder class for constructing SatelliteSubscriberInfo objects
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public static class Builder {
+ @NonNull private String mSubscriberId;
+ private int mCarrierId;
+ @NonNull
+ private String mNiddApn;
+ private int mSubId;
+ @SubscriberIdType
+ private int mSubscriberIdType;
+
+ /**
+ * Set the SubscriberId and returns the Builder class.
+ *
+ * @hide
+ */
+ public Builder setSubscriberId(String subscriberId) {
+ mSubscriberId = subscriberId;
+ return this;
+ }
+
+ /**
+ * Set the CarrierId and returns the Builder class.
+ * @hide
+ */
+ @NonNull
+ public Builder setCarrierId(int carrierId) {
+ mCarrierId = carrierId;
+ return this;
+ }
+
+ /**
+ * Set the niddApn and returns the Builder class.
+ * @hide
+ */
+ @NonNull
+ public Builder setNiddApn(String niddApn) {
+ mNiddApn = niddApn;
+ return this;
+ }
+
+ /**
+ * Set the subId and returns the Builder class.
+ * @hide
+ */
+ @NonNull
+ public Builder setSubId(int subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ /**
+ * Set the SubscriberIdType and returns the Builder class.
+ * @hide
+ */
+ @NonNull
+ public Builder setSubscriberIdType(@SubscriberIdType int subscriberIdType) {
+ mSubscriberIdType = subscriberIdType;
+ return this;
+ }
+
+ /**
+ * Returns SatelliteSubscriberInfo object.
+ * @hide
+ */
+ @NonNull
+ public SatelliteSubscriberInfo build() {
+ return new SatelliteSubscriberInfo(this);
+ }
+ }
+
/**
* @hide
*/
@@ -69,6 +162,8 @@
out.writeString(mSubscriberId);
out.writeInt(mCarrierId);
out.writeString(mNiddApn);
+ out.writeInt(mSubId);
+ out.writeInt(mSubscriberIdType);
}
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@@ -121,6 +216,24 @@
return mNiddApn;
}
+ /**
+ * @return subId.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * @return subscriberIdType.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ public @SubscriberIdType int getSubscriberIdType() {
+ return mSubscriberIdType;
+ }
+
@NonNull
@Override
public String toString() {
@@ -136,26 +249,37 @@
sb.append("NiddApn:");
sb.append(mNiddApn);
+ sb.append(",");
+
+ sb.append("SubId:");
+ sb.append(mSubId);
+ sb.append(",");
+
+ sb.append("SubscriberIdType:");
+ sb.append(mSubscriberIdType);
return sb.toString();
}
@Override
public int hashCode() {
- return Objects.hash(mSubscriberId, mCarrierId, mNiddApn);
+ return Objects.hash(mSubscriberId, mCarrierId, mNiddApn, mSubId, mSubscriberIdType);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof SatelliteSubscriberProvisionStatus)) return false;
SatelliteSubscriberInfo that = (SatelliteSubscriberInfo) o;
- return mSubscriberId.equals(that.mSubscriberId) && mCarrierId
- == that.mCarrierId && mNiddApn.equals(that.mNiddApn);
+ return Objects.equals(mSubscriberId, that.mSubscriberId) && mCarrierId == that.mCarrierId
+ && Objects.equals(mNiddApn, that.mNiddApn) && mSubId == that.mSubId
+ && mSubscriberIdType == that.mSubscriberIdType;
}
private void readFromParcel(Parcel in) {
mSubscriberId = in.readString();
mCarrierId = in.readInt();
mNiddApn = in.readString();
+ mSubId = in.readInt();
+ mSubscriberIdType = in.readInt();
}
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 8919703..0c5f30f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3413,19 +3413,7 @@
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestProvisionSubscriberIds(in ResultReceiver result);
-
- /**
- * Request to get provisioned status for given a satellite subscriber id.
- *
- * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
- * @param result The result receiver, which returns the provisioned status of the token if the
- * request is successful or an error code if the request failed.
- * @hide
- */
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
- + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- void requestIsProvisioned(in String satelliteSubscriberId, in ResultReceiver result);
+ void requestSatelliteSubscriberProvisionStatus(in ResultReceiver result);
/**
* Deliver the list of provisioned satellite subscriber infos.
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 638d594..eb63e49 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -28,6 +28,7 @@
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -114,28 +115,28 @@
/**
* In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
- * this is fixed and the nav bar shows as invisible
+ * this is fixed and the status bar shows as invisible
*/
@Presubmit
@Test
fun statusBarLayerIsInvisibleInLandscapePhone() {
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
- Assume.assumeFalse(usesTaskbar)
+ Assume.assumeFalse(flicker.scenario.isTablet)
flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
}
/**
* In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
- * this is fixed and the nav bar shows as invisible
+ * this is fixed and the status bar shows as invisible
*/
@Presubmit
@Test
fun statusBarLayerIsInvisibleInLandscapeTablet() {
Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
- Assume.assumeTrue(usesTaskbar)
+ Assume.assumeTrue(flicker.scenario.isTablet)
flicker.statusBarLayerIsVisibleAtStartAndEnd()
}
@@ -149,6 +150,10 @@
@Ignore("Visibility changes depending on orientation and navigation mode")
override fun navBarLayerPositionAtStartAndEnd() {}
+ @Test
+ @Ignore("Visibility changes depending on orientation and navigation mode")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
+
/** {@inheritDoc} */
@Test
@Ignore("Visibility changes depending on orientation and navigation mode")
@@ -161,7 +166,10 @@
@Presubmit
@Test
- override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+ fun taskBarLayerIsVisibleAtStartAndEndForTablets() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarLayerIsVisibleAtStartAndEnd()
+ }
@Presubmit
@Test
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 70d762e..851ce02 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -137,8 +137,6 @@
/**
* Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the
* transition
- *
- * Note: Large screen only
*/
@Presubmit
@Test
@@ -149,8 +147,6 @@
/**
* Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
- *
- * Note: Large screen only
*/
@Presubmit
@Test
diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml
index 7b67e9e..2d6c650e 100644
--- a/tests/Internal/AndroidTest.xml
+++ b/tests/Internal/AndroidTest.xml
@@ -26,4 +26,12 @@
<option name="package" value="com.android.internal.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.internal.tests/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
</configuration>
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index 7d0c596..4b745b2 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -29,7 +29,6 @@
import static org.mockito.Mockito.when;
import static java.io.File.createTempFile;
-import static java.nio.file.Files.createTempDirectory;
import android.content.Context;
import android.os.SystemClock;
@@ -45,6 +44,7 @@
import android.util.proto.ProtoInputStream;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
@@ -67,7 +67,6 @@
import java.io.IOException;
import java.util.List;
import java.util.Random;
-import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -78,7 +77,8 @@
@Presubmit
@RunWith(JUnit4.class)
public class PerfettoProtoLogImplTest {
- private final File mTracingDirectory = createTempDirectory("temp").toFile();
+ private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
+ .getTargetContext().getFilesDir();
private final ResultWriter mWriter = new ResultWriter()
.forScenario(new ScenarioBuilder()
@@ -384,7 +384,7 @@
new Object[]{5});
verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- LogLevel.INFO), eq("UNKNOWN MESSAGE#1234 (5)"));
+ LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)"));
verify(mReader).getViewerString(eq(1234L));
}
@@ -451,8 +451,8 @@
before = SystemClock.elapsedRealtimeNanos();
mProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP,
- "My test message :: %s, %d, %o, %x, %f, %b",
- "test", 1, 2, 3, 0.4, true);
+ "My test message :: %s, %d, %x, %f, %b",
+ "test", 1, 3, 0.4, true);
after = SystemClock.elapsedRealtimeNanos();
} finally {
traceMonitor.stop(mWriter);
@@ -467,7 +467,7 @@
Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
.isAtMost(after);
Truth.assertThat(protolog.messages.getFirst().getMessage())
- .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, true");
+ .isEqualTo("My test message :: test, 2, 6, 0.400000, true");
}
@Test
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1c85e9f..a5aecc8 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -151,6 +151,7 @@
}
if (res->value != nullptr) {
+ res->value->SetFlagStatus(res->flag_status);
// Attach the comment, source and config to the value.
res->value->SetComment(std::move(res->comment));
res->value->SetSource(std::move(res->source));
@@ -546,30 +547,11 @@
});
std::string resource_type = parser->element_name();
- std::optional<StringPiece> flag =
- xml::FindAttribute(parser, "http://schemas.android.com/apk/res/android", "featureFlag");
- out_resource->flag_status = FlagStatus::NoFlag;
- if (flag) {
- auto flag_it = options_.feature_flag_values.find(flag.value());
- if (flag_it == options_.feature_flag_values.end()) {
- diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
- << "Resource flag value undefined");
- return false;
- }
- const auto& flag_properties = flag_it->second;
- if (!flag_properties.read_only) {
- diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
- << "Only read only flags may be used with resources");
- return false;
- }
- if (!flag_properties.enabled.has_value()) {
- diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
- << "Only flags with a value may be used with resources");
- return false;
- }
- out_resource->flag_status =
- flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+ auto flag_status = GetFlagStatus(parser);
+ if (!flag_status) {
+ return false;
}
+ out_resource->flag_status = flag_status.value();
// The value format accepted for this resource.
uint32_t resource_format = 0u;
@@ -751,6 +733,33 @@
return false;
}
+std::optional<FlagStatus> ResourceParser::GetFlagStatus(xml::XmlPullParser* parser) {
+ auto flag_status = FlagStatus::NoFlag;
+
+ std::optional<StringPiece> flag = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag");
+ if (flag) {
+ auto flag_it = options_.feature_flag_values.find(flag.value());
+ if (flag_it == options_.feature_flag_values.end()) {
+ diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+ << "Resource flag value undefined");
+ return {};
+ }
+ const auto& flag_properties = flag_it->second;
+ if (!flag_properties.read_only) {
+ diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+ << "Only read only flags may be used with resources");
+ return {};
+ }
+ if (!flag_properties.enabled.has_value()) {
+ diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+ << "Only flags with a value may be used with resources");
+ return {};
+ }
+ flag_status = flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+ }
+ return flag_status;
+}
+
bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
ParsedResource* out_resource,
const uint32_t format) {
@@ -1657,12 +1666,18 @@
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "item") {
+ auto flag_status = GetFlagStatus(parser);
+ if (!flag_status) {
+ error = true;
+ continue;
+ }
std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
if (!item) {
diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
error = true;
continue;
}
+ item->SetFlagStatus(flag_status.value());
item->SetSource(item_source);
array->elements.emplace_back(std::move(item));
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 45d41c1..442dea8 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -85,6 +85,8 @@
private:
DISALLOW_COPY_AND_ASSIGN(ResourceParser);
+ std::optional<FlagStatus> GetFlagStatus(xml::XmlPullParser* parser);
+
std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
// Parses the XML subtree as a StyleString (flattened XML representation for strings with
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 1cdb715..7a4f40e 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -605,12 +605,12 @@
if (!config_value->value) {
// Resource does not exist, add it now.
config_value->value = std::move(res.value);
- config_value->flag_status = res.flag_status;
} else {
// When validation is enabled, ensure that a resource cannot have multiple values defined for
// the same configuration unless protected by flags.
- auto result = validate ? ResolveFlagCollision(config_value->flag_status, res.flag_status)
- : CollisionResult::kKeepBoth;
+ auto result =
+ validate ? ResolveFlagCollision(config_value->value->GetFlagStatus(), res.flag_status)
+ : CollisionResult::kKeepBoth;
if (result == CollisionResult::kConflict) {
result = ResolveValueCollision(config_value->value.get(), res.value.get());
}
@@ -619,7 +619,6 @@
// Insert the value ignoring for duplicate configurations
entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product));
entry->values.back()->value = std::move(res.value);
- entry->values.back()->flag_status = res.flag_status;
break;
case CollisionResult::kTakeNew:
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 4f76e7d..cba6b70 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -104,8 +104,6 @@
// The actual Value.
std::unique_ptr<Value> value;
- FlagStatus flag_status = FlagStatus::NoFlag;
-
ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product)
: config(config), product(product) {
}
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 166b01b..b75e87c 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -971,6 +971,16 @@
*out << "(array) [" << util::Joiner(elements, ", ") << "]";
}
+void Array::RemoveFlagDisabledElements() {
+ const auto end_iter = elements.end();
+ const auto remove_iter = std::stable_partition(
+ elements.begin(), end_iter, [](const std::unique_ptr<Item>& item) -> bool {
+ return item->GetFlagStatus() != FlagStatus::Disabled;
+ });
+
+ elements.erase(remove_iter, end_iter);
+}
+
bool Plural::Equals(const Value* value) const {
const Plural* other = ValueCast<Plural>(value);
if (!other) {
@@ -1092,6 +1102,7 @@
std::unique_ptr<T> CopyValueFields(std::unique_ptr<T> new_value, const T* value) {
new_value->SetSource(value->GetSource());
new_value->SetComment(value->GetComment());
+ new_value->SetFlagStatus(value->GetFlagStatus());
return new_value;
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 5192c2b..a1b1839 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -65,6 +65,14 @@
return translatable_;
}
+ void SetFlagStatus(FlagStatus val) {
+ flag_status_ = val;
+ }
+
+ FlagStatus GetFlagStatus() const {
+ return flag_status_;
+ }
+
// Returns the source where this value was defined.
const android::Source& GetSource() const {
return source_;
@@ -109,6 +117,10 @@
// of brevity and readability. Default implementation just calls Print().
virtual void PrettyPrint(text::Printer* printer) const;
+ // Removes any part of the value that is beind a disabled flag.
+ virtual void RemoveFlagDisabledElements() {
+ }
+
friend std::ostream& operator<<(std::ostream& out, const Value& value);
protected:
@@ -116,6 +128,7 @@
std::string comment_;
bool weak_ = false;
bool translatable_ = true;
+ FlagStatus flag_status_ = FlagStatus::NoFlag;
private:
virtual Value* TransformValueImpl(ValueTransformer& transformer) const = 0;
@@ -346,6 +359,7 @@
bool Equals(const Value* value) const override;
void Print(std::ostream* out) const override;
+ void RemoveFlagDisabledElements() override;
};
struct Plural : public TransformableValue<Plural, BaseValue<Plural>> {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 2ecc82a..5c64089 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -246,7 +246,7 @@
message ConfigValue {
Configuration config = 1;
Value value = 2;
- uint32 flag_status = 3;
+ reserved 3;
}
// The generic meta-data for every value in a resource table.
@@ -280,6 +280,9 @@
Id id = 6;
Primitive prim = 7;
}
+
+ // The status of the flag the value is behind if any
+ uint32 flag_status = 8;
}
// A CompoundValue is an abstract type. It represents a value that is a made of other values.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 56f5288..be63f82 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1878,7 +1878,7 @@
for (auto& type : package->types) {
for (auto& entry : type->entries) {
for (auto& config_value : entry->values) {
- if (config_value->flag_status == FlagStatus::Disabled) {
+ if (config_value->value->GetFlagStatus() == FlagStatus::Disabled) {
config_value->value->Accept(&visitor);
}
}
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aaab315..55f5e56 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -534,8 +534,6 @@
return false;
}
- config_value->flag_status = (FlagStatus)pb_config_value.flag_status();
-
config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
&out_table->string_pool, files, out_error);
if (config_value->value == nullptr) {
@@ -877,11 +875,12 @@
return value;
}
-std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
- const android::ResStringPool& src_pool,
- const ConfigDescription& config,
- android::StringPool* value_pool,
- io::IFileCollection* files, std::string* out_error) {
+std::unique_ptr<Item> DeserializeItemFromPbInternal(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ android::StringPool* value_pool,
+ io::IFileCollection* files,
+ std::string* out_error) {
switch (pb_item.value_case()) {
case pb::Item::kRef: {
const pb::Reference& pb_ref = pb_item.ref();
@@ -1010,6 +1009,19 @@
return {};
}
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+ const android::ResStringPool& src_pool,
+ const ConfigDescription& config,
+ android::StringPool* value_pool,
+ io::IFileCollection* files, std::string* out_error) {
+ auto item =
+ DeserializeItemFromPbInternal(pb_item, src_pool, config, value_pool, files, out_error);
+ if (item) {
+ item->SetFlagStatus((FlagStatus)pb_item.flag_status());
+ }
+ return item;
+}
+
std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
std::string* out_error) {
if (!pb_node.has_element()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index c1e15bc..5772b3b 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -426,7 +426,6 @@
pb_config_value->mutable_config()->set_product(config_value->product);
SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
source_pool.get());
- pb_config_value->set_flag_status((uint32_t)config_value->flag_status);
}
}
}
@@ -720,6 +719,9 @@
if (src_pool != nullptr) {
SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
}
+ if (out_value->has_item()) {
+ out_value->mutable_item()->set_flag_status((uint32_t)value.GetFlagStatus());
+ }
}
void SerializeItemToPb(const Item& item, pb::Item* out_item) {
@@ -727,6 +729,7 @@
ValueSerializer serializer(&value, nullptr);
item.Accept(&serializer);
out_item->MergeFrom(value.item());
+ out_item->set_flag_status((uint32_t)item.GetFlagStatus());
}
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
index 5932271..4866d2c 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -28,11 +28,13 @@
srcs: [
"res/values/bools.xml",
"res/values/bools2.xml",
+ "res/values/ints.xml",
"res/values/strings.xml",
],
out: [
"values_bools.arsc.flat",
"values_bools2.arsc.flat",
+ "values_ints.arsc.flat",
"values_strings.arsc.flat",
],
cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
new file mode 100644
index 0000000..26a5c40
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <integer-array name="intarr1">
+ <item>1</item>
+ <item>2</item>
+ <item android:featureFlag="test.package.falseFlag">666</item>
+ <item>3</item>
+ </integer-array>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
index 5c0fca1..3cbb928 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
@@ -3,4 +3,11 @@
<string name="str">plain string</string>
<string name="str1" android:featureFlag="test.package.falseFlag">DONTFIND</string>
+
+ <string-array name="strarr1">
+ <item>one</item>
+ <item>two</item>
+ <item android:featureFlag="test.package.falseFlag">remove</item>
+ <item android:featureFlag="test.package.trueFlag">three</item>
+ </string-array>
</resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.cpp b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
index e3289e2..3ac1762 100644
--- a/tools/aapt2/link/FlagDisabledResourceRemover.cpp
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
@@ -32,12 +32,17 @@
const auto remove_iter =
std::stable_partition(entry->values.begin(), end_iter,
[](const std::unique_ptr<ResourceConfigValue>& value) -> bool {
- return value->flag_status != FlagStatus::Disabled;
+ return value->value->GetFlagStatus() != FlagStatus::Disabled;
});
bool keep = remove_iter != entry->values.begin();
entry->values.erase(remove_iter, end_iter);
+
+ for (auto& value : entry->values) {
+ value->value->RemoveFlagDisabledElements();
+ }
+
return keep;
}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1942fc11..37a039e 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -212,8 +212,8 @@
collision_result =
ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
} else {
- collision_result = ResourceTable::ResolveFlagCollision(dst_config_value->flag_status,
- src_config_value->flag_status);
+ collision_result =
+ ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
if (collision_result == CollisionResult::kConflict) {
collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
}
@@ -295,7 +295,6 @@
} else {
dst_config_value =
dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product);
- dst_config_value->flag_status = src_config_value->flag_status;
}
// Continue if we're taking the new resource.
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 36bfbef..cb4db57 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -37,6 +37,7 @@
import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.util.CheckClassAdapter
import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
@@ -273,7 +274,7 @@
if (filename == null) {
return block(null)
}
- return ZipOutputStream(FileOutputStream(filename)).use(block)
+ return ZipOutputStream(BufferedOutputStream(FileOutputStream(filename))).use(block)
}
/**
@@ -334,13 +335,14 @@
entry: ZipEntry,
out: ZipOutputStream,
) {
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ // TODO: It seems like copying entries this way is _very_ slow,
+ // even with out.setLevel(0). Look for other ways to do it.
+
+ inZip.getInputStream(entry).use { ins ->
// Copy unknown entries as is to the impl out. (but not to the stub out.)
val outEntry = ZipEntry(entry.name)
out.putNextEntry(outEntry)
- while (bis.available() > 0) {
- out.write(bis.read())
- }
+ ins.transferTo(out)
out.closeEntry()
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index ee4a06f..fcdf824 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -121,7 +121,7 @@
return level.ordinal <= maxLogLevel.ordinal
}
- private fun println(level: LogLevel, message: String) {
+ fun println(level: LogLevel, message: String) {
printers.forEach {
if (it.logLevel.ordinal >= level.ordinal) {
it.println(level, indent, message)
@@ -129,7 +129,7 @@
}
}
- private fun println(level: LogLevel, format: String, vararg args: Any?) {
+ fun println(level: LogLevel, format: String, vararg args: Any?) {
if (isEnabled(level)) {
println(level, String.format(format, *args))
}
@@ -185,14 +185,29 @@
println(LogLevel.Debug, format, *args)
}
- inline fun <T> iTime(message: String, block: () -> T): T {
+ inline fun <T> logTime(level: LogLevel, message: String, block: () -> T): T {
val start = System.currentTimeMillis()
- val ret = block()
- val end = System.currentTimeMillis()
+ try {
+ return block()
+ } finally {
+ val end = System.currentTimeMillis()
+ if (isEnabled(level)) {
+ println(level,
+ String.format("%s: took %.1f second(s).", message, (end - start) / 1000.0))
+ }
+ }
+ }
- log.i("%s: took %.1f second(s).", message, (end - start) / 1000.0)
+ inline fun <T> iTime(message: String, block: () -> T): T {
+ return logTime(LogLevel.Info, message, block)
+ }
- return ret
+ inline fun <T> vTime(message: String, block: () -> T): T {
+ return logTime(LogLevel.Verbose, message, block)
+ }
+
+ inline fun <T> dTime(message: String, block: () -> T): T {
+ return logTime(LogLevel.Debug, message, block)
}
inline fun forVerbose(block: () -> Unit) {
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index aa53005..2285880 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -26,7 +26,6 @@
import com.github.javaparser.ast.Modifier
import com.github.javaparser.ast.NodeList
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
-import com.github.javaparser.ast.body.InitializerDeclaration
import com.github.javaparser.ast.expr.ArrayAccessExpr
import com.github.javaparser.ast.expr.ArrayCreationExpr
import com.github.javaparser.ast.expr.ArrayInitializerExpr
@@ -42,7 +41,10 @@
import com.github.javaparser.ast.expr.ObjectCreationExpr
import com.github.javaparser.ast.expr.SimpleName
import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.ast.expr.VariableDeclarationExpr
import com.github.javaparser.ast.stmt.BlockStmt
+import com.github.javaparser.ast.stmt.ReturnStmt
+import com.github.javaparser.ast.type.ClassOrInterfaceType
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
@@ -195,6 +197,7 @@
groups: Map<String, LogGroup>,
protoLogGroupsClassName: String
) {
+ var needsCreateLogGroupsMap = false
classDeclaration.fields.forEach { field ->
field.getAnnotationByClass(ProtoLogToolInjected::class.java)
.ifPresent { annotationExpr ->
@@ -222,33 +225,10 @@
} ?: NullLiteralExpr())
}
ProtoLogToolInjected.Value.LOG_GROUPS.name -> {
- val initializerBlockStmt = BlockStmt()
- for (group in groups) {
- initializerBlockStmt.addStatement(
- MethodCallExpr()
- .setName("put")
- .setArguments(
- NodeList(StringLiteralExpr(group.key),
- FieldAccessExpr()
- .setScope(
- NameExpr(
- protoLogGroupsClassName
- ))
- .setName(group.value.name)))
- )
- group.key
- }
-
- val treeMapCreation = ObjectCreationExpr()
- .setType("TreeMap<String, IProtoLogGroup>")
- .setAnonymousClassBody(NodeList(
- InitializerDeclaration().setBody(
- initializerBlockStmt
- )
- ))
-
+ needsCreateLogGroupsMap = true
field.setFinal(true)
- field.variables.first().setInitializer(treeMapCreation)
+ field.variables.first().setInitializer(
+ MethodCallExpr().setName("createLogGroupsMap"))
}
ProtoLogToolInjected.Value.CACHE_UPDATER.name -> {
field.setFinal(true)
@@ -261,6 +241,45 @@
}
}
}
+
+ if (needsCreateLogGroupsMap) {
+ val body = BlockStmt()
+ body.addStatement(AssignExpr(
+ VariableDeclarationExpr(
+ ClassOrInterfaceType("TreeMap<String, IProtoLogGroup>"),
+ "result"
+ ),
+ ObjectCreationExpr().setType("TreeMap<String, IProtoLogGroup>"),
+ AssignExpr.Operator.ASSIGN
+ ))
+ for (group in groups) {
+ body.addStatement(
+ MethodCallExpr(
+ NameExpr("result"),
+ "put",
+ NodeList(
+ StringLiteralExpr(group.key),
+ FieldAccessExpr()
+ .setScope(
+ NameExpr(
+ protoLogGroupsClassName
+ ))
+ .setName(group.value.name)
+ )
+ )
+ )
+ }
+ body.addStatement(ReturnStmt(NameExpr("result")))
+
+ val method = classDeclaration.addMethod(
+ "createLogGroupsMap",
+ Modifier.Keyword.PRIVATE,
+ Modifier.Keyword.STATIC,
+ Modifier.Keyword.FINAL
+ )
+ method.setType("TreeMap<String, IProtoLogGroup>")
+ method.setBody(body)
+ }
}
private fun injectCacheClass(